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