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