FragmentManager.java revision 87ffa20a2fd4dff11262b87607a9aca992bdd05d
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 * Print the FragmentManager's state into the given stream. 278 * 279 * @param prefix Text to print at the front of each line. 280 * @param fd The raw file descriptor that the dump is being sent to. 281 * @param writer A PrintWriter to which the dump is to be set. 282 * @param args Additional arguments to the dump request. 283 */ 284 public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); 285 286 /** 287 * Control whether the framework's internal fragment manager debugging 288 * logs are turned on. If enabled, you will see output in logcat as 289 * the framework performs fragment operations. 290 */ 291 public static void enableDebugLogging(boolean enabled) { 292 FragmentManagerImpl.DEBUG = enabled; 293 } 294} 295 296final class FragmentManagerState implements Parcelable { 297 FragmentState[] mActive; 298 int[] mAdded; 299 BackStackState[] mBackStack; 300 301 public FragmentManagerState() { 302 } 303 304 public FragmentManagerState(Parcel in) { 305 mActive = in.createTypedArray(FragmentState.CREATOR); 306 mAdded = in.createIntArray(); 307 mBackStack = in.createTypedArray(BackStackState.CREATOR); 308 } 309 310 public int describeContents() { 311 return 0; 312 } 313 314 public void writeToParcel(Parcel dest, int flags) { 315 dest.writeTypedArray(mActive, flags); 316 dest.writeIntArray(mAdded); 317 dest.writeTypedArray(mBackStack, flags); 318 } 319 320 public static final Parcelable.Creator<FragmentManagerState> CREATOR 321 = new Parcelable.Creator<FragmentManagerState>() { 322 public FragmentManagerState createFromParcel(Parcel in) { 323 return new FragmentManagerState(in); 324 } 325 326 public FragmentManagerState[] newArray(int size) { 327 return new FragmentManagerState[size]; 328 } 329 }; 330} 331 332/** 333 * Container for fragments associated with an activity. 334 */ 335final class FragmentManagerImpl extends FragmentManager { 336 static boolean DEBUG = false; 337 static final String TAG = "FragmentManager"; 338 339 static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; 340 static final String TARGET_STATE_TAG = "android:target_state"; 341 static final String VIEW_STATE_TAG = "android:view_state"; 342 343 ArrayList<Runnable> mPendingActions; 344 Runnable[] mTmpActions; 345 boolean mExecutingActions; 346 347 ArrayList<Fragment> mActive; 348 ArrayList<Fragment> mAdded; 349 ArrayList<Integer> mAvailIndices; 350 ArrayList<BackStackRecord> mBackStack; 351 ArrayList<Fragment> mCreatedMenus; 352 353 // Must be accessed while locked. 354 ArrayList<BackStackRecord> mBackStackIndices; 355 ArrayList<Integer> mAvailBackStackIndices; 356 357 ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; 358 359 int mCurState = Fragment.INITIALIZING; 360 Activity mActivity; 361 362 boolean mNeedMenuInvalidate; 363 boolean mStateSaved; 364 boolean mDestroyed; 365 String mNoTransactionsBecause; 366 367 // Temporary vars for state save and restore. 368 Bundle mStateBundle = null; 369 SparseArray<Parcelable> mStateArray = null; 370 371 Runnable mExecCommit = new Runnable() { 372 @Override 373 public void run() { 374 execPendingActions(); 375 } 376 }; 377 378 @Override 379 public FragmentTransaction beginTransaction() { 380 return new BackStackRecord(this); 381 } 382 383 @Override 384 public boolean executePendingTransactions() { 385 return execPendingActions(); 386 } 387 388 @Override 389 public void popBackStack() { 390 enqueueAction(new Runnable() { 391 @Override public void run() { 392 popBackStackState(mActivity.mHandler, null, -1, 0); 393 } 394 }, false); 395 } 396 397 @Override 398 public boolean popBackStackImmediate() { 399 checkStateLoss(); 400 executePendingTransactions(); 401 return popBackStackState(mActivity.mHandler, null, -1, 0); 402 } 403 404 @Override 405 public void popBackStack(final String name, final int flags) { 406 enqueueAction(new Runnable() { 407 @Override public void run() { 408 popBackStackState(mActivity.mHandler, name, -1, flags); 409 } 410 }, false); 411 } 412 413 @Override 414 public boolean popBackStackImmediate(String name, int flags) { 415 checkStateLoss(); 416 executePendingTransactions(); 417 return popBackStackState(mActivity.mHandler, name, -1, flags); 418 } 419 420 @Override 421 public void popBackStack(final int id, final int flags) { 422 if (id < 0) { 423 throw new IllegalArgumentException("Bad id: " + id); 424 } 425 enqueueAction(new Runnable() { 426 @Override public void run() { 427 popBackStackState(mActivity.mHandler, null, id, flags); 428 } 429 }, false); 430 } 431 432 @Override 433 public boolean popBackStackImmediate(int id, int flags) { 434 checkStateLoss(); 435 executePendingTransactions(); 436 if (id < 0) { 437 throw new IllegalArgumentException("Bad id: " + id); 438 } 439 return popBackStackState(mActivity.mHandler, null, id, flags); 440 } 441 442 @Override 443 public int getBackStackEntryCount() { 444 return mBackStack != null ? mBackStack.size() : 0; 445 } 446 447 @Override 448 public BackStackEntry getBackStackEntryAt(int index) { 449 return mBackStack.get(index); 450 } 451 452 @Override 453 public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { 454 if (mBackStackChangeListeners == null) { 455 mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>(); 456 } 457 mBackStackChangeListeners.add(listener); 458 } 459 460 @Override 461 public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { 462 if (mBackStackChangeListeners != null) { 463 mBackStackChangeListeners.remove(listener); 464 } 465 } 466 467 @Override 468 public void putFragment(Bundle bundle, String key, Fragment fragment) { 469 if (fragment.mIndex < 0) { 470 throw new IllegalStateException("Fragment " + fragment 471 + " is not currently in the FragmentManager"); 472 } 473 bundle.putInt(key, fragment.mIndex); 474 } 475 476 @Override 477 public Fragment getFragment(Bundle bundle, String key) { 478 int index = bundle.getInt(key, -1); 479 if (index == -1) { 480 return null; 481 } 482 if (index >= mActive.size()) { 483 throw new IllegalStateException("Fragement no longer exists for key " 484 + key + ": index " + index); 485 } 486 Fragment f = mActive.get(index); 487 if (f == null) { 488 throw new IllegalStateException("Fragement no longer exists for key " 489 + key + ": index " + index); 490 } 491 return f; 492 } 493 494 @Override 495 public String toString() { 496 StringBuilder sb = new StringBuilder(128); 497 sb.append("FragmentManager{"); 498 sb.append(Integer.toHexString(System.identityHashCode(this))); 499 sb.append(" in "); 500 DebugUtils.buildShortClassTag(mActivity, sb); 501 sb.append("}}"); 502 return sb.toString(); 503 } 504 505 @Override 506 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 507 String innerPrefix = prefix + " "; 508 509 int N; 510 if (mActive != null) { 511 N = mActive.size(); 512 if (N > 0) { 513 writer.print(prefix); writer.print("Active Fragments in "); 514 writer.print(Integer.toHexString(System.identityHashCode(this))); 515 writer.println(":"); 516 for (int i=0; i<N; i++) { 517 Fragment f = mActive.get(i); 518 writer.print(prefix); writer.print(" #"); writer.print(i); 519 writer.print(": "); writer.println(f); 520 if (f != null) { 521 f.dump(innerPrefix, fd, writer, args); 522 } 523 } 524 } 525 } 526 527 if (mAdded != null) { 528 N = mAdded.size(); 529 if (N > 0) { 530 writer.print(prefix); writer.println("Added Fragments:"); 531 for (int i=0; i<N; i++) { 532 Fragment f = mAdded.get(i); 533 writer.print(prefix); writer.print(" #"); writer.print(i); 534 writer.print(": "); writer.println(f.toString()); 535 } 536 } 537 } 538 539 if (mCreatedMenus != null) { 540 N = mCreatedMenus.size(); 541 if (N > 0) { 542 writer.print(prefix); writer.println("Fragments Created Menus:"); 543 for (int i=0; i<N; i++) { 544 Fragment f = mCreatedMenus.get(i); 545 writer.print(prefix); writer.print(" #"); writer.print(i); 546 writer.print(": "); writer.println(f.toString()); 547 } 548 } 549 } 550 551 if (mBackStack != null) { 552 N = mBackStack.size(); 553 if (N > 0) { 554 writer.print(prefix); writer.println("Back Stack:"); 555 for (int i=0; i<N; i++) { 556 BackStackRecord bs = mBackStack.get(i); 557 writer.print(prefix); writer.print(" #"); writer.print(i); 558 writer.print(": "); writer.println(bs.toString()); 559 bs.dump(innerPrefix, fd, writer, args); 560 } 561 } 562 } 563 564 synchronized (this) { 565 if (mBackStackIndices != null) { 566 N = mBackStackIndices.size(); 567 if (N > 0) { 568 writer.print(prefix); writer.println("Back Stack Indices:"); 569 for (int i=0; i<N; i++) { 570 BackStackRecord bs = mBackStackIndices.get(i); 571 writer.print(prefix); writer.print(" #"); writer.print(i); 572 writer.print(": "); writer.println(bs); 573 } 574 } 575 } 576 577 if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) { 578 writer.print(prefix); writer.print("mAvailBackStackIndices: "); 579 writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); 580 } 581 } 582 583 if (mPendingActions != null) { 584 N = mPendingActions.size(); 585 if (N > 0) { 586 writer.print(prefix); writer.println("Pending Actions:"); 587 for (int i=0; i<N; i++) { 588 Runnable r = mPendingActions.get(i); 589 writer.print(prefix); writer.print(" #"); writer.print(i); 590 writer.print(": "); writer.println(r); 591 } 592 } 593 } 594 595 writer.print(prefix); writer.println("FragmentManager misc state:"); 596 writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState); 597 writer.print(" mStateSaved="); writer.print(mStateSaved); 598 writer.print(" mDestroyed="); writer.println(mDestroyed); 599 if (mNeedMenuInvalidate) { 600 writer.print(prefix); writer.print(" mNeedMenuInvalidate="); 601 writer.println(mNeedMenuInvalidate); 602 } 603 if (mNoTransactionsBecause != null) { 604 writer.print(prefix); writer.print(" mNoTransactionsBecause="); 605 writer.println(mNoTransactionsBecause); 606 } 607 if (mAvailIndices != null && mAvailIndices.size() > 0) { 608 writer.print(prefix); writer.print(" mAvailIndices: "); 609 writer.println(Arrays.toString(mAvailIndices.toArray())); 610 } 611 } 612 613 Animator loadAnimator(Fragment fragment, int transit, boolean enter, 614 int transitionStyle) { 615 Animator animObj = fragment.onCreateAnimator(transit, enter, 616 fragment.mNextAnim); 617 if (animObj != null) { 618 return animObj; 619 } 620 621 if (fragment.mNextAnim != 0) { 622 Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim); 623 if (anim != null) { 624 return anim; 625 } 626 } 627 628 if (transit == 0) { 629 return null; 630 } 631 632 int styleIndex = transitToStyleIndex(transit, enter); 633 if (styleIndex < 0) { 634 return null; 635 } 636 637 if (transitionStyle == 0 && mActivity.getWindow() != null) { 638 transitionStyle = mActivity.getWindow().getAttributes().windowAnimations; 639 } 640 if (transitionStyle == 0) { 641 return null; 642 } 643 644 TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, 645 com.android.internal.R.styleable.FragmentAnimation); 646 int anim = attrs.getResourceId(styleIndex, 0); 647 attrs.recycle(); 648 649 if (anim == 0) { 650 return null; 651 } 652 653 return AnimatorInflater.loadAnimator(mActivity, anim); 654 } 655 656 void moveToState(Fragment f, int newState, int transit, int transitionStyle) { 657 // Fragments that are not currently added will sit in the onCreate() state. 658 if (!f.mAdded && newState > Fragment.CREATED) { 659 newState = Fragment.CREATED; 660 } 661 662 if (f.mState < newState) { 663 if (f.mAnimatingAway != null) { 664 // The fragment is currently being animated... but! Now we 665 // want to move our state back up. Give up on waiting for the 666 // animation, move to whatever the final state should be once 667 // the animation is done, and then we can proceed from there. 668 f.mAnimatingAway = null; 669 moveToState(f, f.mStateAfterAnimating, 0, 0); 670 } 671 switch (f.mState) { 672 case Fragment.INITIALIZING: 673 if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); 674 if (f.mSavedFragmentState != null) { 675 f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( 676 FragmentManagerImpl.VIEW_STATE_TAG); 677 f.mTarget = getFragment(f.mSavedFragmentState, 678 FragmentManagerImpl.TARGET_STATE_TAG); 679 if (f.mTarget != null) { 680 f.mTargetRequestCode = f.mSavedFragmentState.getInt( 681 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); 682 } 683 } 684 f.mActivity = mActivity; 685 f.mFragmentManager = mActivity.mFragments; 686 f.mCalled = false; 687 f.onAttach(mActivity); 688 if (!f.mCalled) { 689 throw new SuperNotCalledException("Fragment " + f 690 + " did not call through to super.onAttach()"); 691 } 692 mActivity.onAttachFragment(f); 693 694 if (!f.mRetaining) { 695 f.mCalled = false; 696 f.onCreate(f.mSavedFragmentState); 697 if (!f.mCalled) { 698 throw new SuperNotCalledException("Fragment " + f 699 + " did not call through to super.onCreate()"); 700 } 701 } 702 f.mRetaining = false; 703 if (f.mFromLayout) { 704 // For fragments that are part of the content view 705 // layout, we need to instantiate the view immediately 706 // and the inflater will take care of adding it. 707 f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), 708 null, f.mSavedFragmentState); 709 if (f.mView != null) { 710 f.mView.setSaveFromParentEnabled(false); 711 f.restoreViewState(); 712 if (f.mHidden) f.mView.setVisibility(View.GONE); 713 } 714 } 715 case Fragment.CREATED: 716 if (newState > Fragment.CREATED) { 717 if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f); 718 if (!f.mFromLayout) { 719 ViewGroup container = null; 720 if (f.mContainerId != 0) { 721 container = (ViewGroup)mActivity.findViewById(f.mContainerId); 722 if (container == null && !f.mRestored) { 723 throw new IllegalArgumentException("No view found for id 0x" 724 + Integer.toHexString(f.mContainerId) 725 + " for fragment " + f); 726 } 727 } 728 f.mContainer = container; 729 f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), 730 container, f.mSavedFragmentState); 731 if (f.mView != null) { 732 f.mView.setSaveFromParentEnabled(false); 733 if (container != null) { 734 Animator anim = loadAnimator(f, transit, true, 735 transitionStyle); 736 if (anim != null) { 737 anim.setTarget(f.mView); 738 anim.start(); 739 } 740 container.addView(f.mView); 741 f.restoreViewState(); 742 } 743 if (f.mHidden) f.mView.setVisibility(View.GONE); 744 } 745 } 746 747 f.mCalled = false; 748 f.onActivityCreated(f.mSavedFragmentState); 749 if (!f.mCalled) { 750 throw new SuperNotCalledException("Fragment " + f 751 + " did not call through to super.onActivityCreated()"); 752 } 753 f.mSavedFragmentState = null; 754 } 755 case Fragment.ACTIVITY_CREATED: 756 if (newState > Fragment.ACTIVITY_CREATED) { 757 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 758 f.mCalled = false; 759 f.onStart(); 760 if (!f.mCalled) { 761 throw new SuperNotCalledException("Fragment " + f 762 + " did not call through to super.onStart()"); 763 } 764 } 765 case Fragment.STARTED: 766 if (newState > Fragment.STARTED) { 767 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 768 f.mCalled = false; 769 f.mResumed = true; 770 f.onResume(); 771 if (!f.mCalled) { 772 throw new SuperNotCalledException("Fragment " + f 773 + " did not call through to super.onResume()"); 774 } 775 } 776 } 777 } else if (f.mState > newState) { 778 switch (f.mState) { 779 case Fragment.RESUMED: 780 if (newState < Fragment.RESUMED) { 781 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 782 f.mCalled = false; 783 f.onPause(); 784 if (!f.mCalled) { 785 throw new SuperNotCalledException("Fragment " + f 786 + " did not call through to super.onPause()"); 787 } 788 f.mResumed = false; 789 } 790 case Fragment.STARTED: 791 if (newState < Fragment.STARTED) { 792 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 793 f.mCalled = false; 794 f.performStop(); 795 if (!f.mCalled) { 796 throw new SuperNotCalledException("Fragment " + f 797 + " did not call through to super.onStop()"); 798 } 799 } 800 case Fragment.ACTIVITY_CREATED: 801 if (newState < Fragment.ACTIVITY_CREATED) { 802 if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f); 803 if (f.mView != null) { 804 // Need to save the current view state if not 805 // done already. 806 if (!mActivity.isFinishing() && f.mSavedViewState == null) { 807 saveFragmentViewState(f); 808 } 809 } 810 f.mCalled = false; 811 f.onDestroyView(); 812 if (!f.mCalled) { 813 throw new SuperNotCalledException("Fragment " + f 814 + " did not call through to super.onDestroyView()"); 815 } 816 if (f.mView != null && f.mContainer != null) { 817 Animator anim = null; 818 if (mCurState > Fragment.INITIALIZING && !mDestroyed) { 819 anim = loadAnimator(f, transit, false, 820 transitionStyle); 821 } 822 if (anim != null) { 823 final ViewGroup container = f.mContainer; 824 final View view = f.mView; 825 final Fragment fragment = f; 826 container.startViewTransition(view); 827 f.mAnimatingAway = anim; 828 f.mStateAfterAnimating = newState; 829 anim.addListener(new AnimatorListenerAdapter() { 830 @Override 831 public void onAnimationEnd(Animator anim) { 832 container.endViewTransition(view); 833 if (fragment.mAnimatingAway != null) { 834 fragment.mAnimatingAway = null; 835 moveToState(fragment, fragment.mStateAfterAnimating, 836 0, 0); 837 } 838 } 839 }); 840 anim.setTarget(f.mView); 841 anim.start(); 842 843 } 844 f.mContainer.removeView(f.mView); 845 } 846 f.mContainer = null; 847 f.mView = null; 848 } 849 case Fragment.CREATED: 850 if (newState < Fragment.CREATED) { 851 if (mDestroyed) { 852 if (f.mAnimatingAway != null) { 853 // The fragment's containing activity is 854 // being destroyed, but this fragment is 855 // currently animating away. Stop the 856 // animation right now -- it is not needed, 857 // and we can't wait any more on destroying 858 // the fragment. 859 Animator anim = f.mAnimatingAway; 860 f.mAnimatingAway = null; 861 anim.cancel(); 862 } 863 } 864 if (f.mAnimatingAway != null) { 865 // We are waiting for the fragment's view to finish 866 // animating away. Just make a note of the state 867 // the fragment now should move to once the animation 868 // is done. 869 f.mStateAfterAnimating = newState; 870 } else { 871 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 872 if (!f.mRetaining) { 873 f.mCalled = false; 874 f.onDestroy(); 875 if (!f.mCalled) { 876 throw new SuperNotCalledException("Fragment " + f 877 + " did not call through to super.onDestroy()"); 878 } 879 } 880 881 f.mCalled = false; 882 f.onDetach(); 883 if (!f.mCalled) { 884 throw new SuperNotCalledException("Fragment " + f 885 + " did not call through to super.onDetach()"); 886 } 887 f.mImmediateActivity = null; 888 f.mActivity = null; 889 f.mFragmentManager = null; 890 } 891 } 892 } 893 } 894 895 f.mState = newState; 896 } 897 898 void moveToState(Fragment f) { 899 moveToState(f, mCurState, 0, 0); 900 } 901 902 void moveToState(int newState, boolean always) { 903 moveToState(newState, 0, 0, always); 904 } 905 906 void moveToState(int newState, int transit, int transitStyle, boolean always) { 907 if (mActivity == null && newState != Fragment.INITIALIZING) { 908 throw new IllegalStateException("No activity"); 909 } 910 911 if (!always && mCurState == newState) { 912 return; 913 } 914 915 mCurState = newState; 916 if (mActive != null) { 917 for (int i=0; i<mActive.size(); i++) { 918 Fragment f = mActive.get(i); 919 if (f != null) { 920 moveToState(f, newState, transit, transitStyle); 921 } 922 } 923 924 if (mNeedMenuInvalidate && mActivity != null) { 925 mActivity.invalidateOptionsMenu(); 926 mNeedMenuInvalidate = false; 927 } 928 } 929 } 930 931 void makeActive(Fragment f) { 932 if (f.mIndex >= 0) { 933 return; 934 } 935 936 if (mAvailIndices == null || mAvailIndices.size() <= 0) { 937 if (mActive == null) { 938 mActive = new ArrayList<Fragment>(); 939 } 940 f.setIndex(mActive.size()); 941 mActive.add(f); 942 943 } else { 944 f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1)); 945 mActive.set(f.mIndex, f); 946 } 947 } 948 949 void makeInactive(Fragment f) { 950 if (f.mIndex < 0) { 951 return; 952 } 953 954 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex); 955 mActive.set(f.mIndex, null); 956 if (mAvailIndices == null) { 957 mAvailIndices = new ArrayList<Integer>(); 958 } 959 mAvailIndices.add(f.mIndex); 960 mActivity.invalidateFragmentIndex(f.mIndex); 961 f.clearIndex(); 962 } 963 964 public void addFragment(Fragment fragment, boolean moveToStateNow) { 965 if (mAdded == null) { 966 mAdded = new ArrayList<Fragment>(); 967 } 968 mAdded.add(fragment); 969 makeActive(fragment); 970 if (DEBUG) Log.v(TAG, "add: " + fragment); 971 fragment.mAdded = true; 972 fragment.mRemoving = false; 973 if (fragment.mHasMenu) { 974 mNeedMenuInvalidate = true; 975 } 976 if (moveToStateNow) { 977 moveToState(fragment); 978 } 979 } 980 981 public void removeFragment(Fragment fragment, int transition, int transitionStyle) { 982 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 983 mAdded.remove(fragment); 984 final boolean inactive = fragment.mBackStackNesting <= 0; 985 if (fragment.mHasMenu) { 986 mNeedMenuInvalidate = true; 987 } 988 fragment.mAdded = false; 989 fragment.mRemoving = true; 990 moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, 991 transition, transitionStyle); 992 if (inactive) { 993 makeInactive(fragment); 994 } 995 } 996 997 public void hideFragment(Fragment fragment, int transition, int transitionStyle) { 998 if (DEBUG) Log.v(TAG, "hide: " + fragment); 999 if (!fragment.mHidden) { 1000 fragment.mHidden = true; 1001 if (fragment.mView != null) { 1002 Animator anim = loadAnimator(fragment, transition, true, 1003 transitionStyle); 1004 if (anim != null) { 1005 anim.setTarget(fragment.mView); 1006 // Delay the actual hide operation until the animation finishes, otherwise 1007 // the fragment will just immediately disappear 1008 final Fragment finalFragment = fragment; 1009 anim.addListener(new AnimatorListenerAdapter() { 1010 @Override 1011 public void onAnimationEnd(Animator animation) { 1012 if (finalFragment.mView != null) { 1013 finalFragment.mView.setVisibility(View.GONE); 1014 } 1015 } 1016 }); 1017 anim.start(); 1018 } else { 1019 fragment.mView.setVisibility(View.GONE); 1020 } 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 Animator anim = loadAnimator(fragment, transition, true, 1035 transitionStyle); 1036 if (anim != null) { 1037 anim.setTarget(fragment.mView); 1038 anim.start(); 1039 } 1040 fragment.mView.setVisibility(View.VISIBLE); 1041 } 1042 if (fragment.mAdded && fragment.mHasMenu) { 1043 mNeedMenuInvalidate = true; 1044 } 1045 fragment.onHiddenChanged(false); 1046 } 1047 } 1048 1049 public Fragment findFragmentById(int id) { 1050 if (mActive != null) { 1051 // First look through added fragments. 1052 for (int i=mAdded.size()-1; i>=0; i--) { 1053 Fragment f = mAdded.get(i); 1054 if (f != null && f.mFragmentId == id) { 1055 return f; 1056 } 1057 } 1058 // Now for any known fragment. 1059 for (int i=mActive.size()-1; i>=0; i--) { 1060 Fragment f = mActive.get(i); 1061 if (f != null && f.mFragmentId == id) { 1062 return f; 1063 } 1064 } 1065 } 1066 return null; 1067 } 1068 1069 public Fragment findFragmentByTag(String tag) { 1070 if (mActive != null && tag != null) { 1071 // First look through added fragments. 1072 for (int i=mAdded.size()-1; i>=0; i--) { 1073 Fragment f = mAdded.get(i); 1074 if (f != null && tag.equals(f.mTag)) { 1075 return f; 1076 } 1077 } 1078 // Now for any known fragment. 1079 for (int i=mActive.size()-1; i>=0; i--) { 1080 Fragment f = mActive.get(i); 1081 if (f != null && tag.equals(f.mTag)) { 1082 return f; 1083 } 1084 } 1085 } 1086 return null; 1087 } 1088 1089 public Fragment findFragmentByWho(String who) { 1090 if (mActive != null && who != null) { 1091 for (int i=mActive.size()-1; i>=0; i--) { 1092 Fragment f = mActive.get(i); 1093 if (f != null && who.equals(f.mWho)) { 1094 return f; 1095 } 1096 } 1097 } 1098 return null; 1099 } 1100 1101 private void checkStateLoss() { 1102 if (mStateSaved) { 1103 throw new IllegalStateException( 1104 "Can not perform this action after onSaveInstanceState"); 1105 } 1106 if (mNoTransactionsBecause != null) { 1107 throw new IllegalStateException( 1108 "Can not perform this action inside of " + mNoTransactionsBecause); 1109 } 1110 } 1111 1112 public void enqueueAction(Runnable action, boolean allowStateLoss) { 1113 if (!allowStateLoss) { 1114 checkStateLoss(); 1115 } 1116 synchronized (this) { 1117 if (mActivity == null) { 1118 throw new IllegalStateException("Activity has been destroyed"); 1119 } 1120 if (mPendingActions == null) { 1121 mPendingActions = new ArrayList<Runnable>(); 1122 } 1123 mPendingActions.add(action); 1124 if (mPendingActions.size() == 1) { 1125 mActivity.mHandler.removeCallbacks(mExecCommit); 1126 mActivity.mHandler.post(mExecCommit); 1127 } 1128 } 1129 } 1130 1131 public int allocBackStackIndex(BackStackRecord bse) { 1132 synchronized (this) { 1133 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 1134 if (mBackStackIndices == null) { 1135 mBackStackIndices = new ArrayList<BackStackRecord>(); 1136 } 1137 int index = mBackStackIndices.size(); 1138 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1139 mBackStackIndices.add(bse); 1140 return index; 1141 1142 } else { 1143 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 1144 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1145 mBackStackIndices.set(index, bse); 1146 return index; 1147 } 1148 } 1149 } 1150 1151 public void setBackStackIndex(int index, BackStackRecord bse) { 1152 synchronized (this) { 1153 if (mBackStackIndices == null) { 1154 mBackStackIndices = new ArrayList<BackStackRecord>(); 1155 } 1156 int N = mBackStackIndices.size(); 1157 if (index < N) { 1158 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1159 mBackStackIndices.set(index, bse); 1160 } else { 1161 while (N < index) { 1162 mBackStackIndices.add(null); 1163 if (mAvailBackStackIndices == null) { 1164 mAvailBackStackIndices = new ArrayList<Integer>(); 1165 } 1166 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 1167 mAvailBackStackIndices.add(N); 1168 N++; 1169 } 1170 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1171 mBackStackIndices.add(bse); 1172 } 1173 } 1174 } 1175 1176 public void freeBackStackIndex(int index) { 1177 synchronized (this) { 1178 mBackStackIndices.set(index, null); 1179 if (mAvailBackStackIndices == null) { 1180 mAvailBackStackIndices = new ArrayList<Integer>(); 1181 } 1182 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 1183 mAvailBackStackIndices.add(index); 1184 } 1185 } 1186 1187 /** 1188 * Only call from main thread! 1189 */ 1190 public boolean execPendingActions() { 1191 if (mExecutingActions) { 1192 throw new IllegalStateException("Recursive entry to executePendingTransactions"); 1193 } 1194 1195 if (Looper.myLooper() != mActivity.mHandler.getLooper()) { 1196 throw new IllegalStateException("Must be called from main thread of process"); 1197 } 1198 1199 boolean didSomething = false; 1200 1201 while (true) { 1202 int numActions; 1203 1204 synchronized (this) { 1205 if (mPendingActions == null || mPendingActions.size() == 0) { 1206 return didSomething; 1207 } 1208 1209 numActions = mPendingActions.size(); 1210 if (mTmpActions == null || mTmpActions.length < numActions) { 1211 mTmpActions = new Runnable[numActions]; 1212 } 1213 mPendingActions.toArray(mTmpActions); 1214 mPendingActions.clear(); 1215 mActivity.mHandler.removeCallbacks(mExecCommit); 1216 } 1217 1218 mExecutingActions = true; 1219 for (int i=0; i<numActions; i++) { 1220 mTmpActions[i].run(); 1221 } 1222 mExecutingActions = false; 1223 didSomething = true; 1224 } 1225 } 1226 1227 void reportBackStackChanged() { 1228 if (mBackStackChangeListeners != null) { 1229 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 1230 mBackStackChangeListeners.get(i).onBackStackChanged(); 1231 } 1232 } 1233 } 1234 1235 void addBackStackState(BackStackRecord state) { 1236 if (mBackStack == null) { 1237 mBackStack = new ArrayList<BackStackRecord>(); 1238 } 1239 mBackStack.add(state); 1240 reportBackStackChanged(); 1241 } 1242 1243 boolean popBackStackState(Handler handler, String name, int id, int flags) { 1244 if (mBackStack == null) { 1245 return false; 1246 } 1247 if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { 1248 int last = mBackStack.size()-1; 1249 if (last < 0) { 1250 return false; 1251 } 1252 final BackStackRecord bss = mBackStack.remove(last); 1253 bss.popFromBackStack(true); 1254 reportBackStackChanged(); 1255 } else { 1256 int index = -1; 1257 if (name != null || id >= 0) { 1258 // If a name or ID is specified, look for that place in 1259 // the stack. 1260 index = mBackStack.size()-1; 1261 while (index >= 0) { 1262 BackStackRecord bss = mBackStack.get(index); 1263 if (name != null && name.equals(bss.getName())) { 1264 break; 1265 } 1266 if (id >= 0 && id == bss.mIndex) { 1267 break; 1268 } 1269 index--; 1270 } 1271 if (index < 0) { 1272 return false; 1273 } 1274 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { 1275 index--; 1276 // Consume all following entries that match. 1277 while (index >= 0) { 1278 BackStackRecord bss = mBackStack.get(index); 1279 if ((name != null && name.equals(bss.getName())) 1280 || (id >= 0 && id == bss.mIndex)) { 1281 index--; 1282 continue; 1283 } 1284 break; 1285 } 1286 } 1287 } 1288 if (index == mBackStack.size()-1) { 1289 return false; 1290 } 1291 final ArrayList<BackStackRecord> states 1292 = new ArrayList<BackStackRecord>(); 1293 for (int i=mBackStack.size()-1; i>index; i--) { 1294 states.add(mBackStack.remove(i)); 1295 } 1296 final int LAST = states.size()-1; 1297 for (int i=0; i<=LAST; i++) { 1298 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); 1299 states.get(i).popFromBackStack(i == LAST); 1300 } 1301 reportBackStackChanged(); 1302 } 1303 return true; 1304 } 1305 1306 ArrayList<Fragment> retainNonConfig() { 1307 ArrayList<Fragment> fragments = null; 1308 if (mActive != null) { 1309 for (int i=0; i<mActive.size(); i++) { 1310 Fragment f = mActive.get(i); 1311 if (f != null && f.mRetainInstance) { 1312 if (fragments == null) { 1313 fragments = new ArrayList<Fragment>(); 1314 } 1315 fragments.add(f); 1316 f.mRetaining = true; 1317 } 1318 } 1319 } 1320 return fragments; 1321 } 1322 1323 void saveFragmentViewState(Fragment f) { 1324 if (f.mView == null) { 1325 return; 1326 } 1327 if (mStateArray == null) { 1328 mStateArray = new SparseArray<Parcelable>(); 1329 } 1330 f.mView.saveHierarchyState(mStateArray); 1331 if (mStateArray.size() > 0) { 1332 f.mSavedViewState = mStateArray; 1333 mStateArray = null; 1334 } 1335 } 1336 1337 Parcelable saveAllState() { 1338 // Make sure all pending operations have now been executed to get 1339 // our state update-to-date. 1340 execPendingActions(); 1341 1342 mStateSaved = true; 1343 1344 if (mActive == null || mActive.size() <= 0) { 1345 return null; 1346 } 1347 1348 // First collect all active fragments. 1349 int N = mActive.size(); 1350 FragmentState[] active = new FragmentState[N]; 1351 boolean haveFragments = false; 1352 for (int i=0; i<N; i++) { 1353 Fragment f = mActive.get(i); 1354 if (f != null) { 1355 haveFragments = true; 1356 1357 FragmentState fs = new FragmentState(f); 1358 active[i] = fs; 1359 1360 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 1361 if (mStateBundle == null) { 1362 mStateBundle = new Bundle(); 1363 } 1364 f.onSaveInstanceState(mStateBundle); 1365 if (!mStateBundle.isEmpty()) { 1366 fs.mSavedFragmentState = mStateBundle; 1367 mStateBundle = null; 1368 } 1369 1370 if (f.mView != null) { 1371 saveFragmentViewState(f); 1372 if (f.mSavedViewState != null) { 1373 if (fs.mSavedFragmentState == null) { 1374 fs.mSavedFragmentState = new Bundle(); 1375 } 1376 fs.mSavedFragmentState.putSparseParcelableArray( 1377 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 1378 } 1379 } 1380 1381 if (f.mTarget != null) { 1382 if (f.mTarget.mIndex < 0) { 1383 String msg = "Failure saving state: " + f 1384 + " has target not in fragment manager: " + f.mTarget; 1385 Slog.e(TAG, msg); 1386 dump(" ", null, new PrintWriter(new LogWriter( 1387 Log.ERROR, TAG, Log.LOG_ID_SYSTEM)), new String[] { }); 1388 throw new IllegalStateException(msg); 1389 } 1390 if (fs.mSavedFragmentState == null) { 1391 fs.mSavedFragmentState = new Bundle(); 1392 } 1393 putFragment(fs.mSavedFragmentState, 1394 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 1395 if (f.mTargetRequestCode != 0) { 1396 fs.mSavedFragmentState.putInt( 1397 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 1398 f.mTargetRequestCode); 1399 } 1400 } 1401 1402 } else { 1403 fs.mSavedFragmentState = f.mSavedFragmentState; 1404 } 1405 1406 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 1407 + fs.mSavedFragmentState); 1408 } 1409 } 1410 1411 if (!haveFragments) { 1412 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 1413 return null; 1414 } 1415 1416 int[] added = null; 1417 BackStackState[] backStack = null; 1418 1419 // Build list of currently added fragments. 1420 if (mAdded != null) { 1421 N = mAdded.size(); 1422 if (N > 0) { 1423 added = new int[N]; 1424 for (int i=0; i<N; i++) { 1425 added[i] = mAdded.get(i).mIndex; 1426 if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i 1427 + ": " + mAdded.get(i)); 1428 } 1429 } 1430 } 1431 1432 // Now save back stack. 1433 if (mBackStack != null) { 1434 N = mBackStack.size(); 1435 if (N > 0) { 1436 backStack = new BackStackState[N]; 1437 for (int i=0; i<N; i++) { 1438 backStack[i] = new BackStackState(this, mBackStack.get(i)); 1439 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 1440 + ": " + mBackStack.get(i)); 1441 } 1442 } 1443 } 1444 1445 FragmentManagerState fms = new FragmentManagerState(); 1446 fms.mActive = active; 1447 fms.mAdded = added; 1448 fms.mBackStack = backStack; 1449 return fms; 1450 } 1451 1452 void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 1453 // If there is no saved state at all, then there can not be 1454 // any nonConfig fragments either, so that is that. 1455 if (state == null) return; 1456 FragmentManagerState fms = (FragmentManagerState)state; 1457 if (fms.mActive == null) return; 1458 1459 // First re-attach any non-config instances we are retaining back 1460 // to their saved state, so we don't try to instantiate them again. 1461 if (nonConfig != null) { 1462 for (int i=0; i<nonConfig.size(); i++) { 1463 Fragment f = nonConfig.get(i); 1464 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 1465 FragmentState fs = fms.mActive[f.mIndex]; 1466 fs.mInstance = f; 1467 f.mSavedViewState = null; 1468 f.mBackStackNesting = 0; 1469 f.mInLayout = false; 1470 f.mAdded = false; 1471 if (fs.mSavedFragmentState != null) { 1472 fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); 1473 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 1474 FragmentManagerImpl.VIEW_STATE_TAG); 1475 } 1476 } 1477 } 1478 1479 // Build the full list of active fragments, instantiating them from 1480 // their saved state. 1481 mActive = new ArrayList<Fragment>(fms.mActive.length); 1482 if (mAvailIndices != null) { 1483 mAvailIndices.clear(); 1484 } 1485 for (int i=0; i<fms.mActive.length; i++) { 1486 FragmentState fs = fms.mActive[i]; 1487 if (fs != null) { 1488 Fragment f = fs.instantiate(mActivity); 1489 if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f); 1490 mActive.add(f); 1491 // Now that the fragment is instantiated (or came from being 1492 // retained above), clear mInstance in case we end up re-restoring 1493 // from this FragmentState again. 1494 fs.mInstance = null; 1495 } else { 1496 if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)"); 1497 mActive.add(null); 1498 if (mAvailIndices == null) { 1499 mAvailIndices = new ArrayList<Integer>(); 1500 } 1501 if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i); 1502 mAvailIndices.add(i); 1503 } 1504 } 1505 1506 // Update the target of all retained fragments. 1507 if (nonConfig != null) { 1508 for (int i=0; i<nonConfig.size(); i++) { 1509 Fragment f = nonConfig.get(i); 1510 if (f.mTarget != null) { 1511 if (f.mTarget.mIndex < mActive.size()) { 1512 f.mTarget = mActive.get(f.mTarget.mIndex); 1513 } else { 1514 Log.w(TAG, "Re-attaching retained fragment " + f 1515 + " target no longer exists: " + f.mTarget); 1516 f.mTarget = null; 1517 } 1518 } 1519 } 1520 } 1521 1522 // Build the list of currently added fragments. 1523 if (fms.mAdded != null) { 1524 mAdded = new ArrayList<Fragment>(fms.mAdded.length); 1525 for (int i=0; i<fms.mAdded.length; i++) { 1526 Fragment f = mActive.get(fms.mAdded[i]); 1527 if (f == null) { 1528 throw new IllegalStateException( 1529 "No instantiated fragment for index #" + fms.mAdded[i]); 1530 } 1531 f.mAdded = true; 1532 f.mImmediateActivity = mActivity; 1533 if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f); 1534 mAdded.add(f); 1535 } 1536 } else { 1537 mAdded = null; 1538 } 1539 1540 // Build the back stack. 1541 if (fms.mBackStack != null) { 1542 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 1543 for (int i=0; i<fms.mBackStack.length; i++) { 1544 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 1545 if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i 1546 + " (index " + bse.mIndex + "): " + bse); 1547 mBackStack.add(bse); 1548 if (bse.mIndex >= 0) { 1549 setBackStackIndex(bse.mIndex, bse); 1550 } 1551 } 1552 } else { 1553 mBackStack = null; 1554 } 1555 } 1556 1557 public void attachActivity(Activity activity) { 1558 if (mActivity != null) throw new IllegalStateException(); 1559 mActivity = activity; 1560 } 1561 1562 public void noteStateNotSaved() { 1563 mStateSaved = false; 1564 } 1565 1566 public void dispatchCreate() { 1567 mStateSaved = false; 1568 moveToState(Fragment.CREATED, false); 1569 } 1570 1571 public void dispatchActivityCreated() { 1572 mStateSaved = false; 1573 moveToState(Fragment.ACTIVITY_CREATED, false); 1574 } 1575 1576 public void dispatchStart() { 1577 mStateSaved = false; 1578 moveToState(Fragment.STARTED, false); 1579 } 1580 1581 public void dispatchResume() { 1582 mStateSaved = false; 1583 moveToState(Fragment.RESUMED, false); 1584 } 1585 1586 public void dispatchPause() { 1587 moveToState(Fragment.STARTED, false); 1588 } 1589 1590 public void dispatchStop() { 1591 moveToState(Fragment.ACTIVITY_CREATED, false); 1592 } 1593 1594 public void dispatchDestroy() { 1595 mDestroyed = true; 1596 moveToState(Fragment.INITIALIZING, false); 1597 mActivity = null; 1598 } 1599 1600 public void dispatchConfigurationChanged(Configuration newConfig) { 1601 if (mActive != null) { 1602 for (int i=0; i<mAdded.size(); i++) { 1603 Fragment f = mAdded.get(i); 1604 if (f != null) { 1605 f.onConfigurationChanged(newConfig); 1606 } 1607 } 1608 } 1609 } 1610 1611 public void dispatchLowMemory() { 1612 if (mActive != null) { 1613 for (int i=0; i<mAdded.size(); i++) { 1614 Fragment f = mAdded.get(i); 1615 if (f != null) { 1616 f.onLowMemory(); 1617 } 1618 } 1619 } 1620 } 1621 1622 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1623 boolean show = false; 1624 ArrayList<Fragment> newMenus = null; 1625 if (mActive != null) { 1626 for (int i=0; i<mAdded.size(); i++) { 1627 Fragment f = mAdded.get(i); 1628 if (f != null && !f.mHidden && f.mHasMenu) { 1629 show = true; 1630 f.onCreateOptionsMenu(menu, inflater); 1631 if (newMenus == null) { 1632 newMenus = new ArrayList<Fragment>(); 1633 } 1634 newMenus.add(f); 1635 } 1636 } 1637 } 1638 1639 if (mCreatedMenus != null) { 1640 for (int i=0; i<mCreatedMenus.size(); i++) { 1641 Fragment f = mCreatedMenus.get(i); 1642 if (newMenus == null || !newMenus.contains(f)) { 1643 f.onDestroyOptionsMenu(); 1644 } 1645 } 1646 } 1647 1648 mCreatedMenus = newMenus; 1649 1650 return show; 1651 } 1652 1653 public boolean dispatchPrepareOptionsMenu(Menu menu) { 1654 boolean show = false; 1655 if (mActive != null) { 1656 for (int i=0; i<mAdded.size(); i++) { 1657 Fragment f = mAdded.get(i); 1658 if (f != null && !f.mHidden && f.mHasMenu) { 1659 show = true; 1660 f.onPrepareOptionsMenu(menu); 1661 } 1662 } 1663 } 1664 return show; 1665 } 1666 1667 public boolean dispatchOptionsItemSelected(MenuItem item) { 1668 if (mActive != null) { 1669 for (int i=0; i<mAdded.size(); i++) { 1670 Fragment f = mAdded.get(i); 1671 if (f != null && !f.mHidden && f.mHasMenu) { 1672 if (f.onOptionsItemSelected(item)) { 1673 return true; 1674 } 1675 } 1676 } 1677 } 1678 return false; 1679 } 1680 1681 public boolean dispatchContextItemSelected(MenuItem item) { 1682 if (mActive != null) { 1683 for (int i=0; i<mAdded.size(); i++) { 1684 Fragment f = mAdded.get(i); 1685 if (f != null && !f.mHidden) { 1686 if (f.onContextItemSelected(item)) { 1687 return true; 1688 } 1689 } 1690 } 1691 } 1692 return false; 1693 } 1694 1695 public void dispatchOptionsMenuClosed(Menu menu) { 1696 if (mActive != null) { 1697 for (int i=0; i<mAdded.size(); i++) { 1698 Fragment f = mAdded.get(i); 1699 if (f != null && !f.mHidden && f.mHasMenu) { 1700 f.onOptionsMenuClosed(menu); 1701 } 1702 } 1703 } 1704 } 1705 1706 public static int reverseTransit(int transit) { 1707 int rev = 0; 1708 switch (transit) { 1709 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1710 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 1711 break; 1712 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1713 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 1714 break; 1715 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 1716 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; 1717 break; 1718 } 1719 return rev; 1720 1721 } 1722 1723 public static int transitToStyleIndex(int transit, boolean enter) { 1724 int animAttr = -1; 1725 switch (transit) { 1726 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1727 animAttr = enter 1728 ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation 1729 : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation; 1730 break; 1731 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1732 animAttr = enter 1733 ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation 1734 : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation; 1735 break; 1736 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 1737 animAttr = enter 1738 ? com.android.internal.R.styleable.FragmentAnimation_fragmentFadeEnterAnimation 1739 : com.android.internal.R.styleable.FragmentAnimation_fragmentFadeExitAnimation; 1740 break; 1741 } 1742 return animAttr; 1743 } 1744} 1745