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