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