FragmentManager.java revision 445646c52128a763b56ed7bb3bd009e2f33e3e4f
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.content.res.TypedArray;
20import android.os.Bundle;
21import android.os.Handler;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.util.Log;
25import android.util.SparseArray;
26import android.view.Menu;
27import android.view.MenuInflater;
28import android.view.MenuItem;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.animation.Animation;
32import android.view.animation.AnimationUtils;
33
34import java.util.ArrayList;
35
36final class FragmentManagerState implements Parcelable {
37    FragmentState[] mActive;
38    int[] mAdded;
39    BackStackState[] mBackStack;
40
41    public FragmentManagerState() {
42    }
43
44    public FragmentManagerState(Parcel in) {
45        mActive = in.createTypedArray(FragmentState.CREATOR);
46        mAdded = in.createIntArray();
47        mBackStack = in.createTypedArray(BackStackState.CREATOR);
48    }
49
50    public int describeContents() {
51        return 0;
52    }
53
54    public void writeToParcel(Parcel dest, int flags) {
55        dest.writeTypedArray(mActive, flags);
56        dest.writeIntArray(mAdded);
57        dest.writeTypedArray(mBackStack, flags);
58    }
59
60    public static final Parcelable.Creator<FragmentManagerState> CREATOR
61            = new Parcelable.Creator<FragmentManagerState>() {
62        public FragmentManagerState createFromParcel(Parcel in) {
63            return new FragmentManagerState(in);
64        }
65
66        public FragmentManagerState[] newArray(int size) {
67            return new FragmentManagerState[size];
68        }
69    };
70}
71
72/**
73 * @hide
74 * Container for fragments associated with an activity.
75 */
76public class FragmentManager {
77    static final boolean DEBUG = true;
78    static final String TAG = "FragmentManager";
79
80    ArrayList<Runnable> mPendingActions;
81    Runnable[] mTmpActions;
82    boolean mExecutingActions;
83
84    ArrayList<Fragment> mActive;
85    ArrayList<Fragment> mAdded;
86    ArrayList<Integer> mAvailIndices;
87    ArrayList<BackStackEntry> mBackStack;
88
89    int mCurState = Fragment.INITIALIZING;
90    Activity mActivity;
91
92    boolean mNeedMenuInvalidate;
93
94    // Temporary vars for state save and restore.
95    Bundle mStateBundle = null;
96    SparseArray<Parcelable> mStateArray = null;
97
98    Runnable mExecCommit = new Runnable() {
99        @Override
100        public void run() {
101            execPendingActions();
102        }
103    };
104
105    Animation loadAnimation(Fragment fragment, int transit, boolean enter,
106            int transitionStyle) {
107        Animation animObj = fragment.onCreateAnimation(transitionStyle, enter,
108                fragment.mNextAnim);
109        if (animObj != null) {
110            return animObj;
111        }
112
113        if (fragment.mNextAnim != 0) {
114            Animation anim = AnimationUtils.loadAnimation(mActivity, fragment.mNextAnim);
115            if (anim != null) {
116                return anim;
117            }
118        }
119
120        if (transit == 0) {
121            return null;
122        }
123
124        int styleIndex = transitToStyleIndex(transit, enter);
125        if (styleIndex < 0) {
126            return null;
127        }
128
129        if (transitionStyle == 0 && mActivity.getWindow() != null) {
130            transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
131        }
132        if (transitionStyle == 0) {
133            return null;
134        }
135
136        TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
137                com.android.internal.R.styleable.WindowAnimation);
138        int anim = attrs.getResourceId(styleIndex, 0);
139        attrs.recycle();
140
141        if (anim == 0) {
142            return null;
143        }
144
145        return AnimationUtils.loadAnimation(mActivity, anim);
146    }
147
148    void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
149        // Fragments that are not currently added will sit in the onCreate() state.
150        if (!f.mAdded && newState > Fragment.CREATED) {
151            newState = Fragment.CREATED;
152        }
153
154        if (f.mState < newState) {
155            switch (f.mState) {
156                case Fragment.INITIALIZING:
157                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
158                    f.mActivity = mActivity;
159                    f.mCalled = false;
160                    f.onAttach(mActivity);
161                    if (!f.mCalled) {
162                        throw new SuperNotCalledException("Fragment " + f
163                                + " did not call through to super.onAttach()");
164                    }
165
166                    if (!f.mRetaining) {
167                        f.mCalled = false;
168                        f.onCreate(f.mSavedFragmentState);
169                        if (!f.mCalled) {
170                            throw new SuperNotCalledException("Fragment " + f
171                                    + " did not call through to super.onCreate()");
172                        }
173                    }
174                    f.mRetaining = false;
175                    if (f.mFromLayout) {
176                        // For fragments that are part of the content view
177                        // layout, we need to instantiate the view immediately
178                        // and the inflater will take care of adding it.
179                        f.mView = f.onCreateView(mActivity.getLayoutInflater(),
180                                null, f.mSavedFragmentState);
181                        if (f.mView != null) {
182                            f.mView.setSaveFromParentEnabled(false);
183                            f.restoreViewState();
184                            if (f.mHidden) f.mView.setVisibility(View.GONE);
185                        }
186                    }
187                case Fragment.CREATED:
188                    if (newState > Fragment.CREATED) {
189                        if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f);
190                        if (!f.mFromLayout) {
191                            ViewGroup container = null;
192                            if (f.mContainerId != 0) {
193                                container = (ViewGroup)mActivity.findViewById(f.mContainerId);
194                                if (container == null) {
195                                    throw new IllegalArgumentException("New view found for id 0x"
196                                            + Integer.toHexString(f.mContainerId)
197                                            + " for fragment " + f);
198                                }
199                            }
200                            f.mContainer = container;
201                            f.mView = f.onCreateView(mActivity.getLayoutInflater(),
202                                    container, f.mSavedFragmentState);
203                            if (f.mView != null) {
204                                f.mView.setSaveFromParentEnabled(false);
205                                if (container != null) {
206                                    Animation anim = loadAnimation(f, transit, true,
207                                            transitionStyle);
208                                    if (anim != null) {
209                                        f.mView.setAnimation(anim);
210                                    }
211                                    container.addView(f.mView);
212                                    f.restoreViewState();
213                                }
214                                if (f.mHidden) f.mView.setVisibility(View.GONE);
215                            }
216                        }
217
218                        f.mCalled = false;
219                        f.onReady(f.mSavedFragmentState);
220                        if (!f.mCalled) {
221                            throw new SuperNotCalledException("Fragment " + f
222                                    + " did not call through to super.onReady()");
223                        }
224                        f.mSavedFragmentState = null;
225                    }
226                case Fragment.CONTENT:
227                    if (newState > Fragment.CONTENT) {
228                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
229                        f.mCalled = false;
230                        f.onStart();
231                        if (!f.mCalled) {
232                            throw new SuperNotCalledException("Fragment " + f
233                                    + " did not call through to super.onStart()");
234                        }
235                    }
236                case Fragment.STARTED:
237                    if (newState > Fragment.STARTED) {
238                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
239                        f.mCalled = false;
240                        f.onResume();
241                        if (!f.mCalled) {
242                            throw new SuperNotCalledException("Fragment " + f
243                                    + " did not call through to super.onResume()");
244                        }
245                    }
246            }
247        } else if (f.mState > newState) {
248            switch (f.mState) {
249                case Fragment.RESUMED:
250                    if (newState < Fragment.RESUMED) {
251                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
252                        f.mCalled = false;
253                        f.onPause();
254                        if (!f.mCalled) {
255                            throw new SuperNotCalledException("Fragment " + f
256                                    + " did not call through to super.onPause()");
257                        }
258                    }
259                case Fragment.STARTED:
260                    if (newState < Fragment.STARTED) {
261                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
262                        f.mCalled = false;
263                        f.onStop();
264                        if (!f.mCalled) {
265                            throw new SuperNotCalledException("Fragment " + f
266                                    + " did not call through to super.onStop()");
267                        }
268                    }
269                case Fragment.CONTENT:
270                    if (newState < Fragment.CONTENT) {
271                        if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
272                        if (f.mView != null) {
273                            f.mCalled = false;
274                            f.onDestroyView();
275                            if (!f.mCalled) {
276                                throw new SuperNotCalledException("Fragment " + f
277                                        + " did not call through to super.onDestroyedView()");
278                            }
279                            // Need to save the current view state if not
280                            // done already.
281                            if (!mActivity.isFinishing() && f.mSavedFragmentState == null) {
282                                saveFragmentViewState(f);
283                            }
284                            if (f.mContainer != null) {
285                                if (mCurState > Fragment.INITIALIZING) {
286                                    Animation anim = loadAnimation(f, transit, false,
287                                            transitionStyle);
288                                    if (anim != null) {
289                                        f.mView.setAnimation(anim);
290                                    }
291                                }
292                                f.mContainer.removeView(f.mView);
293                            }
294                        }
295                        f.mContainer = null;
296                        f.mView = null;
297                    }
298                case Fragment.CREATED:
299                    if (newState < Fragment.CREATED) {
300                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
301                        if (!f.mRetaining) {
302                            f.mCalled = false;
303                            f.onDestroy();
304                            if (!f.mCalled) {
305                                throw new SuperNotCalledException("Fragment " + f
306                                        + " did not call through to super.onDestroy()");
307                            }
308                        }
309
310                        f.mCalled = false;
311                        f.onDetach();
312                        if (!f.mCalled) {
313                            throw new SuperNotCalledException("Fragment " + f
314                                    + " did not call through to super.onDetach()");
315                        }
316                        f.mActivity = null;
317                    }
318            }
319        }
320
321        f.mState = newState;
322    }
323
324    void moveToState(int newState, boolean always) {
325        moveToState(newState, 0, 0, always);
326    }
327
328    void moveToState(int newState, int transit, int transitStyle, boolean always) {
329        if (mActivity == null && newState != Fragment.INITIALIZING) {
330            throw new IllegalStateException("No activity");
331        }
332
333        if (!always && mCurState == newState) {
334            return;
335        }
336
337        mCurState = newState;
338        if (mActive != null) {
339            for (int i=0; i<mActive.size(); i++) {
340                Fragment f = mActive.get(i);
341                if (f != null) {
342                    moveToState(f, newState, transit, transitStyle);
343                }
344            }
345        }
346    }
347
348    void makeActive(Fragment f) {
349        if (f.mIndex >= 0) {
350            return;
351        }
352
353        if (mAvailIndices == null || mAvailIndices.size() <= 0) {
354            if (mActive == null) {
355                mActive = new ArrayList<Fragment>();
356            }
357            f.setIndex(mActive.size());
358            mActive.add(f);
359
360        } else {
361            f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1));
362            mActive.set(f.mIndex, f);
363        }
364    }
365
366    void makeInactive(Fragment f) {
367        if (f.mIndex < 0) {
368            return;
369        }
370
371        mActive.set(f.mIndex, null);
372        if (mAvailIndices == null) {
373            mAvailIndices = new ArrayList<Integer>();
374        }
375        mAvailIndices.add(f.mIndex);
376        f.clearIndex();
377    }
378
379    public void addFragment(Fragment fragment, boolean moveToStateNow) {
380        if (DEBUG) Log.v(TAG, "add: " + fragment);
381        if (mAdded == null) {
382            mAdded = new ArrayList<Fragment>();
383        }
384        mAdded.add(fragment);
385        makeActive(fragment);
386        fragment.mAdded = true;
387        if (fragment.mHasMenu) {
388            mNeedMenuInvalidate = true;
389        }
390        if (moveToStateNow) {
391            moveToState(fragment, mCurState, 0, 0);
392        }
393    }
394
395    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
396        if (DEBUG) Log.v(TAG, "remove: " + fragment);
397        mAdded.remove(fragment);
398        final boolean inactive = fragment.mBackStackNesting <= 0;
399        if (inactive) {
400            makeInactive(fragment);
401        }
402        if (fragment.mHasMenu) {
403            mNeedMenuInvalidate = true;
404        }
405        fragment.mAdded = false;
406        moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
407                transition, transitionStyle);
408    }
409
410    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
411        if (DEBUG) Log.v(TAG, "hide: " + fragment);
412        if (!fragment.mHidden) {
413            fragment.mHidden = true;
414            if (fragment.mView != null) {
415                Animation anim = loadAnimation(fragment, transition, false,
416                        transitionStyle);
417                if (anim != null) {
418                    fragment.mView.setAnimation(anim);
419                }
420                fragment.mView.setVisibility(View.GONE);
421            }
422            if (fragment.mAdded && fragment.mHasMenu) {
423                mNeedMenuInvalidate = true;
424            }
425            fragment.onHiddenChanged(true);
426        }
427    }
428
429    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
430        if (DEBUG) Log.v(TAG, "show: " + fragment);
431        if (fragment.mHidden) {
432            fragment.mHidden = false;
433            if (fragment.mView != null) {
434                Animation anim = loadAnimation(fragment, transition, true,
435                        transitionStyle);
436                if (anim != null) {
437                    fragment.mView.setAnimation(anim);
438                }
439                fragment.mView.setVisibility(View.VISIBLE);
440            }
441            if (fragment.mAdded && fragment.mHasMenu) {
442                mNeedMenuInvalidate = true;
443            }
444            fragment.onHiddenChanged(false);
445        }
446    }
447
448    public Fragment findFragmentById(int id) {
449        if (mActive != null) {
450            // First look through added fragments.
451            for (int i=mAdded.size()-1; i>=0; i--) {
452                Fragment f = mAdded.get(i);
453                if (f != null && f.mFragmentId == id) {
454                    return f;
455                }
456            }
457            // Now for any known fragment.
458            for (int i=mActive.size()-1; i>=0; i--) {
459                Fragment f = mActive.get(i);
460                if (f != null && f.mFragmentId == id) {
461                    return f;
462                }
463            }
464        }
465        return null;
466    }
467
468    public Fragment findFragmentByTag(String tag) {
469        if (mActive != null && tag != null) {
470            // First look through added fragments.
471            for (int i=mAdded.size()-1; i>=0; i--) {
472                Fragment f = mAdded.get(i);
473                if (f != null && tag.equals(f.mTag)) {
474                    return f;
475                }
476            }
477            // Now for any known fragment.
478            for (int i=mActive.size()-1; i>=0; i--) {
479                Fragment f = mActive.get(i);
480                if (f != null && tag.equals(f.mTag)) {
481                    return f;
482                }
483            }
484        }
485        return null;
486    }
487
488    public Fragment findFragmentByWho(String who) {
489        if (mActive != null && who != null) {
490            for (int i=mActive.size()-1; i>=0; i--) {
491                Fragment f = mActive.get(i);
492                if (f != null && who.equals(f.mWho)) {
493                    return f;
494                }
495            }
496        }
497        return null;
498    }
499
500    public void enqueueAction(Runnable action) {
501        synchronized (this) {
502            if (mPendingActions == null) {
503                mPendingActions = new ArrayList<Runnable>();
504            }
505            mPendingActions.add(action);
506            if (mPendingActions.size() == 1) {
507                mActivity.mHandler.removeCallbacks(mExecCommit);
508                mActivity.mHandler.post(mExecCommit);
509            }
510        }
511    }
512
513    /**
514     * Only call from main thread!
515     */
516    public void execPendingActions() {
517        if (mExecutingActions) {
518            throw new IllegalStateException("Recursive entry to execPendingActions");
519        }
520
521        while (true) {
522            int numActions;
523
524            synchronized (this) {
525                if (mPendingActions == null || mPendingActions.size() == 0) {
526                    return;
527                }
528
529                numActions = mPendingActions.size();
530                if (mTmpActions == null || mTmpActions.length < numActions) {
531                    mTmpActions = new Runnable[numActions];
532                }
533                mPendingActions.toArray(mTmpActions);
534                mPendingActions.clear();
535                mActivity.mHandler.removeCallbacks(mExecCommit);
536            }
537
538            mExecutingActions = true;
539            for (int i=0; i<numActions; i++) {
540                mTmpActions[i].run();
541            }
542            mExecutingActions = false;
543        }
544    }
545
546    public void addBackStackState(BackStackEntry state) {
547        if (mBackStack == null) {
548            mBackStack = new ArrayList<BackStackEntry>();
549        }
550        mBackStack.add(state);
551    }
552
553    public boolean popBackStackState(Handler handler, String name) {
554        if (mBackStack == null) {
555            return false;
556        }
557        if (name == null) {
558            int last = mBackStack.size()-1;
559            if (last < 0) {
560                return false;
561            }
562            final BackStackEntry bss = mBackStack.remove(last);
563            enqueueAction(new Runnable() {
564                public void run() {
565                    if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
566                    bss.popFromBackStack();
567                    moveToState(mCurState, reverseTransit(bss.getTransition()),
568                            bss.getTransitionStyle(), true);
569                }
570            });
571        } else {
572            int index = mBackStack.size()-1;
573            while (index >= 0) {
574                BackStackEntry bss = mBackStack.get(index);
575                if (name.equals(bss.getName())) {
576                    break;
577                }
578            }
579            if (index < 0 || index == mBackStack.size()-1) {
580                return false;
581            }
582            final ArrayList<BackStackEntry> states
583                    = new ArrayList<BackStackEntry>();
584            for (int i=mBackStack.size()-1; i>index; i--) {
585                states.add(mBackStack.remove(i));
586            }
587            enqueueAction(new Runnable() {
588                public void run() {
589                    for (int i=0; i<states.size(); i++) {
590                        if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
591                        states.get(i).popFromBackStack();
592                    }
593                    moveToState(mCurState, true);
594                }
595            });
596        }
597        return true;
598    }
599
600    ArrayList<Fragment> retainNonConfig() {
601        ArrayList<Fragment> fragments = null;
602        if (mActive != null) {
603            for (int i=0; i<mActive.size(); i++) {
604                Fragment f = mActive.get(i);
605                if (f != null && f.mRetainInstance) {
606                    if (fragments == null) {
607                        fragments = new ArrayList<Fragment>();
608                    }
609                    fragments.add(f);
610                    f.mRetaining = true;
611                }
612            }
613        }
614        return fragments;
615    }
616
617    void saveFragmentViewState(Fragment f) {
618        if (f.mView == null) {
619            return;
620        }
621        if (mStateArray == null) {
622            mStateArray = new SparseArray<Parcelable>();
623        }
624        f.mView.saveHierarchyState(mStateArray);
625        if (mStateArray.size() > 0) {
626            f.mSavedViewState = mStateArray;
627            mStateArray = null;
628        }
629    }
630
631    Parcelable saveAllState() {
632        if (mActive == null || mActive.size() <= 0) {
633            return null;
634        }
635
636        // First collect all active fragments.
637        int N = mActive.size();
638        FragmentState[] active = new FragmentState[N];
639        boolean haveFragments = false;
640        for (int i=0; i<N; i++) {
641            Fragment f = mActive.get(i);
642            if (f != null) {
643                haveFragments = true;
644
645                FragmentState fs = new FragmentState(f);
646                active[i] = fs;
647
648                if (mStateBundle == null) {
649                    mStateBundle = new Bundle();
650                }
651                f.onSaveInstanceState(mStateBundle);
652                if (!mStateBundle.isEmpty()) {
653                    fs.mSavedFragmentState = mStateBundle;
654                    mStateBundle = null;
655                }
656
657                if (f.mView != null) {
658                    saveFragmentViewState(f);
659                    if (f.mSavedViewState != null) {
660                        if (fs.mSavedFragmentState == null) {
661                            fs.mSavedFragmentState = new Bundle();
662                        }
663                        fs.mSavedFragmentState.putSparseParcelableArray(
664                                FragmentState.VIEW_STATE_TAG, f.mSavedViewState);
665                    }
666                }
667
668            }
669        }
670
671        if (!haveFragments) {
672            return null;
673        }
674
675        int[] added = null;
676        BackStackState[] backStack = null;
677
678        // Build list of currently added fragments.
679        N = mAdded.size();
680        if (N > 0) {
681            added = new int[N];
682            for (int i=0; i<N; i++) {
683                added[i] = mAdded.get(i).mIndex;
684            }
685        }
686
687        // Now save back stack.
688        if (mBackStack != null) {
689            N = mBackStack.size();
690            if (N > 0) {
691                backStack = new BackStackState[N];
692                for (int i=0; i<N; i++) {
693                    backStack[i] = new BackStackState(this, mBackStack.get(i));
694                }
695            }
696        }
697
698        FragmentManagerState fms = new FragmentManagerState();
699        fms.mActive = active;
700        fms.mAdded = added;
701        fms.mBackStack = backStack;
702        return fms;
703    }
704
705    void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
706        // If there is no saved state at all, then there can not be
707        // any nonConfig fragments either, so that is that.
708        if (state == null) return;
709        FragmentManagerState fms = (FragmentManagerState)state;
710        if (fms.mActive == null) return;
711
712        // First re-attach any non-config instances we are retaining back
713        // to their saved state, so we don't try to instantiate them again.
714        if (nonConfig != null) {
715            for (int i=0; i<nonConfig.size(); i++) {
716                Fragment f = nonConfig.get(i);
717                FragmentState fs = fms.mActive[f.mIndex];
718                fs.mInstance = f;
719                f.mSavedViewState = null;
720                f.mBackStackNesting = 0;
721                f.mAdded = false;
722                if (fs.mSavedFragmentState != null) {
723                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
724                            FragmentState.VIEW_STATE_TAG);
725                }
726            }
727        }
728
729        // Build the full list of active fragments, instantiating them from
730        // their saved state.
731        mActive = new ArrayList<Fragment>(fms.mActive.length);
732        if (mAvailIndices != null) {
733            mAvailIndices.clear();
734        }
735        for (int i=0; i<fms.mActive.length; i++) {
736            FragmentState fs = fms.mActive[i];
737            if (fs != null) {
738                mActive.add(fs.instantiate(mActivity));
739            } else {
740                mActive.add(null);
741                if (mAvailIndices == null) {
742                    mAvailIndices = new ArrayList<Integer>();
743                }
744                mAvailIndices.add(i);
745            }
746        }
747
748        // Build the list of currently added fragments.
749        if (fms.mAdded != null) {
750            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
751            for (int i=0; i<fms.mAdded.length; i++) {
752                Fragment f = mActive.get(fms.mAdded[i]);
753                if (f == null) {
754                    throw new IllegalStateException(
755                            "No instantiated fragment for index #" + fms.mAdded[i]);
756                }
757                f.mAdded = true;
758                f.mImmediateActivity = mActivity;
759                mAdded.add(f);
760            }
761        } else {
762            mAdded = null;
763        }
764
765        // Build the back stack.
766        if (fms.mBackStack != null) {
767            mBackStack = new ArrayList<BackStackEntry>(fms.mBackStack.length);
768            for (int i=0; i<fms.mBackStack.length; i++) {
769                BackStackEntry bse = fms.mBackStack[i].instantiate(this);
770                mBackStack.add(bse);
771            }
772        } else {
773            mBackStack = null;
774        }
775    }
776
777    public void attachActivity(Activity activity) {
778        if (mActivity != null) throw new IllegalStateException();
779        mActivity = activity;
780    }
781
782    public void dispatchCreate() {
783        moveToState(Fragment.CREATED, false);
784    }
785
786    public void dispatchStart() {
787        moveToState(Fragment.STARTED, false);
788    }
789
790    public void dispatchResume() {
791        moveToState(Fragment.RESUMED, false);
792    }
793
794    public void dispatchPause() {
795        moveToState(Fragment.STARTED, false);
796    }
797
798    public void dispatchStop() {
799        moveToState(Fragment.CONTENT, false);
800    }
801
802    public void dispatchDestroy() {
803        moveToState(Fragment.INITIALIZING, false);
804        mActivity = null;
805    }
806
807    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
808        boolean show = false;
809        if (mActive != null) {
810            for (int i=0; i<mAdded.size(); i++) {
811                Fragment f = mAdded.get(i);
812                if (f != null && !f.mHidden && f.mHasMenu) {
813                    show = true;
814                    f.onCreateOptionsMenu(menu, inflater);
815                }
816            }
817        }
818        return show;
819    }
820
821    public boolean dispatchPrepareOptionsMenu(Menu menu) {
822        boolean show = false;
823        if (mActive != null) {
824            for (int i=0; i<mAdded.size(); i++) {
825                Fragment f = mAdded.get(i);
826                if (f != null && !f.mHidden && f.mHasMenu) {
827                    show = true;
828                    f.onPrepareOptionsMenu(menu);
829                }
830            }
831        }
832        return show;
833    }
834
835    public boolean dispatchOptionsItemSelected(MenuItem item) {
836        if (mActive != null) {
837            for (int i=0; i<mAdded.size(); i++) {
838                Fragment f = mAdded.get(i);
839                if (f != null && !f.mHidden && f.mHasMenu) {
840                    if (f.onOptionsItemSelected(item)) {
841                        return true;
842                    }
843                }
844            }
845        }
846        return false;
847    }
848
849    public boolean dispatchContextItemSelected(MenuItem item) {
850        if (mActive != null) {
851            for (int i=0; i<mAdded.size(); i++) {
852                Fragment f = mAdded.get(i);
853                if (f != null && !f.mHidden) {
854                    if (f.onContextItemSelected(item)) {
855                        return true;
856                    }
857                }
858            }
859        }
860        return false;
861    }
862
863    public void dispatchOptionsMenuClosed(Menu menu) {
864        if (mActive != null) {
865            for (int i=0; i<mAdded.size(); i++) {
866                Fragment f = mAdded.get(i);
867                if (f != null && !f.mHidden && f.mHasMenu) {
868                    f.onOptionsMenuClosed(menu);
869                }
870            }
871        }
872    }
873
874    public static int reverseTransit(int transit) {
875        int rev = 0;
876        switch (transit) {
877            case FragmentTransaction.TRANSIT_ENTER:
878                rev = FragmentTransaction.TRANSIT_EXIT;
879                break;
880            case FragmentTransaction.TRANSIT_EXIT:
881                rev = FragmentTransaction.TRANSIT_ENTER;
882                break;
883            case FragmentTransaction.TRANSIT_SHOW:
884                rev = FragmentTransaction.TRANSIT_HIDE;
885                break;
886            case FragmentTransaction.TRANSIT_HIDE:
887                rev = FragmentTransaction.TRANSIT_SHOW;
888                break;
889            case FragmentTransaction.TRANSIT_ACTIVITY_OPEN:
890                rev = FragmentTransaction.TRANSIT_ACTIVITY_CLOSE;
891                break;
892            case FragmentTransaction.TRANSIT_ACTIVITY_CLOSE:
893                rev = FragmentTransaction.TRANSIT_ACTIVITY_OPEN;
894                break;
895            case FragmentTransaction.TRANSIT_TASK_OPEN:
896                rev = FragmentTransaction.TRANSIT_TASK_CLOSE;
897                break;
898            case FragmentTransaction.TRANSIT_TASK_CLOSE:
899                rev = FragmentTransaction.TRANSIT_TASK_OPEN;
900                break;
901            case FragmentTransaction.TRANSIT_TASK_TO_FRONT:
902                rev = FragmentTransaction.TRANSIT_TASK_TO_BACK;
903                break;
904            case FragmentTransaction.TRANSIT_TASK_TO_BACK:
905                rev = FragmentTransaction.TRANSIT_TASK_TO_FRONT;
906                break;
907            case FragmentTransaction.TRANSIT_WALLPAPER_OPEN:
908                rev = FragmentTransaction.TRANSIT_WALLPAPER_CLOSE;
909                break;
910            case FragmentTransaction.TRANSIT_WALLPAPER_CLOSE:
911                rev = FragmentTransaction.TRANSIT_WALLPAPER_OPEN;
912                break;
913            case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN:
914                rev = FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE;
915                break;
916            case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE:
917                rev = FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN;
918                break;
919        }
920        return rev;
921
922    }
923
924    public static int transitToStyleIndex(int transit, boolean enter) {
925        int animAttr = -1;
926        switch (transit) {
927            case FragmentTransaction.TRANSIT_ENTER:
928                animAttr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
929                break;
930            case FragmentTransaction.TRANSIT_EXIT:
931                animAttr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
932                break;
933            case FragmentTransaction.TRANSIT_SHOW:
934                animAttr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
935                break;
936            case FragmentTransaction.TRANSIT_HIDE:
937                animAttr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
938                break;
939            case FragmentTransaction.TRANSIT_ACTIVITY_OPEN:
940                animAttr = enter
941                        ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation
942                        : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
943                break;
944            case FragmentTransaction.TRANSIT_ACTIVITY_CLOSE:
945                animAttr = enter
946                        ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
947                        : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
948                break;
949            case FragmentTransaction.TRANSIT_TASK_OPEN:
950                animAttr = enter
951                        ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation
952                        : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
953                break;
954            case FragmentTransaction.TRANSIT_TASK_CLOSE:
955                animAttr = enter
956                        ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation
957                        : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
958                break;
959            case FragmentTransaction.TRANSIT_TASK_TO_FRONT:
960                animAttr = enter
961                        ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation
962                        : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
963                break;
964            case FragmentTransaction.TRANSIT_TASK_TO_BACK:
965                animAttr = enter
966                        ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
967                        : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
968                break;
969            case FragmentTransaction.TRANSIT_WALLPAPER_OPEN:
970                animAttr = enter
971                        ? com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
972                        : com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
973                break;
974            case FragmentTransaction.TRANSIT_WALLPAPER_CLOSE:
975                animAttr = enter
976                        ? com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
977                        : com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
978                break;
979            case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN:
980                animAttr = enter
981                        ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
982                        : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
983                break;
984            case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE:
985                animAttr = enter
986                        ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
987                        : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
988                break;
989        }
990        return animAttr;
991    }
992}
993