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