FragmentManager.java revision 4500be62dea3895a98336122a2944cc4ab024bc5
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.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); 877 f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( 878 FragmentManagerImpl.VIEW_STATE_TAG); 879 f.mTarget = getFragment(f.mSavedFragmentState, 880 FragmentManagerImpl.TARGET_STATE_TAG); 881 if (f.mTarget != null) { 882 f.mTargetRequestCode = f.mSavedFragmentState.getInt( 883 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); 884 } 885 f.mUserVisibleHint = f.mSavedFragmentState.getBoolean( 886 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true); 887 if (!f.mUserVisibleHint) { 888 f.mDeferStart = true; 889 if (newState > Fragment.STOPPED) { 890 newState = Fragment.STOPPED; 891 } 892 } 893 } 894 f.mActivity = mActivity; 895 f.mParentFragment = mParent; 896 f.mFragmentManager = mParent != null 897 ? mParent.mChildFragmentManager : mActivity.mFragments; 898 f.mCalled = false; 899 f.onAttach(mActivity); 900 if (!f.mCalled) { 901 throw new SuperNotCalledException("Fragment " + f 902 + " did not call through to super.onAttach()"); 903 } 904 if (f.mParentFragment == null) { 905 mActivity.onAttachFragment(f); 906 } 907 908 if (!f.mRetaining) { 909 f.performCreate(f.mSavedFragmentState); 910 } 911 f.mRetaining = false; 912 if (f.mFromLayout) { 913 // For fragments that are part of the content view 914 // layout, we need to instantiate the view immediately 915 // and the inflater will take care of adding it. 916 f.mView = f.performCreateView(f.getLayoutInflater( 917 f.mSavedFragmentState), null, f.mSavedFragmentState); 918 if (f.mView != null) { 919 f.mInnerView = f.mView; 920 f.mView = NoSaveStateFrameLayout.wrap(f.mView); 921 if (f.mHidden) f.mView.setVisibility(View.GONE); 922 f.onViewCreated(f.mView, f.mSavedFragmentState); 923 } else { 924 f.mInnerView = null; 925 } 926 } 927 case Fragment.CREATED: 928 if (newState > Fragment.CREATED) { 929 if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); 930 if (!f.mFromLayout) { 931 ViewGroup container = null; 932 if (f.mContainerId != 0) { 933 container = (ViewGroup)mContainer.findViewById(f.mContainerId); 934 if (container == null && !f.mRestored) { 935 throwException(new IllegalArgumentException( 936 "No view found for id 0x" 937 + Integer.toHexString(f.mContainerId) + " (" 938 + f.getResources().getResourceName(f.mContainerId) 939 + ") for fragment " + f)); 940 } 941 } 942 f.mContainer = container; 943 f.mView = f.performCreateView(f.getLayoutInflater( 944 f.mSavedFragmentState), container, f.mSavedFragmentState); 945 if (f.mView != null) { 946 f.mInnerView = f.mView; 947 f.mView = NoSaveStateFrameLayout.wrap(f.mView); 948 if (container != null) { 949 Animation anim = loadAnimation(f, transit, true, 950 transitionStyle); 951 if (anim != null) { 952 f.mView.startAnimation(anim); 953 } 954 container.addView(f.mView); 955 } 956 if (f.mHidden) f.mView.setVisibility(View.GONE); 957 f.onViewCreated(f.mView, f.mSavedFragmentState); 958 } else { 959 f.mInnerView = null; 960 } 961 } 962 963 f.performActivityCreated(f.mSavedFragmentState); 964 if (f.mView != null) { 965 f.restoreViewState(f.mSavedFragmentState); 966 } 967 f.mSavedFragmentState = null; 968 } 969 case Fragment.ACTIVITY_CREATED: 970 case Fragment.STOPPED: 971 if (newState > Fragment.STOPPED) { 972 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 973 f.performStart(); 974 } 975 case Fragment.STARTED: 976 if (newState > Fragment.STARTED) { 977 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 978 f.mResumed = true; 979 f.performResume(); 980 f.mSavedFragmentState = null; 981 f.mSavedViewState = null; 982 } 983 } 984 } else if (f.mState > newState) { 985 switch (f.mState) { 986 case Fragment.RESUMED: 987 if (newState < Fragment.RESUMED) { 988 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 989 f.performPause(); 990 f.mResumed = false; 991 } 992 case Fragment.STARTED: 993 if (newState < Fragment.STARTED) { 994 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 995 f.performStop(); 996 } 997 case Fragment.STOPPED: 998 if (newState < Fragment.STOPPED) { 999 if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f); 1000 f.performReallyStop(); 1001 } 1002 case Fragment.ACTIVITY_CREATED: 1003 if (newState < Fragment.ACTIVITY_CREATED) { 1004 if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f); 1005 if (f.mView != null) { 1006 // Need to save the current view state if not 1007 // done already. 1008 if (!mActivity.isFinishing() && f.mSavedViewState == null) { 1009 saveFragmentViewState(f); 1010 } 1011 } 1012 f.performDestroyView(); 1013 if (f.mView != null && f.mContainer != null) { 1014 Animation anim = null; 1015 if (mCurState > Fragment.INITIALIZING && !mDestroyed) { 1016 anim = loadAnimation(f, transit, false, 1017 transitionStyle); 1018 } 1019 if (anim != null) { 1020 final Fragment fragment = f; 1021 f.mAnimatingAway = f.mView; 1022 f.mStateAfterAnimating = newState; 1023 anim.setAnimationListener(new AnimationListener() { 1024 @Override 1025 public void onAnimationEnd(Animation animation) { 1026 if (fragment.mAnimatingAway != null) { 1027 fragment.mAnimatingAway = null; 1028 moveToState(fragment, fragment.mStateAfterAnimating, 1029 0, 0, false); 1030 } 1031 } 1032 @Override 1033 public void onAnimationRepeat(Animation animation) { 1034 } 1035 @Override 1036 public void onAnimationStart(Animation animation) { 1037 } 1038 }); 1039 f.mView.startAnimation(anim); 1040 } 1041 f.mContainer.removeView(f.mView); 1042 } 1043 f.mContainer = null; 1044 f.mView = null; 1045 f.mInnerView = null; 1046 } 1047 case Fragment.CREATED: 1048 if (newState < Fragment.CREATED) { 1049 if (mDestroyed) { 1050 if (f.mAnimatingAway != null) { 1051 // The fragment's containing activity is 1052 // being destroyed, but this fragment is 1053 // currently animating away. Stop the 1054 // animation right now -- it is not needed, 1055 // and we can't wait any more on destroying 1056 // the fragment. 1057 View v = f.mAnimatingAway; 1058 f.mAnimatingAway = null; 1059 v.clearAnimation(); 1060 } 1061 } 1062 if (f.mAnimatingAway != null) { 1063 // We are waiting for the fragment's view to finish 1064 // animating away. Just make a note of the state 1065 // the fragment now should move to once the animation 1066 // is done. 1067 f.mStateAfterAnimating = newState; 1068 newState = Fragment.CREATED; 1069 } else { 1070 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 1071 if (!f.mRetaining) { 1072 f.performDestroy(); 1073 } 1074 1075 f.mCalled = false; 1076 f.onDetach(); 1077 if (!f.mCalled) { 1078 throw new SuperNotCalledException("Fragment " + f 1079 + " did not call through to super.onDetach()"); 1080 } 1081 if (!keepActive) { 1082 if (!f.mRetaining) { 1083 makeInactive(f); 1084 } else { 1085 f.mActivity = null; 1086 f.mParentFragment = null; 1087 f.mFragmentManager = null; 1088 f.mChildFragmentManager = null; 1089 } 1090 } 1091 } 1092 } 1093 } 1094 } 1095 1096 f.mState = newState; 1097 } 1098 1099 void moveToState(Fragment f) { 1100 moveToState(f, mCurState, 0, 0, false); 1101 } 1102 1103 void moveToState(int newState, boolean always) { 1104 moveToState(newState, 0, 0, always); 1105 } 1106 1107 void moveToState(int newState, int transit, int transitStyle, boolean always) { 1108 if (mActivity == null && newState != Fragment.INITIALIZING) { 1109 throw new IllegalStateException("No activity"); 1110 } 1111 1112 if (!always && mCurState == newState) { 1113 return; 1114 } 1115 1116 mCurState = newState; 1117 if (mActive != null) { 1118 boolean loadersRunning = false; 1119 for (int i=0; i<mActive.size(); i++) { 1120 Fragment f = mActive.get(i); 1121 if (f != null) { 1122 moveToState(f, newState, transit, transitStyle, false); 1123 if (f.mLoaderManager != null) { 1124 loadersRunning |= f.mLoaderManager.hasRunningLoaders(); 1125 } 1126 } 1127 } 1128 1129 if (!loadersRunning) { 1130 startPendingDeferredFragments(); 1131 } 1132 1133 if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) { 1134 mActivity.supportInvalidateOptionsMenu(); 1135 mNeedMenuInvalidate = false; 1136 } 1137 } 1138 } 1139 1140 void startPendingDeferredFragments() { 1141 if (mActive == null) return; 1142 1143 for (int i=0; i<mActive.size(); i++) { 1144 Fragment f = mActive.get(i); 1145 if (f != null) { 1146 performPendingDeferredStart(f); 1147 } 1148 } 1149 } 1150 1151 void makeActive(Fragment f) { 1152 if (f.mIndex >= 0) { 1153 return; 1154 } 1155 1156 if (mAvailIndices == null || mAvailIndices.size() <= 0) { 1157 if (mActive == null) { 1158 mActive = new ArrayList<Fragment>(); 1159 } 1160 f.setIndex(mActive.size(), mParent); 1161 mActive.add(f); 1162 1163 } else { 1164 f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent); 1165 mActive.set(f.mIndex, f); 1166 } 1167 if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); 1168 } 1169 1170 void makeInactive(Fragment f) { 1171 if (f.mIndex < 0) { 1172 return; 1173 } 1174 1175 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f); 1176 mActive.set(f.mIndex, null); 1177 if (mAvailIndices == null) { 1178 mAvailIndices = new ArrayList<Integer>(); 1179 } 1180 mAvailIndices.add(f.mIndex); 1181 mActivity.invalidateSupportFragment(f.mWho); 1182 f.initState(); 1183 } 1184 1185 public void addFragment(Fragment fragment, boolean moveToStateNow) { 1186 if (mAdded == null) { 1187 mAdded = new ArrayList<Fragment>(); 1188 } 1189 if (DEBUG) Log.v(TAG, "add: " + fragment); 1190 makeActive(fragment); 1191 if (!fragment.mDetached) { 1192 if (mAdded.contains(fragment)) { 1193 throw new IllegalStateException("Fragment already added: " + fragment); 1194 } 1195 mAdded.add(fragment); 1196 fragment.mAdded = true; 1197 fragment.mRemoving = false; 1198 if (fragment.mHasMenu && fragment.mMenuVisible) { 1199 mNeedMenuInvalidate = true; 1200 } 1201 if (moveToStateNow) { 1202 moveToState(fragment); 1203 } 1204 } 1205 } 1206 1207 public void removeFragment(Fragment fragment, int transition, int transitionStyle) { 1208 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 1209 final boolean inactive = !fragment.isInBackStack(); 1210 if (!fragment.mDetached || inactive) { 1211 if (mAdded != null) { 1212 mAdded.remove(fragment); 1213 } 1214 if (fragment.mHasMenu && fragment.mMenuVisible) { 1215 mNeedMenuInvalidate = true; 1216 } 1217 fragment.mAdded = false; 1218 fragment.mRemoving = true; 1219 moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, 1220 transition, transitionStyle, false); 1221 } 1222 } 1223 1224 public void hideFragment(Fragment fragment, int transition, int transitionStyle) { 1225 if (DEBUG) Log.v(TAG, "hide: " + fragment); 1226 if (!fragment.mHidden) { 1227 fragment.mHidden = true; 1228 if (fragment.mView != null) { 1229 Animation anim = loadAnimation(fragment, transition, false, 1230 transitionStyle); 1231 if (anim != null) { 1232 fragment.mView.startAnimation(anim); 1233 } 1234 fragment.mView.setVisibility(View.GONE); 1235 } 1236 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1237 mNeedMenuInvalidate = true; 1238 } 1239 fragment.onHiddenChanged(true); 1240 } 1241 } 1242 1243 public void showFragment(Fragment fragment, int transition, int transitionStyle) { 1244 if (DEBUG) Log.v(TAG, "show: " + fragment); 1245 if (fragment.mHidden) { 1246 fragment.mHidden = false; 1247 if (fragment.mView != null) { 1248 Animation anim = loadAnimation(fragment, transition, true, 1249 transitionStyle); 1250 if (anim != null) { 1251 fragment.mView.startAnimation(anim); 1252 } 1253 fragment.mView.setVisibility(View.VISIBLE); 1254 } 1255 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1256 mNeedMenuInvalidate = true; 1257 } 1258 fragment.onHiddenChanged(false); 1259 } 1260 } 1261 1262 public void detachFragment(Fragment fragment, int transition, int transitionStyle) { 1263 if (DEBUG) Log.v(TAG, "detach: " + fragment); 1264 if (!fragment.mDetached) { 1265 fragment.mDetached = true; 1266 if (fragment.mAdded) { 1267 // We are not already in back stack, so need to remove the fragment. 1268 if (mAdded != null) { 1269 if (DEBUG) Log.v(TAG, "remove from detach: " + fragment); 1270 mAdded.remove(fragment); 1271 } 1272 if (fragment.mHasMenu && fragment.mMenuVisible) { 1273 mNeedMenuInvalidate = true; 1274 } 1275 fragment.mAdded = false; 1276 moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false); 1277 } 1278 } 1279 } 1280 1281 public void attachFragment(Fragment fragment, int transition, int transitionStyle) { 1282 if (DEBUG) Log.v(TAG, "attach: " + fragment); 1283 if (fragment.mDetached) { 1284 fragment.mDetached = false; 1285 if (!fragment.mAdded) { 1286 if (mAdded == null) { 1287 mAdded = new ArrayList<Fragment>(); 1288 } 1289 if (mAdded.contains(fragment)) { 1290 throw new IllegalStateException("Fragment already added: " + fragment); 1291 } 1292 if (DEBUG) Log.v(TAG, "add from attach: " + fragment); 1293 mAdded.add(fragment); 1294 fragment.mAdded = true; 1295 if (fragment.mHasMenu && fragment.mMenuVisible) { 1296 mNeedMenuInvalidate = true; 1297 } 1298 moveToState(fragment, mCurState, transition, transitionStyle, false); 1299 } 1300 } 1301 } 1302 1303 public Fragment findFragmentById(int id) { 1304 if (mAdded != null) { 1305 // First look through added fragments. 1306 for (int i=mAdded.size()-1; i>=0; i--) { 1307 Fragment f = mAdded.get(i); 1308 if (f != null && f.mFragmentId == id) { 1309 return f; 1310 } 1311 } 1312 } 1313 if (mActive != null) { 1314 // Now for any known fragment. 1315 for (int i=mActive.size()-1; i>=0; i--) { 1316 Fragment f = mActive.get(i); 1317 if (f != null && f.mFragmentId == id) { 1318 return f; 1319 } 1320 } 1321 } 1322 return null; 1323 } 1324 1325 public Fragment findFragmentByTag(String tag) { 1326 if (mAdded != null && tag != null) { 1327 // First look through added fragments. 1328 for (int i=mAdded.size()-1; i>=0; i--) { 1329 Fragment f = mAdded.get(i); 1330 if (f != null && tag.equals(f.mTag)) { 1331 return f; 1332 } 1333 } 1334 } 1335 if (mActive != null && tag != null) { 1336 // Now for any known fragment. 1337 for (int i=mActive.size()-1; i>=0; i--) { 1338 Fragment f = mActive.get(i); 1339 if (f != null && tag.equals(f.mTag)) { 1340 return f; 1341 } 1342 } 1343 } 1344 return null; 1345 } 1346 1347 public Fragment findFragmentByWho(String who) { 1348 if (mActive != null && who != null) { 1349 for (int i=mActive.size()-1; i>=0; i--) { 1350 Fragment f = mActive.get(i); 1351 if (f != null && (f=f.findFragmentByWho(who)) != null) { 1352 return f; 1353 } 1354 } 1355 } 1356 return null; 1357 } 1358 1359 private void checkStateLoss() { 1360 if (mStateSaved) { 1361 throw new IllegalStateException( 1362 "Can not perform this action after onSaveInstanceState"); 1363 } 1364 if (mNoTransactionsBecause != null) { 1365 throw new IllegalStateException( 1366 "Can not perform this action inside of " + mNoTransactionsBecause); 1367 } 1368 } 1369 1370 /** 1371 * Adds an action to the queue of pending actions. 1372 * 1373 * @param action the action to add 1374 * @param allowStateLoss whether to allow loss of state information 1375 * @throws IllegalStateException if the activity has been destroyed 1376 */ 1377 public void enqueueAction(Runnable action, boolean allowStateLoss) { 1378 if (!allowStateLoss) { 1379 checkStateLoss(); 1380 } 1381 synchronized (this) { 1382 if (mDestroyed || mActivity == null) { 1383 throw new IllegalStateException("Activity has been destroyed"); 1384 } 1385 if (mPendingActions == null) { 1386 mPendingActions = new ArrayList<Runnable>(); 1387 } 1388 mPendingActions.add(action); 1389 if (mPendingActions.size() == 1) { 1390 mActivity.mHandler.removeCallbacks(mExecCommit); 1391 mActivity.mHandler.post(mExecCommit); 1392 } 1393 } 1394 } 1395 1396 public int allocBackStackIndex(BackStackRecord bse) { 1397 synchronized (this) { 1398 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 1399 if (mBackStackIndices == null) { 1400 mBackStackIndices = new ArrayList<BackStackRecord>(); 1401 } 1402 int index = mBackStackIndices.size(); 1403 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1404 mBackStackIndices.add(bse); 1405 return index; 1406 1407 } else { 1408 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 1409 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1410 mBackStackIndices.set(index, bse); 1411 return index; 1412 } 1413 } 1414 } 1415 1416 public void setBackStackIndex(int index, BackStackRecord bse) { 1417 synchronized (this) { 1418 if (mBackStackIndices == null) { 1419 mBackStackIndices = new ArrayList<BackStackRecord>(); 1420 } 1421 int N = mBackStackIndices.size(); 1422 if (index < N) { 1423 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1424 mBackStackIndices.set(index, bse); 1425 } else { 1426 while (N < index) { 1427 mBackStackIndices.add(null); 1428 if (mAvailBackStackIndices == null) { 1429 mAvailBackStackIndices = new ArrayList<Integer>(); 1430 } 1431 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 1432 mAvailBackStackIndices.add(N); 1433 N++; 1434 } 1435 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1436 mBackStackIndices.add(bse); 1437 } 1438 } 1439 } 1440 1441 public void freeBackStackIndex(int index) { 1442 synchronized (this) { 1443 mBackStackIndices.set(index, null); 1444 if (mAvailBackStackIndices == null) { 1445 mAvailBackStackIndices = new ArrayList<Integer>(); 1446 } 1447 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 1448 mAvailBackStackIndices.add(index); 1449 } 1450 } 1451 1452 /** 1453 * Only call from main thread! 1454 */ 1455 public boolean execPendingActions() { 1456 if (mExecutingActions) { 1457 throw new IllegalStateException("Recursive entry to executePendingTransactions"); 1458 } 1459 1460 if (Looper.myLooper() != mActivity.mHandler.getLooper()) { 1461 throw new IllegalStateException("Must be called from main thread of process"); 1462 } 1463 1464 boolean didSomething = false; 1465 1466 while (true) { 1467 int numActions; 1468 1469 synchronized (this) { 1470 if (mPendingActions == null || mPendingActions.size() == 0) { 1471 break; 1472 } 1473 1474 numActions = mPendingActions.size(); 1475 if (mTmpActions == null || mTmpActions.length < numActions) { 1476 mTmpActions = new Runnable[numActions]; 1477 } 1478 mPendingActions.toArray(mTmpActions); 1479 mPendingActions.clear(); 1480 mActivity.mHandler.removeCallbacks(mExecCommit); 1481 } 1482 1483 mExecutingActions = true; 1484 for (int i=0; i<numActions; i++) { 1485 mTmpActions[i].run(); 1486 mTmpActions[i] = null; 1487 } 1488 mExecutingActions = false; 1489 didSomething = true; 1490 } 1491 1492 if (mHavePendingDeferredStart) { 1493 boolean loadersRunning = false; 1494 for (int i=0; i<mActive.size(); i++) { 1495 Fragment f = mActive.get(i); 1496 if (f != null && f.mLoaderManager != null) { 1497 loadersRunning |= f.mLoaderManager.hasRunningLoaders(); 1498 } 1499 } 1500 if (!loadersRunning) { 1501 mHavePendingDeferredStart = false; 1502 startPendingDeferredFragments(); 1503 } 1504 } 1505 return didSomething; 1506 } 1507 1508 void reportBackStackChanged() { 1509 if (mBackStackChangeListeners != null) { 1510 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 1511 mBackStackChangeListeners.get(i).onBackStackChanged(); 1512 } 1513 } 1514 } 1515 1516 void addBackStackState(BackStackRecord state) { 1517 if (mBackStack == null) { 1518 mBackStack = new ArrayList<BackStackRecord>(); 1519 } 1520 mBackStack.add(state); 1521 reportBackStackChanged(); 1522 } 1523 1524 boolean popBackStackState(Handler handler, String name, int id, int flags) { 1525 if (mBackStack == null) { 1526 return false; 1527 } 1528 if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { 1529 int last = mBackStack.size()-1; 1530 if (last < 0) { 1531 return false; 1532 } 1533 final BackStackRecord bss = mBackStack.remove(last); 1534 bss.popFromBackStack(true, null); 1535 reportBackStackChanged(); 1536 } else { 1537 int index = -1; 1538 if (name != null || id >= 0) { 1539 // If a name or ID is specified, look for that place in 1540 // the stack. 1541 index = mBackStack.size()-1; 1542 while (index >= 0) { 1543 BackStackRecord bss = mBackStack.get(index); 1544 if (name != null && name.equals(bss.getName())) { 1545 break; 1546 } 1547 if (id >= 0 && id == bss.mIndex) { 1548 break; 1549 } 1550 index--; 1551 } 1552 if (index < 0) { 1553 return false; 1554 } 1555 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { 1556 index--; 1557 // Consume all following entries that match. 1558 while (index >= 0) { 1559 BackStackRecord bss = mBackStack.get(index); 1560 if ((name != null && name.equals(bss.getName())) 1561 || (id >= 0 && id == bss.mIndex)) { 1562 index--; 1563 continue; 1564 } 1565 break; 1566 } 1567 } 1568 } 1569 if (index == mBackStack.size()-1) { 1570 return false; 1571 } 1572 final ArrayList<BackStackRecord> states 1573 = new ArrayList<BackStackRecord>(); 1574 for (int i=mBackStack.size()-1; i>index; i--) { 1575 states.add(mBackStack.remove(i)); 1576 } 1577 final int LAST = states.size()-1; 1578 Object transitionState = null; 1579 for (int i=0; i<=LAST; i++) { 1580 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); 1581 transitionState = states.get(i).popFromBackStack(i == LAST, transitionState); 1582 } 1583 reportBackStackChanged(); 1584 } 1585 return true; 1586 } 1587 1588 ArrayList<Fragment> retainNonConfig() { 1589 ArrayList<Fragment> fragments = null; 1590 if (mActive != null) { 1591 for (int i=0; i<mActive.size(); i++) { 1592 Fragment f = mActive.get(i); 1593 if (f != null && f.mRetainInstance) { 1594 if (fragments == null) { 1595 fragments = new ArrayList<Fragment>(); 1596 } 1597 fragments.add(f); 1598 f.mRetaining = true; 1599 f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; 1600 if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); 1601 } 1602 } 1603 } 1604 return fragments; 1605 } 1606 1607 void saveFragmentViewState(Fragment f) { 1608 if (f.mInnerView == null) { 1609 return; 1610 } 1611 if (mStateArray == null) { 1612 mStateArray = new SparseArray<Parcelable>(); 1613 } else { 1614 mStateArray.clear(); 1615 } 1616 f.mInnerView.saveHierarchyState(mStateArray); 1617 if (mStateArray.size() > 0) { 1618 f.mSavedViewState = mStateArray; 1619 mStateArray = null; 1620 } 1621 } 1622 1623 Bundle saveFragmentBasicState(Fragment f) { 1624 Bundle result = null; 1625 1626 if (mStateBundle == null) { 1627 mStateBundle = new Bundle(); 1628 } 1629 f.performSaveInstanceState(mStateBundle); 1630 if (!mStateBundle.isEmpty()) { 1631 result = mStateBundle; 1632 mStateBundle = null; 1633 } 1634 1635 if (f.mView != null) { 1636 saveFragmentViewState(f); 1637 } 1638 if (f.mSavedViewState != null) { 1639 if (result == null) { 1640 result = new Bundle(); 1641 } 1642 result.putSparseParcelableArray( 1643 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 1644 } 1645 if (!f.mUserVisibleHint) { 1646 if (result == null) { 1647 result = new Bundle(); 1648 } 1649 // Only add this if it's not the default value 1650 result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint); 1651 } 1652 1653 return result; 1654 } 1655 1656 Parcelable saveAllState() { 1657 // Make sure all pending operations have now been executed to get 1658 // our state update-to-date. 1659 execPendingActions(); 1660 1661 if (HONEYCOMB) { 1662 // As of Honeycomb, we save state after pausing. Prior to that 1663 // it is before pausing. With fragments this is an issue, since 1664 // there are many things you may do after pausing but before 1665 // stopping that change the fragment state. For those older 1666 // devices, we will not at this point say that we have saved 1667 // the state, so we will allow them to continue doing fragment 1668 // transactions. This retains the same semantics as Honeycomb, 1669 // though you do have the risk of losing the very most recent state 1670 // if the process is killed... we'll live with that. 1671 mStateSaved = true; 1672 } 1673 1674 if (mActive == null || mActive.size() <= 0) { 1675 return null; 1676 } 1677 1678 // First collect all active fragments. 1679 int N = mActive.size(); 1680 FragmentState[] active = new FragmentState[N]; 1681 boolean haveFragments = false; 1682 for (int i=0; i<N; i++) { 1683 Fragment f = mActive.get(i); 1684 if (f != null) { 1685 if (f.mIndex < 0) { 1686 throwException(new IllegalStateException( 1687 "Failure saving state: active " + f 1688 + " has cleared index: " + f.mIndex)); 1689 } 1690 1691 haveFragments = true; 1692 1693 FragmentState fs = new FragmentState(f); 1694 active[i] = fs; 1695 1696 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 1697 fs.mSavedFragmentState = saveFragmentBasicState(f); 1698 1699 if (f.mTarget != null) { 1700 if (f.mTarget.mIndex < 0) { 1701 throwException(new IllegalStateException( 1702 "Failure saving state: " + f 1703 + " has target not in fragment manager: " + f.mTarget)); 1704 } 1705 if (fs.mSavedFragmentState == null) { 1706 fs.mSavedFragmentState = new Bundle(); 1707 } 1708 putFragment(fs.mSavedFragmentState, 1709 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 1710 if (f.mTargetRequestCode != 0) { 1711 fs.mSavedFragmentState.putInt( 1712 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 1713 f.mTargetRequestCode); 1714 } 1715 } 1716 1717 } else { 1718 fs.mSavedFragmentState = f.mSavedFragmentState; 1719 } 1720 1721 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 1722 + fs.mSavedFragmentState); 1723 } 1724 } 1725 1726 if (!haveFragments) { 1727 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 1728 return null; 1729 } 1730 1731 int[] added = null; 1732 BackStackState[] backStack = null; 1733 1734 // Build list of currently added fragments. 1735 if (mAdded != null) { 1736 N = mAdded.size(); 1737 if (N > 0) { 1738 added = new int[N]; 1739 for (int i=0; i<N; i++) { 1740 added[i] = mAdded.get(i).mIndex; 1741 if (added[i] < 0) { 1742 throwException(new IllegalStateException( 1743 "Failure saving state: active " + mAdded.get(i) 1744 + " has cleared index: " + added[i])); 1745 } 1746 if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i 1747 + ": " + mAdded.get(i)); 1748 } 1749 } 1750 } 1751 1752 // Now save back stack. 1753 if (mBackStack != null) { 1754 N = mBackStack.size(); 1755 if (N > 0) { 1756 backStack = new BackStackState[N]; 1757 for (int i=0; i<N; i++) { 1758 backStack[i] = new BackStackState(this, mBackStack.get(i)); 1759 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 1760 + ": " + mBackStack.get(i)); 1761 } 1762 } 1763 } 1764 1765 FragmentManagerState fms = new FragmentManagerState(); 1766 fms.mActive = active; 1767 fms.mAdded = added; 1768 fms.mBackStack = backStack; 1769 return fms; 1770 } 1771 1772 void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 1773 // If there is no saved state at all, then there can not be 1774 // any nonConfig fragments either, so that is that. 1775 if (state == null) return; 1776 FragmentManagerState fms = (FragmentManagerState)state; 1777 if (fms.mActive == null) return; 1778 1779 // First re-attach any non-config instances we are retaining back 1780 // to their saved state, so we don't try to instantiate them again. 1781 if (nonConfig != null) { 1782 for (int i=0; i<nonConfig.size(); i++) { 1783 Fragment f = nonConfig.get(i); 1784 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 1785 FragmentState fs = fms.mActive[f.mIndex]; 1786 fs.mInstance = f; 1787 f.mSavedViewState = null; 1788 f.mBackStackNesting = 0; 1789 f.mInLayout = false; 1790 f.mAdded = false; 1791 f.mTarget = null; 1792 if (fs.mSavedFragmentState != null) { 1793 fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); 1794 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 1795 FragmentManagerImpl.VIEW_STATE_TAG); 1796 } 1797 } 1798 } 1799 1800 // Build the full list of active fragments, instantiating them from 1801 // their saved state. 1802 mActive = new ArrayList<Fragment>(fms.mActive.length); 1803 if (mAvailIndices != null) { 1804 mAvailIndices.clear(); 1805 } 1806 for (int i=0; i<fms.mActive.length; i++) { 1807 FragmentState fs = fms.mActive[i]; 1808 if (fs != null) { 1809 Fragment f = fs.instantiate(mActivity, mParent); 1810 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f); 1811 mActive.add(f); 1812 // Now that the fragment is instantiated (or came from being 1813 // retained above), clear mInstance in case we end up re-restoring 1814 // from this FragmentState again. 1815 fs.mInstance = null; 1816 } else { 1817 mActive.add(null); 1818 if (mAvailIndices == null) { 1819 mAvailIndices = new ArrayList<Integer>(); 1820 } 1821 if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i); 1822 mAvailIndices.add(i); 1823 } 1824 } 1825 1826 // Update the target of all retained fragments. 1827 if (nonConfig != null) { 1828 for (int i=0; i<nonConfig.size(); i++) { 1829 Fragment f = nonConfig.get(i); 1830 if (f.mTargetIndex >= 0) { 1831 if (f.mTargetIndex < mActive.size()) { 1832 f.mTarget = mActive.get(f.mTargetIndex); 1833 } else { 1834 Log.w(TAG, "Re-attaching retained fragment " + f 1835 + " target no longer exists: " + f.mTargetIndex); 1836 f.mTarget = null; 1837 } 1838 } 1839 } 1840 } 1841 1842 // Build the list of currently added fragments. 1843 if (fms.mAdded != null) { 1844 mAdded = new ArrayList<Fragment>(fms.mAdded.length); 1845 for (int i=0; i<fms.mAdded.length; i++) { 1846 Fragment f = mActive.get(fms.mAdded[i]); 1847 if (f == null) { 1848 throwException(new IllegalStateException( 1849 "No instantiated fragment for index #" + fms.mAdded[i])); 1850 } 1851 f.mAdded = true; 1852 if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f); 1853 if (mAdded.contains(f)) { 1854 throw new IllegalStateException("Already added!"); 1855 } 1856 mAdded.add(f); 1857 } 1858 } else { 1859 mAdded = null; 1860 } 1861 1862 // Build the back stack. 1863 if (fms.mBackStack != null) { 1864 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 1865 for (int i=0; i<fms.mBackStack.length; i++) { 1866 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 1867 if (DEBUG) { 1868 Log.v(TAG, "restoreAllState: back stack #" + i 1869 + " (index " + bse.mIndex + "): " + bse); 1870 LogWriter logw = new LogWriter(TAG); 1871 PrintWriter pw = new PrintWriter(logw); 1872 bse.dump(" ", pw, false); 1873 } 1874 mBackStack.add(bse); 1875 if (bse.mIndex >= 0) { 1876 setBackStackIndex(bse.mIndex, bse); 1877 } 1878 } 1879 } else { 1880 mBackStack = null; 1881 } 1882 } 1883 1884 public void attachActivity(FragmentActivity activity, 1885 FragmentContainer container, Fragment parent) { 1886 if (mActivity != null) throw new IllegalStateException("Already attached"); 1887 mActivity = activity; 1888 mContainer = container; 1889 mParent = parent; 1890 } 1891 1892 public void noteStateNotSaved() { 1893 mStateSaved = false; 1894 } 1895 1896 public void dispatchCreate() { 1897 mStateSaved = false; 1898 moveToState(Fragment.CREATED, false); 1899 } 1900 1901 public void dispatchActivityCreated() { 1902 mStateSaved = false; 1903 moveToState(Fragment.ACTIVITY_CREATED, false); 1904 } 1905 1906 public void dispatchStart() { 1907 mStateSaved = false; 1908 moveToState(Fragment.STARTED, false); 1909 } 1910 1911 public void dispatchResume() { 1912 mStateSaved = false; 1913 moveToState(Fragment.RESUMED, false); 1914 } 1915 1916 public void dispatchPause() { 1917 moveToState(Fragment.STARTED, false); 1918 } 1919 1920 public void dispatchStop() { 1921 // See saveAllState() for the explanation of this. We do this for 1922 // all platform versions, to keep our behavior more consistent between 1923 // them. 1924 mStateSaved = true; 1925 1926 moveToState(Fragment.STOPPED, false); 1927 } 1928 1929 public void dispatchReallyStop() { 1930 moveToState(Fragment.ACTIVITY_CREATED, false); 1931 } 1932 1933 public void dispatchDestroyView() { 1934 moveToState(Fragment.CREATED, false); 1935 } 1936 1937 public void dispatchDestroy() { 1938 mDestroyed = true; 1939 execPendingActions(); 1940 moveToState(Fragment.INITIALIZING, false); 1941 mActivity = null; 1942 mContainer = null; 1943 mParent = null; 1944 } 1945 1946 public void dispatchConfigurationChanged(Configuration newConfig) { 1947 if (mAdded != null) { 1948 for (int i=0; i<mAdded.size(); i++) { 1949 Fragment f = mAdded.get(i); 1950 if (f != null) { 1951 f.performConfigurationChanged(newConfig); 1952 } 1953 } 1954 } 1955 } 1956 1957 public void dispatchLowMemory() { 1958 if (mAdded != null) { 1959 for (int i=0; i<mAdded.size(); i++) { 1960 Fragment f = mAdded.get(i); 1961 if (f != null) { 1962 f.performLowMemory(); 1963 } 1964 } 1965 } 1966 } 1967 1968 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1969 boolean show = false; 1970 ArrayList<Fragment> newMenus = null; 1971 if (mAdded != null) { 1972 for (int i=0; i<mAdded.size(); i++) { 1973 Fragment f = mAdded.get(i); 1974 if (f != null) { 1975 if (f.performCreateOptionsMenu(menu, inflater)) { 1976 show = true; 1977 if (newMenus == null) { 1978 newMenus = new ArrayList<Fragment>(); 1979 } 1980 newMenus.add(f); 1981 } 1982 } 1983 } 1984 } 1985 1986 if (mCreatedMenus != null) { 1987 for (int i=0; i<mCreatedMenus.size(); i++) { 1988 Fragment f = mCreatedMenus.get(i); 1989 if (newMenus == null || !newMenus.contains(f)) { 1990 f.onDestroyOptionsMenu(); 1991 } 1992 } 1993 } 1994 1995 mCreatedMenus = newMenus; 1996 1997 return show; 1998 } 1999 2000 public boolean dispatchPrepareOptionsMenu(Menu menu) { 2001 boolean show = false; 2002 if (mAdded != null) { 2003 for (int i=0; i<mAdded.size(); i++) { 2004 Fragment f = mAdded.get(i); 2005 if (f != null) { 2006 if (f.performPrepareOptionsMenu(menu)) { 2007 show = true; 2008 } 2009 } 2010 } 2011 } 2012 return show; 2013 } 2014 2015 public boolean dispatchOptionsItemSelected(MenuItem item) { 2016 if (mAdded != null) { 2017 for (int i=0; i<mAdded.size(); i++) { 2018 Fragment f = mAdded.get(i); 2019 if (f != null) { 2020 if (f.performOptionsItemSelected(item)) { 2021 return true; 2022 } 2023 } 2024 } 2025 } 2026 return false; 2027 } 2028 2029 public boolean dispatchContextItemSelected(MenuItem item) { 2030 if (mAdded != null) { 2031 for (int i=0; i<mAdded.size(); i++) { 2032 Fragment f = mAdded.get(i); 2033 if (f != null) { 2034 if (f.performContextItemSelected(item)) { 2035 return true; 2036 } 2037 } 2038 } 2039 } 2040 return false; 2041 } 2042 2043 public void dispatchOptionsMenuClosed(Menu menu) { 2044 if (mAdded != null) { 2045 for (int i=0; i<mAdded.size(); i++) { 2046 Fragment f = mAdded.get(i); 2047 if (f != null) { 2048 f.performOptionsMenuClosed(menu); 2049 } 2050 } 2051 } 2052 } 2053 2054 public static int reverseTransit(int transit) { 2055 int rev = 0; 2056 switch (transit) { 2057 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 2058 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 2059 break; 2060 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 2061 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 2062 break; 2063 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 2064 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; 2065 break; 2066 } 2067 return rev; 2068 2069 } 2070 2071 public static final int ANIM_STYLE_OPEN_ENTER = 1; 2072 public static final int ANIM_STYLE_OPEN_EXIT = 2; 2073 public static final int ANIM_STYLE_CLOSE_ENTER = 3; 2074 public static final int ANIM_STYLE_CLOSE_EXIT = 4; 2075 public static final int ANIM_STYLE_FADE_ENTER = 5; 2076 public static final int ANIM_STYLE_FADE_EXIT = 6; 2077 2078 public static int transitToStyleIndex(int transit, boolean enter) { 2079 int animAttr = -1; 2080 switch (transit) { 2081 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 2082 animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT; 2083 break; 2084 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 2085 animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT; 2086 break; 2087 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 2088 animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT; 2089 break; 2090 } 2091 return animAttr; 2092 } 2093} 2094