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