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