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.Debug;
     26 import android.os.Handler;
     27 import android.os.Looper;
     28 import android.os.Parcel;
     29 import android.os.Parcelable;
     30 import android.util.DebugUtils;
     31 import android.util.Log;
     32 import android.util.LogWriter;
     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      * Returns true if the final {@link Activity#onDestroy() Activity.onDestroy()}
    322      * call has been made on the FragmentManager's Activity, so this instance is now dead.
    323      */
    324     public abstract boolean isDestroyed();
    325 
    326     /**
    327      * Print the FragmentManager's state into the given stream.
    328      *
    329      * @param prefix Text to print at the front of each line.
    330      * @param fd The raw file descriptor that the dump is being sent to.
    331      * @param writer A PrintWriter to which the dump is to be set.
    332      * @param args Additional arguments to the dump request.
    333      */
    334     public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
    335 
    336     /**
    337      * Control whether the framework's internal fragment manager debugging
    338      * logs are turned on.  If enabled, you will see output in logcat as
    339      * the framework performs fragment operations.
    340      */
    341     public static void enableDebugLogging(boolean enabled) {
    342         FragmentManagerImpl.DEBUG = enabled;
    343     }
    344 
    345     /**
    346      * Invalidate the attached activity's options menu as necessary.
    347      * This may end up being deferred until we move to the resumed state.
    348      */
    349     public void invalidateOptionsMenu() { }
    350 }
    351 
    352 final class FragmentManagerState implements Parcelable {
    353     FragmentState[] mActive;
    354     int[] mAdded;
    355     BackStackState[] mBackStack;
    356 
    357     public FragmentManagerState() {
    358     }
    359 
    360     public FragmentManagerState(Parcel in) {
    361         mActive = in.createTypedArray(FragmentState.CREATOR);
    362         mAdded = in.createIntArray();
    363         mBackStack = in.createTypedArray(BackStackState.CREATOR);
    364     }
    365 
    366     public int describeContents() {
    367         return 0;
    368     }
    369 
    370     public void writeToParcel(Parcel dest, int flags) {
    371         dest.writeTypedArray(mActive, flags);
    372         dest.writeIntArray(mAdded);
    373         dest.writeTypedArray(mBackStack, flags);
    374     }
    375 
    376     public static final Parcelable.Creator<FragmentManagerState> CREATOR
    377             = new Parcelable.Creator<FragmentManagerState>() {
    378         public FragmentManagerState createFromParcel(Parcel in) {
    379             return new FragmentManagerState(in);
    380         }
    381 
    382         public FragmentManagerState[] newArray(int size) {
    383             return new FragmentManagerState[size];
    384         }
    385     };
    386 }
    387 
    388 /**
    389  * Callbacks from FragmentManagerImpl to its container.
    390  */
    391 interface FragmentContainer {
    392     public View findViewById(int id);
    393 }
    394 
    395 /**
    396  * Container for fragments associated with an activity.
    397  */
    398 final class FragmentManagerImpl extends FragmentManager {
    399     static boolean DEBUG = false;
    400     static final String TAG = "FragmentManager";
    401 
    402     static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
    403     static final String TARGET_STATE_TAG = "android:target_state";
    404     static final String VIEW_STATE_TAG = "android:view_state";
    405     static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
    406 
    407     ArrayList<Runnable> mPendingActions;
    408     Runnable[] mTmpActions;
    409     boolean mExecutingActions;
    410 
    411     ArrayList<Fragment> mActive;
    412     ArrayList<Fragment> mAdded;
    413     ArrayList<Integer> mAvailIndices;
    414     ArrayList<BackStackRecord> mBackStack;
    415     ArrayList<Fragment> mCreatedMenus;
    416 
    417     // Must be accessed while locked.
    418     ArrayList<BackStackRecord> mBackStackIndices;
    419     ArrayList<Integer> mAvailBackStackIndices;
    420 
    421     ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
    422 
    423     int mCurState = Fragment.INITIALIZING;
    424     Activity mActivity;
    425     FragmentContainer mContainer;
    426     Fragment mParent;
    427 
    428     boolean mNeedMenuInvalidate;
    429     boolean mStateSaved;
    430     boolean mDestroyed;
    431     String mNoTransactionsBecause;
    432     boolean mHavePendingDeferredStart;
    433 
    434     // Temporary vars for state save and restore.
    435     Bundle mStateBundle = null;
    436     SparseArray<Parcelable> mStateArray = null;
    437 
    438     Runnable mExecCommit = new Runnable() {
    439         @Override
    440         public void run() {
    441             execPendingActions();
    442         }
    443     };
    444 
    445     private void throwException(RuntimeException ex) {
    446         Log.e(TAG, ex.getMessage());
    447         LogWriter logw = new LogWriter(Log.ERROR, TAG);
    448         PrintWriter pw = new PrintWriter(logw);
    449         if (mActivity != null) {
    450             Log.e(TAG, "Activity state:");
    451             try {
    452                 mActivity.dump("  ", null, pw, new String[] { });
    453             } catch (Exception e) {
    454                 Log.e(TAG, "Failed dumping state", e);
    455             }
    456         } else {
    457             Log.e(TAG, "Fragment manager state:");
    458             try {
    459                 dump("  ", null, pw, new String[] { });
    460             } catch (Exception e) {
    461                 Log.e(TAG, "Failed dumping state", e);
    462             }
    463         }
    464         throw ex;
    465     }
    466 
    467     @Override
    468     public FragmentTransaction beginTransaction() {
    469         return new BackStackRecord(this);
    470     }
    471 
    472     @Override
    473     public boolean executePendingTransactions() {
    474         return execPendingActions();
    475     }
    476 
    477     @Override
    478     public void popBackStack() {
    479         enqueueAction(new Runnable() {
    480             @Override public void run() {
    481                 popBackStackState(mActivity.mHandler, null, -1, 0);
    482             }
    483         }, false);
    484     }
    485 
    486     @Override
    487     public boolean popBackStackImmediate() {
    488         checkStateLoss();
    489         executePendingTransactions();
    490         return popBackStackState(mActivity.mHandler, null, -1, 0);
    491     }
    492 
    493     @Override
    494     public void popBackStack(final String name, final int flags) {
    495         enqueueAction(new Runnable() {
    496             @Override public void run() {
    497                 popBackStackState(mActivity.mHandler, name, -1, flags);
    498             }
    499         }, false);
    500     }
    501 
    502     @Override
    503     public boolean popBackStackImmediate(String name, int flags) {
    504         checkStateLoss();
    505         executePendingTransactions();
    506         return popBackStackState(mActivity.mHandler, name, -1, flags);
    507     }
    508 
    509     @Override
    510     public void popBackStack(final int id, final int flags) {
    511         if (id < 0) {
    512             throw new IllegalArgumentException("Bad id: " + id);
    513         }
    514         enqueueAction(new Runnable() {
    515             @Override public void run() {
    516                 popBackStackState(mActivity.mHandler, null, id, flags);
    517             }
    518         }, false);
    519     }
    520 
    521     @Override
    522     public boolean popBackStackImmediate(int id, int flags) {
    523         checkStateLoss();
    524         executePendingTransactions();
    525         if (id < 0) {
    526             throw new IllegalArgumentException("Bad id: " + id);
    527         }
    528         return popBackStackState(mActivity.mHandler, null, id, flags);
    529     }
    530 
    531     @Override
    532     public int getBackStackEntryCount() {
    533         return mBackStack != null ? mBackStack.size() : 0;
    534     }
    535 
    536     @Override
    537     public BackStackEntry getBackStackEntryAt(int index) {
    538         return mBackStack.get(index);
    539     }
    540 
    541     @Override
    542     public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
    543         if (mBackStackChangeListeners == null) {
    544             mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
    545         }
    546         mBackStackChangeListeners.add(listener);
    547     }
    548 
    549     @Override
    550     public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
    551         if (mBackStackChangeListeners != null) {
    552             mBackStackChangeListeners.remove(listener);
    553         }
    554     }
    555 
    556     @Override
    557     public void putFragment(Bundle bundle, String key, Fragment fragment) {
    558         if (fragment.mIndex < 0) {
    559             throwException(new IllegalStateException("Fragment " + fragment
    560                     + " is not currently in the FragmentManager"));
    561         }
    562         bundle.putInt(key, fragment.mIndex);
    563     }
    564 
    565     @Override
    566     public Fragment getFragment(Bundle bundle, String key) {
    567         int index = bundle.getInt(key, -1);
    568         if (index == -1) {
    569             return null;
    570         }
    571         if (index >= mActive.size()) {
    572             throwException(new IllegalStateException("Fragement no longer exists for key "
    573                     + key + ": index " + index));
    574         }
    575         Fragment f = mActive.get(index);
    576         if (f == null) {
    577             throwException(new IllegalStateException("Fragement no longer exists for key "
    578                     + key + ": index " + index));
    579         }
    580         return f;
    581     }
    582 
    583     @Override
    584     public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) {
    585         if (fragment.mIndex < 0) {
    586             throwException(new IllegalStateException("Fragment " + fragment
    587                     + " is not currently in the FragmentManager"));
    588         }
    589         if (fragment.mState > Fragment.INITIALIZING) {
    590             Bundle result = saveFragmentBasicState(fragment);
    591             return result != null ? new Fragment.SavedState(result) : null;
    592         }
    593         return null;
    594     }
    595 
    596     @Override
    597     public boolean isDestroyed() {
    598         return mDestroyed;
    599     }
    600 
    601     @Override
    602     public String toString() {
    603         StringBuilder sb = new StringBuilder(128);
    604         sb.append("FragmentManager{");
    605         sb.append(Integer.toHexString(System.identityHashCode(this)));
    606         sb.append(" in ");
    607         if (mParent != null) {
    608             DebugUtils.buildShortClassTag(mParent, sb);
    609         } else {
    610             DebugUtils.buildShortClassTag(mActivity, sb);
    611         }
    612         sb.append("}}");
    613         return sb.toString();
    614     }
    615 
    616     @Override
    617     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
    618         String innerPrefix = prefix + "    ";
    619 
    620         int N;
    621         if (mActive != null) {
    622             N = mActive.size();
    623             if (N > 0) {
    624                 writer.print(prefix); writer.print("Active Fragments in ");
    625                         writer.print(Integer.toHexString(System.identityHashCode(this)));
    626                         writer.println(":");
    627                 for (int i=0; i<N; i++) {
    628                     Fragment f = mActive.get(i);
    629                     writer.print(prefix); writer.print("  #"); writer.print(i);
    630                             writer.print(": "); writer.println(f);
    631                     if (f != null) {
    632                         f.dump(innerPrefix, fd, writer, args);
    633                     }
    634                 }
    635             }
    636         }
    637 
    638         if (mAdded != null) {
    639             N = mAdded.size();
    640             if (N > 0) {
    641                 writer.print(prefix); writer.println("Added Fragments:");
    642                 for (int i=0; i<N; i++) {
    643                     Fragment f = mAdded.get(i);
    644                     writer.print(prefix); writer.print("  #"); writer.print(i);
    645                             writer.print(": "); writer.println(f.toString());
    646                 }
    647             }
    648         }
    649 
    650         if (mCreatedMenus != null) {
    651             N = mCreatedMenus.size();
    652             if (N > 0) {
    653                 writer.print(prefix); writer.println("Fragments Created Menus:");
    654                 for (int i=0; i<N; i++) {
    655                     Fragment f = mCreatedMenus.get(i);
    656                     writer.print(prefix); writer.print("  #"); writer.print(i);
    657                             writer.print(": "); writer.println(f.toString());
    658                 }
    659             }
    660         }
    661 
    662         if (mBackStack != null) {
    663             N = mBackStack.size();
    664             if (N > 0) {
    665                 writer.print(prefix); writer.println("Back Stack:");
    666                 for (int i=0; i<N; i++) {
    667                     BackStackRecord bs = mBackStack.get(i);
    668                     writer.print(prefix); writer.print("  #"); writer.print(i);
    669                             writer.print(": "); writer.println(bs.toString());
    670                     bs.dump(innerPrefix, fd, writer, args);
    671                 }
    672             }
    673         }
    674 
    675         synchronized (this) {
    676             if (mBackStackIndices != null) {
    677                 N = mBackStackIndices.size();
    678                 if (N > 0) {
    679                     writer.print(prefix); writer.println("Back Stack Indices:");
    680                     for (int i=0; i<N; i++) {
    681                         BackStackRecord bs = mBackStackIndices.get(i);
    682                         writer.print(prefix); writer.print("  #"); writer.print(i);
    683                                 writer.print(": "); writer.println(bs);
    684                     }
    685                 }
    686             }
    687 
    688             if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) {
    689                 writer.print(prefix); writer.print("mAvailBackStackIndices: ");
    690                         writer.println(Arrays.toString(mAvailBackStackIndices.toArray()));
    691             }
    692         }
    693 
    694         if (mPendingActions != null) {
    695             N = mPendingActions.size();
    696             if (N > 0) {
    697                 writer.print(prefix); writer.println("Pending Actions:");
    698                 for (int i=0; i<N; i++) {
    699                     Runnable r = mPendingActions.get(i);
    700                     writer.print(prefix); writer.print("  #"); writer.print(i);
    701                             writer.print(": "); writer.println(r);
    702                 }
    703             }
    704         }
    705 
    706         writer.print(prefix); writer.println("FragmentManager misc state:");
    707         writer.print(prefix); writer.print("  mActivity="); writer.println(mActivity);
    708         writer.print(prefix); writer.print("  mContainer="); writer.println(mContainer);
    709         if (mParent != null) {
    710             writer.print(prefix); writer.print("  mParent="); writer.println(mParent);
    711         }
    712         writer.print(prefix); writer.print("  mCurState="); writer.print(mCurState);
    713                 writer.print(" mStateSaved="); writer.print(mStateSaved);
    714                 writer.print(" mDestroyed="); writer.println(mDestroyed);
    715         if (mNeedMenuInvalidate) {
    716             writer.print(prefix); writer.print("  mNeedMenuInvalidate=");
    717                     writer.println(mNeedMenuInvalidate);
    718         }
    719         if (mNoTransactionsBecause != null) {
    720             writer.print(prefix); writer.print("  mNoTransactionsBecause=");
    721                     writer.println(mNoTransactionsBecause);
    722         }
    723         if (mAvailIndices != null && mAvailIndices.size() > 0) {
    724             writer.print(prefix); writer.print("  mAvailIndices: ");
    725                     writer.println(Arrays.toString(mAvailIndices.toArray()));
    726         }
    727     }
    728 
    729     Animator loadAnimator(Fragment fragment, int transit, boolean enter,
    730             int transitionStyle) {
    731         Animator animObj = fragment.onCreateAnimator(transit, enter,
    732                 fragment.mNextAnim);
    733         if (animObj != null) {
    734             return animObj;
    735         }
    736 
    737         if (fragment.mNextAnim != 0) {
    738             Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim);
    739             if (anim != null) {
    740                 return anim;
    741             }
    742         }
    743 
    744         if (transit == 0) {
    745             return null;
    746         }
    747 
    748         int styleIndex = transitToStyleIndex(transit, enter);
    749         if (styleIndex < 0) {
    750             return null;
    751         }
    752 
    753         if (transitionStyle == 0 && mActivity.getWindow() != null) {
    754             transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
    755         }
    756         if (transitionStyle == 0) {
    757             return null;
    758         }
    759 
    760         TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
    761                 com.android.internal.R.styleable.FragmentAnimation);
    762         int anim = attrs.getResourceId(styleIndex, 0);
    763         attrs.recycle();
    764 
    765         if (anim == 0) {
    766             return null;
    767         }
    768 
    769         return AnimatorInflater.loadAnimator(mActivity, anim);
    770     }
    771 
    772     public void performPendingDeferredStart(Fragment f) {
    773         if (f.mDeferStart) {
    774             if (mExecutingActions) {
    775                 // Wait until we're done executing our pending transactions
    776                 mHavePendingDeferredStart = true;
    777                 return;
    778             }
    779             f.mDeferStart = false;
    780             moveToState(f, mCurState, 0, 0, false);
    781         }
    782     }
    783 
    784     void moveToState(Fragment f, int newState, int transit, int transitionStyle,
    785             boolean keepActive) {
    786         if (DEBUG && false) Log.v(TAG, "moveToState: " + f
    787             + " oldState=" + f.mState + " newState=" + newState
    788             + " mRemoving=" + f.mRemoving + " Callers=" + Debug.getCallers(5));
    789 
    790         // Fragments that are not currently added will sit in the onCreate() state.
    791         if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
    792             newState = Fragment.CREATED;
    793         }
    794         if (f.mRemoving && newState > f.mState) {
    795             // While removing a fragment, we can't change it to a higher state.
    796             newState = f.mState;
    797         }
    798         // Defer start if requested; don't allow it to move to STARTED or higher
    799         // if it's not already started.
    800         if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
    801             newState = Fragment.STOPPED;
    802         }
    803         if (f.mState < newState) {
    804             // For fragments that are created from a layout, when restoring from
    805             // state we don't want to allow them to be created until they are
    806             // being reloaded from the layout.
    807             if (f.mFromLayout && !f.mInLayout) {
    808                 return;
    809             }
    810             if (f.mAnimatingAway != null) {
    811                 // The fragment is currently being animated...  but!  Now we
    812                 // want to move our state back up.  Give up on waiting for the
    813                 // animation, move to whatever the final state should be once
    814                 // the animation is done, and then we can proceed from there.
    815                 f.mAnimatingAway = null;
    816                 moveToState(f, f.mStateAfterAnimating, 0, 0, true);
    817             }
    818             switch (f.mState) {
    819                 case Fragment.INITIALIZING:
    820                     if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
    821                     if (f.mSavedFragmentState != null) {
    822                         f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
    823                                 FragmentManagerImpl.VIEW_STATE_TAG);
    824                         f.mTarget = getFragment(f.mSavedFragmentState,
    825                                 FragmentManagerImpl.TARGET_STATE_TAG);
    826                         if (f.mTarget != null) {
    827                             f.mTargetRequestCode = f.mSavedFragmentState.getInt(
    828                                     FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
    829                         }
    830                         f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
    831                                 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
    832                         if (!f.mUserVisibleHint) {
    833                             f.mDeferStart = true;
    834                             if (newState > Fragment.STOPPED) {
    835                                 newState = Fragment.STOPPED;
    836                             }
    837                         }
    838                     }
    839                     f.mActivity = mActivity;
    840                     f.mParentFragment = mParent;
    841                     f.mFragmentManager = mParent != null
    842                             ? mParent.mChildFragmentManager : mActivity.mFragments;
    843                     f.mCalled = false;
    844                     f.onAttach(mActivity);
    845                     if (!f.mCalled) {
    846                         throw new SuperNotCalledException("Fragment " + f
    847                                 + " did not call through to super.onAttach()");
    848                     }
    849                     if (f.mParentFragment == null) {
    850                         mActivity.onAttachFragment(f);
    851                     }
    852 
    853                     if (!f.mRetaining) {
    854                         f.performCreate(f.mSavedFragmentState);
    855                     }
    856                     f.mRetaining = false;
    857                     if (f.mFromLayout) {
    858                         // For fragments that are part of the content view
    859                         // layout, we need to instantiate the view immediately
    860                         // and the inflater will take care of adding it.
    861                         f.mView = f.performCreateView(f.getLayoutInflater(
    862                                 f.mSavedFragmentState), null, f.mSavedFragmentState);
    863                         if (f.mView != null) {
    864                             f.mView.setSaveFromParentEnabled(false);
    865                             if (f.mHidden) f.mView.setVisibility(View.GONE);
    866                             f.onViewCreated(f.mView, f.mSavedFragmentState);
    867                         }
    868                     }
    869                 case Fragment.CREATED:
    870                     if (newState > Fragment.CREATED) {
    871                         if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
    872                         if (!f.mFromLayout) {
    873                             ViewGroup container = null;
    874                             if (f.mContainerId != 0) {
    875                                 container = (ViewGroup)mContainer.findViewById(f.mContainerId);
    876                                 if (container == null && !f.mRestored) {
    877                                     throwException(new IllegalArgumentException(
    878                                             "No view found for id 0x"
    879                                             + Integer.toHexString(f.mContainerId) + " ("
    880                                             + f.getResources().getResourceName(f.mContainerId)
    881                                             + ") for fragment " + f));
    882                                 }
    883                             }
    884                             f.mContainer = container;
    885                             f.mView = f.performCreateView(f.getLayoutInflater(
    886                                     f.mSavedFragmentState), container, f.mSavedFragmentState);
    887                             if (f.mView != null) {
    888                                 f.mView.setSaveFromParentEnabled(false);
    889                                 if (container != null) {
    890                                     Animator anim = loadAnimator(f, transit, true,
    891                                             transitionStyle);
    892                                     if (anim != null) {
    893                                         anim.setTarget(f.mView);
    894                                         anim.start();
    895                                     }
    896                                     container.addView(f.mView);
    897                                 }
    898                                 if (f.mHidden) f.mView.setVisibility(View.GONE);
    899                                 f.onViewCreated(f.mView, f.mSavedFragmentState);
    900                             }
    901                         }
    902 
    903                         f.performActivityCreated(f.mSavedFragmentState);
    904                         if (f.mView != null) {
    905                             f.restoreViewState(f.mSavedFragmentState);
    906                         }
    907                         f.mSavedFragmentState = null;
    908                     }
    909                 case Fragment.ACTIVITY_CREATED:
    910                 case Fragment.STOPPED:
    911                     if (newState > Fragment.STOPPED) {
    912                         if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
    913                         f.performStart();
    914                     }
    915                 case Fragment.STARTED:
    916                     if (newState > Fragment.STARTED) {
    917                         if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
    918                         f.mResumed = true;
    919                         f.performResume();
    920                         // Get rid of this in case we saved it and never needed it.
    921                         f.mSavedFragmentState = null;
    922                         f.mSavedViewState = null;
    923                     }
    924             }
    925         } else if (f.mState > newState) {
    926             switch (f.mState) {
    927                 case Fragment.RESUMED:
    928                     if (newState < Fragment.RESUMED) {
    929                         if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
    930                         f.performPause();
    931                         f.mResumed = false;
    932                     }
    933                 case Fragment.STARTED:
    934                     if (newState < Fragment.STARTED) {
    935                         if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
    936                         f.performStop();
    937                     }
    938                 case Fragment.STOPPED:
    939                 case Fragment.ACTIVITY_CREATED:
    940                     if (newState < Fragment.ACTIVITY_CREATED) {
    941                         if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
    942                         if (f.mView != null) {
    943                             // Need to save the current view state if not
    944                             // done already.
    945                             if (!mActivity.isFinishing() && f.mSavedViewState == null) {
    946                                 saveFragmentViewState(f);
    947                             }
    948                         }
    949                         f.performDestroyView();
    950                         if (f.mView != null && f.mContainer != null) {
    951                             Animator anim = null;
    952                             if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
    953                                 anim = loadAnimator(f, transit, false,
    954                                         transitionStyle);
    955                             }
    956                             if (anim != null) {
    957                                 final ViewGroup container = f.mContainer;
    958                                 final View view = f.mView;
    959                                 final Fragment fragment = f;
    960                                 container.startViewTransition(view);
    961                                 f.mAnimatingAway = anim;
    962                                 f.mStateAfterAnimating = newState;
    963                                 anim.addListener(new AnimatorListenerAdapter() {
    964                                     @Override
    965                                     public void onAnimationEnd(Animator anim) {
    966                                         container.endViewTransition(view);
    967                                         if (fragment.mAnimatingAway != null) {
    968                                             fragment.mAnimatingAway = null;
    969                                             moveToState(fragment, fragment.mStateAfterAnimating,
    970                                                     0, 0, false);
    971                                         }
    972                                     }
    973                                 });
    974                                 anim.setTarget(f.mView);
    975                                 anim.start();
    976 
    977                             }
    978                             f.mContainer.removeView(f.mView);
    979                         }
    980                         f.mContainer = null;
    981                         f.mView = null;
    982                     }
    983                 case Fragment.CREATED:
    984                     if (newState < Fragment.CREATED) {
    985                         if (mDestroyed) {
    986                             if (f.mAnimatingAway != null) {
    987                                 // The fragment's containing activity is
    988                                 // being destroyed, but this fragment is
    989                                 // currently animating away.  Stop the
    990                                 // animation right now -- it is not needed,
    991                                 // and we can't wait any more on destroying
    992                                 // the fragment.
    993                                 Animator anim = f.mAnimatingAway;
    994                                 f.mAnimatingAway = null;
    995                                 anim.cancel();
    996                             }
    997                         }
    998                         if (f.mAnimatingAway != null) {
    999                             // We are waiting for the fragment's view to finish
   1000                             // animating away.  Just make a note of the state
   1001                             // the fragment now should move to once the animation
   1002                             // is done.
   1003                             f.mStateAfterAnimating = newState;
   1004                             newState = Fragment.CREATED;
   1005                         } else {
   1006                             if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
   1007                             if (!f.mRetaining) {
   1008                                 f.performDestroy();
   1009                             }
   1010 
   1011                             f.mCalled = false;
   1012                             f.onDetach();
   1013                             if (!f.mCalled) {
   1014                                 throw new SuperNotCalledException("Fragment " + f
   1015                                         + " did not call through to super.onDetach()");
   1016                             }
   1017                             if (!keepActive) {
   1018                                 if (!f.mRetaining) {
   1019                                     makeInactive(f);
   1020                                 } else {
   1021                                     f.mActivity = null;
   1022                                     f.mParentFragment = null;
   1023                                     f.mFragmentManager = null;
   1024                                 }
   1025                             }
   1026                         }
   1027                     }
   1028             }
   1029         }
   1030 
   1031         f.mState = newState;
   1032     }
   1033 
   1034     void moveToState(Fragment f) {
   1035         moveToState(f, mCurState, 0, 0, false);
   1036     }
   1037 
   1038     void moveToState(int newState, boolean always) {
   1039         moveToState(newState, 0, 0, always);
   1040     }
   1041 
   1042     void moveToState(int newState, int transit, int transitStyle, boolean always) {
   1043         if (mActivity == null && newState != Fragment.INITIALIZING) {
   1044             throw new IllegalStateException("No activity");
   1045         }
   1046 
   1047         if (!always && mCurState == newState) {
   1048             return;
   1049         }
   1050 
   1051         mCurState = newState;
   1052         if (mActive != null) {
   1053             boolean loadersRunning = false;
   1054             for (int i=0; i<mActive.size(); i++) {
   1055                 Fragment f = mActive.get(i);
   1056                 if (f != null) {
   1057                     moveToState(f, newState, transit, transitStyle, false);
   1058                     if (f.mLoaderManager != null) {
   1059                         loadersRunning |= f.mLoaderManager.hasRunningLoaders();
   1060                     }
   1061                 }
   1062             }
   1063 
   1064             if (!loadersRunning) {
   1065                 startPendingDeferredFragments();
   1066             }
   1067 
   1068             if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {
   1069                 mActivity.invalidateOptionsMenu();
   1070                 mNeedMenuInvalidate = false;
   1071             }
   1072         }
   1073     }
   1074 
   1075     void startPendingDeferredFragments() {
   1076         if (mActive == null) return;
   1077 
   1078         for (int i=0; i<mActive.size(); i++) {
   1079             Fragment f = mActive.get(i);
   1080             if (f != null) {
   1081                 performPendingDeferredStart(f);
   1082             }
   1083         }
   1084     }
   1085 
   1086     void makeActive(Fragment f) {
   1087         if (f.mIndex >= 0) {
   1088             return;
   1089         }
   1090 
   1091         if (mAvailIndices == null || mAvailIndices.size() <= 0) {
   1092             if (mActive == null) {
   1093                 mActive = new ArrayList<Fragment>();
   1094             }
   1095             f.setIndex(mActive.size(), mParent);
   1096             mActive.add(f);
   1097 
   1098         } else {
   1099             f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
   1100             mActive.set(f.mIndex, f);
   1101         }
   1102         if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
   1103     }
   1104 
   1105     void makeInactive(Fragment f) {
   1106         if (f.mIndex < 0) {
   1107             return;
   1108         }
   1109 
   1110         if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
   1111         mActive.set(f.mIndex, null);
   1112         if (mAvailIndices == null) {
   1113             mAvailIndices = new ArrayList<Integer>();
   1114         }
   1115         mAvailIndices.add(f.mIndex);
   1116         mActivity.invalidateFragment(f.mWho);
   1117         f.initState();
   1118     }
   1119 
   1120     public void addFragment(Fragment fragment, boolean moveToStateNow) {
   1121         if (mAdded == null) {
   1122             mAdded = new ArrayList<Fragment>();
   1123         }
   1124         if (DEBUG) Log.v(TAG, "add: " + fragment);
   1125         makeActive(fragment);
   1126         if (!fragment.mDetached) {
   1127             if (mAdded.contains(fragment)) {
   1128                 throw new IllegalStateException("Fragment already added: " + fragment);
   1129             }
   1130             mAdded.add(fragment);
   1131             fragment.mAdded = true;
   1132             fragment.mRemoving = false;
   1133             if (fragment.mHasMenu && fragment.mMenuVisible) {
   1134                 mNeedMenuInvalidate = true;
   1135             }
   1136             if (moveToStateNow) {
   1137                 moveToState(fragment);
   1138             }
   1139         }
   1140     }
   1141 
   1142     public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
   1143         if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
   1144         final boolean inactive = !fragment.isInBackStack();
   1145         if (!fragment.mDetached || inactive) {
   1146             if (false) {
   1147                 // Would be nice to catch a bad remove here, but we need
   1148                 // time to test this to make sure we aren't crashes cases
   1149                 // where it is not a problem.
   1150                 if (!mAdded.contains(fragment)) {
   1151                     throw new IllegalStateException("Fragment not added: " + fragment);
   1152                 }
   1153             }
   1154             if (mAdded != null) {
   1155                 mAdded.remove(fragment);
   1156             }
   1157             if (fragment.mHasMenu && fragment.mMenuVisible) {
   1158                 mNeedMenuInvalidate = true;
   1159             }
   1160             fragment.mAdded = false;
   1161             fragment.mRemoving = true;
   1162             moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
   1163                     transition, transitionStyle, false);
   1164         }
   1165     }
   1166 
   1167     public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
   1168         if (DEBUG) Log.v(TAG, "hide: " + fragment);
   1169         if (!fragment.mHidden) {
   1170             fragment.mHidden = true;
   1171             if (fragment.mView != null) {
   1172                 Animator anim = loadAnimator(fragment, transition, true,
   1173                         transitionStyle);
   1174                 if (anim != null) {
   1175                     anim.setTarget(fragment.mView);
   1176                     // Delay the actual hide operation until the animation finishes, otherwise
   1177                     // the fragment will just immediately disappear
   1178                     final Fragment finalFragment = fragment;
   1179                     anim.addListener(new AnimatorListenerAdapter() {
   1180                         @Override
   1181                         public void onAnimationEnd(Animator animation) {
   1182                             if (finalFragment.mView != null) {
   1183                                 finalFragment.mView.setVisibility(View.GONE);
   1184                             }
   1185                         }
   1186                     });
   1187                     anim.start();
   1188                 } else {
   1189                     fragment.mView.setVisibility(View.GONE);
   1190                 }
   1191             }
   1192             if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
   1193                 mNeedMenuInvalidate = true;
   1194             }
   1195             fragment.onHiddenChanged(true);
   1196         }
   1197     }
   1198 
   1199     public void showFragment(Fragment fragment, int transition, int transitionStyle) {
   1200         if (DEBUG) Log.v(TAG, "show: " + fragment);
   1201         if (fragment.mHidden) {
   1202             fragment.mHidden = false;
   1203             if (fragment.mView != null) {
   1204                 Animator anim = loadAnimator(fragment, transition, true,
   1205                         transitionStyle);
   1206                 if (anim != null) {
   1207                     anim.setTarget(fragment.mView);
   1208                     anim.start();
   1209                 }
   1210                 fragment.mView.setVisibility(View.VISIBLE);
   1211             }
   1212             if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
   1213                 mNeedMenuInvalidate = true;
   1214             }
   1215             fragment.onHiddenChanged(false);
   1216         }
   1217     }
   1218 
   1219     public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
   1220         if (DEBUG) Log.v(TAG, "detach: " + fragment);
   1221         if (!fragment.mDetached) {
   1222             fragment.mDetached = true;
   1223             if (fragment.mAdded) {
   1224                 // We are not already in back stack, so need to remove the fragment.
   1225                 if (mAdded != null) {
   1226                     if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
   1227                     mAdded.remove(fragment);
   1228                 }
   1229                 if (fragment.mHasMenu && fragment.mMenuVisible) {
   1230                     mNeedMenuInvalidate = true;
   1231                 }
   1232                 fragment.mAdded = false;
   1233                 moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
   1234             }
   1235         }
   1236     }
   1237 
   1238     public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
   1239         if (DEBUG) Log.v(TAG, "attach: " + fragment);
   1240         if (fragment.mDetached) {
   1241             fragment.mDetached = false;
   1242             if (!fragment.mAdded) {
   1243                 if (mAdded == null) {
   1244                     mAdded = new ArrayList<Fragment>();
   1245                 }
   1246                 if (mAdded.contains(fragment)) {
   1247                     throw new IllegalStateException("Fragment already added: " + fragment);
   1248                 }
   1249                 if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
   1250                 mAdded.add(fragment);
   1251                 fragment.mAdded = true;
   1252                 if (fragment.mHasMenu && fragment.mMenuVisible) {
   1253                     mNeedMenuInvalidate = true;
   1254                 }
   1255                 moveToState(fragment, mCurState, transition, transitionStyle, false);
   1256             }
   1257         }
   1258     }
   1259 
   1260     public Fragment findFragmentById(int id) {
   1261         if (mAdded != null) {
   1262             // First look through added fragments.
   1263             for (int i=mAdded.size()-1; i>=0; i--) {
   1264                 Fragment f = mAdded.get(i);
   1265                 if (f != null && f.mFragmentId == id) {
   1266                     return f;
   1267                 }
   1268             }
   1269         }
   1270         if (mActive != null) {
   1271             // Now for any known fragment.
   1272             for (int i=mActive.size()-1; i>=0; i--) {
   1273                 Fragment f = mActive.get(i);
   1274                 if (f != null && f.mFragmentId == id) {
   1275                     return f;
   1276                 }
   1277             }
   1278         }
   1279         return null;
   1280     }
   1281 
   1282     public Fragment findFragmentByTag(String tag) {
   1283         if (mAdded != null && tag != null) {
   1284             // First look through added fragments.
   1285             for (int i=mAdded.size()-1; i>=0; i--) {
   1286                 Fragment f = mAdded.get(i);
   1287                 if (f != null && tag.equals(f.mTag)) {
   1288                     return f;
   1289                 }
   1290             }
   1291         }
   1292         if (mActive != null && tag != null) {
   1293             // Now for any known fragment.
   1294             for (int i=mActive.size()-1; i>=0; i--) {
   1295                 Fragment f = mActive.get(i);
   1296                 if (f != null && tag.equals(f.mTag)) {
   1297                     return f;
   1298                 }
   1299             }
   1300         }
   1301         return null;
   1302     }
   1303 
   1304     public Fragment findFragmentByWho(String who) {
   1305         if (mActive != null && who != null) {
   1306             for (int i=mActive.size()-1; i>=0; i--) {
   1307                 Fragment f = mActive.get(i);
   1308                 if (f != null && (f=f.findFragmentByWho(who)) != null) {
   1309                     return f;
   1310                 }
   1311             }
   1312         }
   1313         return null;
   1314     }
   1315 
   1316     private void checkStateLoss() {
   1317         if (mStateSaved) {
   1318             throw new IllegalStateException(
   1319                     "Can not perform this action after onSaveInstanceState");
   1320         }
   1321         if (mNoTransactionsBecause != null) {
   1322             throw new IllegalStateException(
   1323                     "Can not perform this action inside of " + mNoTransactionsBecause);
   1324         }
   1325     }
   1326 
   1327     public void enqueueAction(Runnable action, boolean allowStateLoss) {
   1328         if (!allowStateLoss) {
   1329             checkStateLoss();
   1330         }
   1331         synchronized (this) {
   1332             if (mActivity == null) {
   1333                 throw new IllegalStateException("Activity has been destroyed");
   1334             }
   1335             if (mPendingActions == null) {
   1336                 mPendingActions = new ArrayList<Runnable>();
   1337             }
   1338             mPendingActions.add(action);
   1339             if (mPendingActions.size() == 1) {
   1340                 mActivity.mHandler.removeCallbacks(mExecCommit);
   1341                 mActivity.mHandler.post(mExecCommit);
   1342             }
   1343         }
   1344     }
   1345 
   1346     public int allocBackStackIndex(BackStackRecord bse) {
   1347         synchronized (this) {
   1348             if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
   1349                 if (mBackStackIndices == null) {
   1350                     mBackStackIndices = new ArrayList<BackStackRecord>();
   1351                 }
   1352                 int index = mBackStackIndices.size();
   1353                 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
   1354                 mBackStackIndices.add(bse);
   1355                 return index;
   1356 
   1357             } else {
   1358                 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
   1359                 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
   1360                 mBackStackIndices.set(index, bse);
   1361                 return index;
   1362             }
   1363         }
   1364     }
   1365 
   1366     public void setBackStackIndex(int index, BackStackRecord bse) {
   1367         synchronized (this) {
   1368             if (mBackStackIndices == null) {
   1369                 mBackStackIndices = new ArrayList<BackStackRecord>();
   1370             }
   1371             int N = mBackStackIndices.size();
   1372             if (index < N) {
   1373                 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
   1374                 mBackStackIndices.set(index, bse);
   1375             } else {
   1376                 while (N < index) {
   1377                     mBackStackIndices.add(null);
   1378                     if (mAvailBackStackIndices == null) {
   1379                         mAvailBackStackIndices = new ArrayList<Integer>();
   1380                     }
   1381                     if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
   1382                     mAvailBackStackIndices.add(N);
   1383                     N++;
   1384                 }
   1385                 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
   1386                 mBackStackIndices.add(bse);
   1387             }
   1388         }
   1389     }
   1390 
   1391     public void freeBackStackIndex(int index) {
   1392         synchronized (this) {
   1393             mBackStackIndices.set(index, null);
   1394             if (mAvailBackStackIndices == null) {
   1395                 mAvailBackStackIndices = new ArrayList<Integer>();
   1396             }
   1397             if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
   1398             mAvailBackStackIndices.add(index);
   1399         }
   1400     }
   1401 
   1402     /**
   1403      * Only call from main thread!
   1404      */
   1405     public boolean execPendingActions() {
   1406         if (mExecutingActions) {
   1407             throw new IllegalStateException("Recursive entry to executePendingTransactions");
   1408         }
   1409 
   1410         if (Looper.myLooper() != mActivity.mHandler.getLooper()) {
   1411             throw new IllegalStateException("Must be called from main thread of process");
   1412         }
   1413 
   1414         boolean didSomething = false;
   1415 
   1416         while (true) {
   1417             int numActions;
   1418 
   1419             synchronized (this) {
   1420                 if (mPendingActions == null || mPendingActions.size() == 0) {
   1421                     break;
   1422                 }
   1423 
   1424                 numActions = mPendingActions.size();
   1425                 if (mTmpActions == null || mTmpActions.length < numActions) {
   1426                     mTmpActions = new Runnable[numActions];
   1427                 }
   1428                 mPendingActions.toArray(mTmpActions);
   1429                 mPendingActions.clear();
   1430                 mActivity.mHandler.removeCallbacks(mExecCommit);
   1431             }
   1432 
   1433             mExecutingActions = true;
   1434             for (int i=0; i<numActions; i++) {
   1435                 mTmpActions[i].run();
   1436                 mTmpActions[i] = null;
   1437             }
   1438             mExecutingActions = false;
   1439             didSomething = true;
   1440         }
   1441 
   1442         if (mHavePendingDeferredStart) {
   1443             boolean loadersRunning = false;
   1444             for (int i=0; i<mActive.size(); i++) {
   1445                 Fragment f = mActive.get(i);
   1446                 if (f != null && f.mLoaderManager != null) {
   1447                     loadersRunning |= f.mLoaderManager.hasRunningLoaders();
   1448                 }
   1449             }
   1450             if (!loadersRunning) {
   1451                 mHavePendingDeferredStart = false;
   1452                 startPendingDeferredFragments();
   1453             }
   1454         }
   1455         return didSomething;
   1456     }
   1457 
   1458     void reportBackStackChanged() {
   1459         if (mBackStackChangeListeners != null) {
   1460             for (int i=0; i<mBackStackChangeListeners.size(); i++) {
   1461                 mBackStackChangeListeners.get(i).onBackStackChanged();
   1462             }
   1463         }
   1464     }
   1465 
   1466     void addBackStackState(BackStackRecord state) {
   1467         if (mBackStack == null) {
   1468             mBackStack = new ArrayList<BackStackRecord>();
   1469         }
   1470         mBackStack.add(state);
   1471         reportBackStackChanged();
   1472     }
   1473 
   1474     boolean popBackStackState(Handler handler, String name, int id, int flags) {
   1475         if (mBackStack == null) {
   1476             return false;
   1477         }
   1478         if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
   1479             int last = mBackStack.size()-1;
   1480             if (last < 0) {
   1481                 return false;
   1482             }
   1483             final BackStackRecord bss = mBackStack.remove(last);
   1484             bss.popFromBackStack(true);
   1485             reportBackStackChanged();
   1486         } else {
   1487             int index = -1;
   1488             if (name != null || id >= 0) {
   1489                 // If a name or ID is specified, look for that place in
   1490                 // the stack.
   1491                 index = mBackStack.size()-1;
   1492                 while (index >= 0) {
   1493                     BackStackRecord bss = mBackStack.get(index);
   1494                     if (name != null && name.equals(bss.getName())) {
   1495                         break;
   1496                     }
   1497                     if (id >= 0 && id == bss.mIndex) {
   1498                         break;
   1499                     }
   1500                     index--;
   1501                 }
   1502                 if (index < 0) {
   1503                     return false;
   1504                 }
   1505                 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
   1506                     index--;
   1507                     // Consume all following entries that match.
   1508                     while (index >= 0) {
   1509                         BackStackRecord bss = mBackStack.get(index);
   1510                         if ((name != null && name.equals(bss.getName()))
   1511                                 || (id >= 0 && id == bss.mIndex)) {
   1512                             index--;
   1513                             continue;
   1514                         }
   1515                         break;
   1516                     }
   1517                 }
   1518             }
   1519             if (index == mBackStack.size()-1) {
   1520                 return false;
   1521             }
   1522             final ArrayList<BackStackRecord> states
   1523                     = new ArrayList<BackStackRecord>();
   1524             for (int i=mBackStack.size()-1; i>index; i--) {
   1525                 states.add(mBackStack.remove(i));
   1526             }
   1527             final int LAST = states.size()-1;
   1528             for (int i=0; i<=LAST; i++) {
   1529                 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
   1530                 states.get(i).popFromBackStack(i == LAST);
   1531             }
   1532             reportBackStackChanged();
   1533         }
   1534         return true;
   1535     }
   1536 
   1537     ArrayList<Fragment> retainNonConfig() {
   1538         ArrayList<Fragment> fragments = null;
   1539         if (mActive != null) {
   1540             for (int i=0; i<mActive.size(); i++) {
   1541                 Fragment f = mActive.get(i);
   1542                 if (f != null && f.mRetainInstance) {
   1543                     if (fragments == null) {
   1544                         fragments = new ArrayList<Fragment>();
   1545                     }
   1546                     fragments.add(f);
   1547                     f.mRetaining = true;
   1548                     f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
   1549                     if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
   1550                 }
   1551             }
   1552         }
   1553         return fragments;
   1554     }
   1555 
   1556     void saveFragmentViewState(Fragment f) {
   1557         if (f.mView == null) {
   1558             return;
   1559         }
   1560         if (mStateArray == null) {
   1561             mStateArray = new SparseArray<Parcelable>();
   1562         } else {
   1563             mStateArray.clear();
   1564         }
   1565         f.mView.saveHierarchyState(mStateArray);
   1566         if (mStateArray.size() > 0) {
   1567             f.mSavedViewState = mStateArray;
   1568             mStateArray = null;
   1569         }
   1570     }
   1571 
   1572     Bundle saveFragmentBasicState(Fragment f) {
   1573         Bundle result = null;
   1574 
   1575         if (mStateBundle == null) {
   1576             mStateBundle = new Bundle();
   1577         }
   1578         f.performSaveInstanceState(mStateBundle);
   1579         if (!mStateBundle.isEmpty()) {
   1580             result = mStateBundle;
   1581             mStateBundle = null;
   1582         }
   1583 
   1584         if (f.mView != null) {
   1585             saveFragmentViewState(f);
   1586         }
   1587         if (f.mSavedViewState != null) {
   1588             if (result == null) {
   1589                 result = new Bundle();
   1590             }
   1591             result.putSparseParcelableArray(
   1592                     FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
   1593         }
   1594         if (!f.mUserVisibleHint) {
   1595             if (result == null) {
   1596                 result = new Bundle();
   1597             }
   1598             // Only add this if it's not the default value
   1599             result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
   1600         }
   1601 
   1602         return result;
   1603     }
   1604 
   1605     Parcelable saveAllState() {
   1606         // Make sure all pending operations have now been executed to get
   1607         // our state update-to-date.
   1608         execPendingActions();
   1609 
   1610         mStateSaved = true;
   1611 
   1612         if (mActive == null || mActive.size() <= 0) {
   1613             return null;
   1614         }
   1615 
   1616         // First collect all active fragments.
   1617         int N = mActive.size();
   1618         FragmentState[] active = new FragmentState[N];
   1619         boolean haveFragments = false;
   1620         for (int i=0; i<N; i++) {
   1621             Fragment f = mActive.get(i);
   1622             if (f != null) {
   1623                 if (f.mIndex < 0) {
   1624                     throwException(new IllegalStateException(
   1625                             "Failure saving state: active " + f
   1626                             + " has cleared index: " + f.mIndex));
   1627                 }
   1628 
   1629                 haveFragments = true;
   1630 
   1631                 FragmentState fs = new FragmentState(f);
   1632                 active[i] = fs;
   1633 
   1634                 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
   1635                     fs.mSavedFragmentState = saveFragmentBasicState(f);
   1636 
   1637                     if (f.mTarget != null) {
   1638                         if (f.mTarget.mIndex < 0) {
   1639                             throwException(new IllegalStateException(
   1640                                     "Failure saving state: " + f
   1641                                     + " has target not in fragment manager: " + f.mTarget));
   1642                         }
   1643                         if (fs.mSavedFragmentState == null) {
   1644                             fs.mSavedFragmentState = new Bundle();
   1645                         }
   1646                         putFragment(fs.mSavedFragmentState,
   1647                                 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
   1648                         if (f.mTargetRequestCode != 0) {
   1649                             fs.mSavedFragmentState.putInt(
   1650                                     FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
   1651                                     f.mTargetRequestCode);
   1652                         }
   1653                     }
   1654 
   1655                 } else {
   1656                     fs.mSavedFragmentState = f.mSavedFragmentState;
   1657                 }
   1658 
   1659                 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
   1660                         + fs.mSavedFragmentState);
   1661             }
   1662         }
   1663 
   1664         if (!haveFragments) {
   1665             if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
   1666             return null;
   1667         }
   1668 
   1669         int[] added = null;
   1670         BackStackState[] backStack = null;
   1671 
   1672         // Build list of currently added fragments.
   1673         if (mAdded != null) {
   1674             N = mAdded.size();
   1675             if (N > 0) {
   1676                 added = new int[N];
   1677                 for (int i=0; i<N; i++) {
   1678                     added[i] = mAdded.get(i).mIndex;
   1679                     if (added[i] < 0) {
   1680                         throwException(new IllegalStateException(
   1681                                 "Failure saving state: active " + mAdded.get(i)
   1682                                 + " has cleared index: " + added[i]));
   1683                     }
   1684                     if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
   1685                             + ": " + mAdded.get(i));
   1686                 }
   1687             }
   1688         }
   1689 
   1690         // Now save back stack.
   1691         if (mBackStack != null) {
   1692             N = mBackStack.size();
   1693             if (N > 0) {
   1694                 backStack = new BackStackState[N];
   1695                 for (int i=0; i<N; i++) {
   1696                     backStack[i] = new BackStackState(this, mBackStack.get(i));
   1697                     if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
   1698                             + ": " + mBackStack.get(i));
   1699                 }
   1700             }
   1701         }
   1702 
   1703         FragmentManagerState fms = new FragmentManagerState();
   1704         fms.mActive = active;
   1705         fms.mAdded = added;
   1706         fms.mBackStack = backStack;
   1707         return fms;
   1708     }
   1709 
   1710     void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
   1711         // If there is no saved state at all, then there can not be
   1712         // any nonConfig fragments either, so that is that.
   1713         if (state == null) return;
   1714         FragmentManagerState fms = (FragmentManagerState)state;
   1715         if (fms.mActive == null) return;
   1716 
   1717         // First re-attach any non-config instances we are retaining back
   1718         // to their saved state, so we don't try to instantiate them again.
   1719         if (nonConfig != null) {
   1720             for (int i=0; i<nonConfig.size(); i++) {
   1721                 Fragment f = nonConfig.get(i);
   1722                 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
   1723                 FragmentState fs = fms.mActive[f.mIndex];
   1724                 fs.mInstance = f;
   1725                 f.mSavedViewState = null;
   1726                 f.mBackStackNesting = 0;
   1727                 f.mInLayout = false;
   1728                 f.mAdded = false;
   1729                 f.mTarget = null;
   1730                 if (fs.mSavedFragmentState != null) {
   1731                     fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
   1732                     f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
   1733                             FragmentManagerImpl.VIEW_STATE_TAG);
   1734                 }
   1735             }
   1736         }
   1737 
   1738         // Build the full list of active fragments, instantiating them from
   1739         // their saved state.
   1740         mActive = new ArrayList<Fragment>(fms.mActive.length);
   1741         if (mAvailIndices != null) {
   1742             mAvailIndices.clear();
   1743         }
   1744         for (int i=0; i<fms.mActive.length; i++) {
   1745             FragmentState fs = fms.mActive[i];
   1746             if (fs != null) {
   1747                 Fragment f = fs.instantiate(mActivity, mParent);
   1748                 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
   1749                 mActive.add(f);
   1750                 // Now that the fragment is instantiated (or came from being
   1751                 // retained above), clear mInstance in case we end up re-restoring
   1752                 // from this FragmentState again.
   1753                 fs.mInstance = null;
   1754             } else {
   1755                 mActive.add(null);
   1756                 if (mAvailIndices == null) {
   1757                     mAvailIndices = new ArrayList<Integer>();
   1758                 }
   1759                 if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
   1760                 mAvailIndices.add(i);
   1761             }
   1762         }
   1763 
   1764         // Update the target of all retained fragments.
   1765         if (nonConfig != null) {
   1766             for (int i=0; i<nonConfig.size(); i++) {
   1767                 Fragment f = nonConfig.get(i);
   1768                 if (f.mTargetIndex >= 0) {
   1769                     if (f.mTargetIndex < mActive.size()) {
   1770                         f.mTarget = mActive.get(f.mTargetIndex);
   1771                     } else {
   1772                         Log.w(TAG, "Re-attaching retained fragment " + f
   1773                                 + " target no longer exists: " + f.mTargetIndex);
   1774                         f.mTarget = null;
   1775                     }
   1776                 }
   1777             }
   1778         }
   1779 
   1780         // Build the list of currently added fragments.
   1781         if (fms.mAdded != null) {
   1782             mAdded = new ArrayList<Fragment>(fms.mAdded.length);
   1783             for (int i=0; i<fms.mAdded.length; i++) {
   1784                 Fragment f = mActive.get(fms.mAdded[i]);
   1785                 if (f == null) {
   1786                     throwException(new IllegalStateException(
   1787                             "No instantiated fragment for index #" + fms.mAdded[i]));
   1788                 }
   1789                 f.mAdded = true;
   1790                 if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
   1791                 if (mAdded.contains(f)) {
   1792                     throw new IllegalStateException("Already added!");
   1793                 }
   1794                 mAdded.add(f);
   1795             }
   1796         } else {
   1797             mAdded = null;
   1798         }
   1799 
   1800         // Build the back stack.
   1801         if (fms.mBackStack != null) {
   1802             mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
   1803             for (int i=0; i<fms.mBackStack.length; i++) {
   1804                 BackStackRecord bse = fms.mBackStack[i].instantiate(this);
   1805                 if (DEBUG) {
   1806                     Log.v(TAG, "restoreAllState: back stack #" + i
   1807                         + " (index " + bse.mIndex + "): " + bse);
   1808                     LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
   1809                     PrintWriter pw = new PrintWriter(logw);
   1810                     bse.dump("  ", pw, false);
   1811                 }
   1812                 mBackStack.add(bse);
   1813                 if (bse.mIndex >= 0) {
   1814                     setBackStackIndex(bse.mIndex, bse);
   1815                 }
   1816             }
   1817         } else {
   1818             mBackStack = null;
   1819         }
   1820     }
   1821 
   1822     public void attachActivity(Activity activity, FragmentContainer container, Fragment parent) {
   1823         if (mActivity != null) throw new IllegalStateException("Already attached");
   1824         mActivity = activity;
   1825         mContainer = container;
   1826         mParent = parent;
   1827     }
   1828 
   1829     public void noteStateNotSaved() {
   1830         mStateSaved = false;
   1831     }
   1832 
   1833     public void dispatchCreate() {
   1834         mStateSaved = false;
   1835         moveToState(Fragment.CREATED, false);
   1836     }
   1837 
   1838     public void dispatchActivityCreated() {
   1839         mStateSaved = false;
   1840         moveToState(Fragment.ACTIVITY_CREATED, false);
   1841     }
   1842 
   1843     public void dispatchStart() {
   1844         mStateSaved = false;
   1845         moveToState(Fragment.STARTED, false);
   1846     }
   1847 
   1848     public void dispatchResume() {
   1849         mStateSaved = false;
   1850         moveToState(Fragment.RESUMED, false);
   1851     }
   1852 
   1853     public void dispatchPause() {
   1854         moveToState(Fragment.STARTED, false);
   1855     }
   1856 
   1857     public void dispatchStop() {
   1858         moveToState(Fragment.STOPPED, false);
   1859     }
   1860 
   1861     public void dispatchDestroyView() {
   1862         moveToState(Fragment.CREATED, false);
   1863     }
   1864 
   1865     public void dispatchDestroy() {
   1866         mDestroyed = true;
   1867         execPendingActions();
   1868         moveToState(Fragment.INITIALIZING, false);
   1869         mActivity = null;
   1870         mContainer = null;
   1871         mParent = null;
   1872     }
   1873 
   1874     public void dispatchConfigurationChanged(Configuration newConfig) {
   1875         if (mAdded != null) {
   1876             for (int i=0; i<mAdded.size(); i++) {
   1877                 Fragment f = mAdded.get(i);
   1878                 if (f != null) {
   1879                     f.performConfigurationChanged(newConfig);
   1880                 }
   1881             }
   1882         }
   1883     }
   1884 
   1885     public void dispatchLowMemory() {
   1886         if (mAdded != null) {
   1887             for (int i=0; i<mAdded.size(); i++) {
   1888                 Fragment f = mAdded.get(i);
   1889                 if (f != null) {
   1890                     f.performLowMemory();
   1891                 }
   1892             }
   1893         }
   1894     }
   1895 
   1896     public void dispatchTrimMemory(int level) {
   1897         if (mAdded != null) {
   1898             for (int i=0; i<mAdded.size(); i++) {
   1899                 Fragment f = mAdded.get(i);
   1900                 if (f != null) {
   1901                     f.performTrimMemory(level);
   1902                 }
   1903             }
   1904         }
   1905     }
   1906 
   1907     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
   1908         boolean show = false;
   1909         ArrayList<Fragment> newMenus = null;
   1910         if (mAdded != null) {
   1911             for (int i=0; i<mAdded.size(); i++) {
   1912                 Fragment f = mAdded.get(i);
   1913                 if (f != null) {
   1914                     if (f.performCreateOptionsMenu(menu, inflater)) {
   1915                         show = true;
   1916                         if (newMenus == null) {
   1917                             newMenus = new ArrayList<Fragment>();
   1918                         }
   1919                         newMenus.add(f);
   1920                     }
   1921                 }
   1922             }
   1923         }
   1924 
   1925         if (mCreatedMenus != null) {
   1926             for (int i=0; i<mCreatedMenus.size(); i++) {
   1927                 Fragment f = mCreatedMenus.get(i);
   1928                 if (newMenus == null || !newMenus.contains(f)) {
   1929                     f.onDestroyOptionsMenu();
   1930                 }
   1931             }
   1932         }
   1933 
   1934         mCreatedMenus = newMenus;
   1935 
   1936         return show;
   1937     }
   1938 
   1939     public boolean dispatchPrepareOptionsMenu(Menu menu) {
   1940         boolean show = false;
   1941         if (mAdded != null) {
   1942             for (int i=0; i<mAdded.size(); i++) {
   1943                 Fragment f = mAdded.get(i);
   1944                 if (f != null) {
   1945                     if (f.performPrepareOptionsMenu(menu)) {
   1946                         show = true;
   1947                     }
   1948                 }
   1949             }
   1950         }
   1951         return show;
   1952     }
   1953 
   1954     public boolean dispatchOptionsItemSelected(MenuItem item) {
   1955         if (mAdded != null) {
   1956             for (int i=0; i<mAdded.size(); i++) {
   1957                 Fragment f = mAdded.get(i);
   1958                 if (f != null) {
   1959                     if (f.performOptionsItemSelected(item)) {
   1960                         return true;
   1961                     }
   1962                 }
   1963             }
   1964         }
   1965         return false;
   1966     }
   1967 
   1968     public boolean dispatchContextItemSelected(MenuItem item) {
   1969         if (mAdded != null) {
   1970             for (int i=0; i<mAdded.size(); i++) {
   1971                 Fragment f = mAdded.get(i);
   1972                 if (f != null) {
   1973                     if (f.performContextItemSelected(item)) {
   1974                         return true;
   1975                     }
   1976                 }
   1977             }
   1978         }
   1979         return false;
   1980     }
   1981 
   1982     public void dispatchOptionsMenuClosed(Menu menu) {
   1983         if (mAdded != null) {
   1984             for (int i=0; i<mAdded.size(); i++) {
   1985                 Fragment f = mAdded.get(i);
   1986                 if (f != null) {
   1987                     f.performOptionsMenuClosed(menu);
   1988                 }
   1989             }
   1990         }
   1991     }
   1992 
   1993     @Override
   1994     public void invalidateOptionsMenu() {
   1995         if (mActivity != null && mCurState == Fragment.RESUMED) {
   1996             mActivity.invalidateOptionsMenu();
   1997         } else {
   1998             mNeedMenuInvalidate = true;
   1999         }
   2000     }
   2001 
   2002     public static int reverseTransit(int transit) {
   2003         int rev = 0;
   2004         switch (transit) {
   2005             case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
   2006                 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
   2007                 break;
   2008             case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
   2009                 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
   2010                 break;
   2011             case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
   2012                 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
   2013                 break;
   2014         }
   2015         return rev;
   2016 
   2017     }
   2018 
   2019     public static int transitToStyleIndex(int transit, boolean enter) {
   2020         int animAttr = -1;
   2021         switch (transit) {
   2022             case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
   2023                 animAttr = enter
   2024                     ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation
   2025                     : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation;
   2026                 break;
   2027             case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
   2028                 animAttr = enter
   2029                     ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation
   2030                     : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation;
   2031                 break;
   2032             case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
   2033                 animAttr = enter
   2034                     ? com.android.internal.R.styleable.FragmentAnimation_fragmentFadeEnterAnimation
   2035                     : com.android.internal.R.styleable.FragmentAnimation_fragmentFadeExitAnimation;
   2036                 break;
   2037         }
   2038         return animAttr;
   2039     }
   2040 }
   2041