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