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