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