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