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