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