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