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