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