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