FragmentManager.java revision 4706c089880423ee5929d4635c6dfc5c3151b20f
1/*
2 * Copyright (C) 2011 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.support.v4.app;
18
19import android.content.Context;
20import android.content.res.Configuration;
21import android.content.res.TypedArray;
22import android.os.Build;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.support.annotation.CallSuper;
29import android.support.annotation.IdRes;
30import android.support.annotation.StringRes;
31import android.support.v4.util.DebugUtils;
32import android.support.v4.util.LogWriter;
33import android.support.v4.view.LayoutInflaterFactory;
34import android.support.v4.view.ViewCompat;
35import android.util.AttributeSet;
36import android.util.Log;
37import android.util.SparseArray;
38import android.view.animation.AccelerateInterpolator;
39import android.view.animation.AlphaAnimation;
40import android.view.animation.Animation;
41import android.view.animation.AnimationSet;
42import android.view.animation.AnimationUtils;
43import android.view.animation.DecelerateInterpolator;
44import android.view.animation.Interpolator;
45import android.view.animation.ScaleAnimation;
46import android.view.animation.Animation.AnimationListener;
47import android.view.Menu;
48import android.view.MenuInflater;
49import android.view.MenuItem;
50import android.view.View;
51import android.view.ViewGroup;
52
53import java.io.FileDescriptor;
54import java.io.PrintWriter;
55import java.lang.reflect.Field;
56import java.util.ArrayList;
57import java.util.Arrays;
58import java.util.List;
59
60/**
61 * Static library support version of the framework's {@link android.app.FragmentManager}.
62 * Used to write apps that run on platforms prior to Android 3.0.  When running
63 * on Android 3.0 or above, this implementation is still used; it does not try
64 * to switch to the framework's implementation.  See the framework {@link FragmentManager}
65 * documentation for a class overview.
66 *
67 * <p>Your activity must derive from {@link FragmentActivity} to use this. From such an activity,
68 * you can acquire the {@link FragmentManager} by calling
69 * {@link FragmentActivity#getSupportFragmentManager}.
70 */
71public abstract class FragmentManager {
72    /**
73     * Representation of an entry on the fragment back stack, as created
74     * with {@link FragmentTransaction#addToBackStack(String)
75     * FragmentTransaction.addToBackStack()}.  Entries can later be
76     * retrieved with {@link FragmentManager#getBackStackEntryAt(int)
77     * FragmentManager.getBackStackEntry()}.
78     *
79     * <p>Note that you should never hold on to a BackStackEntry object;
80     * the identifier as returned by {@link #getId} is the only thing that
81     * will be persisted across activity instances.
82     */
83    public interface BackStackEntry {
84        /**
85         * Return the unique identifier for the entry.  This is the only
86         * representation of the entry that will persist across activity
87         * instances.
88         */
89        public int getId();
90
91        /**
92         * Get the name that was supplied to
93         * {@link FragmentTransaction#addToBackStack(String)
94         * FragmentTransaction.addToBackStack(String)} when creating this entry.
95         */
96        public String getName();
97
98        /**
99         * Return the full bread crumb title resource identifier for the entry,
100         * or 0 if it does not have one.
101         */
102        @StringRes
103        public int getBreadCrumbTitleRes();
104
105        /**
106         * Return the short bread crumb title resource identifier for the entry,
107         * or 0 if it does not have one.
108         */
109        @StringRes
110        public int getBreadCrumbShortTitleRes();
111
112        /**
113         * Return the full bread crumb title for the entry, or null if it
114         * does not have one.
115         */
116        public CharSequence getBreadCrumbTitle();
117
118        /**
119         * Return the short bread crumb title for the entry, or null if it
120         * does not have one.
121         */
122        public CharSequence getBreadCrumbShortTitle();
123    }
124
125    /**
126     * Interface to watch for changes to the back stack.
127     */
128    public interface OnBackStackChangedListener {
129        /**
130         * Called whenever the contents of the back stack change.
131         */
132        public void onBackStackChanged();
133    }
134
135    /**
136     * Start a series of edit operations on the Fragments associated with
137     * this FragmentManager.
138     *
139     * <p>Note: A fragment transaction can only be created/committed prior
140     * to an activity saving its state.  If you try to commit a transaction
141     * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()}
142     * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart}
143     * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error.
144     * This is because the framework takes care of saving your current fragments
145     * in the state, and if changes are made after the state is saved then they
146     * will be lost.</p>
147     */
148    public abstract FragmentTransaction beginTransaction();
149
150    /** @hide -- remove once prebuilts are in. */
151    @Deprecated
152    public FragmentTransaction openTransaction() {
153        return beginTransaction();
154    }
155
156    /**
157     * After a {@link FragmentTransaction} is committed with
158     * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it
159     * is scheduled to be executed asynchronously on the process's main thread.
160     * If you want to immediately executing any such pending operations, you
161     * can call this function (only from the main thread) to do so.  Note that
162     * all callbacks and other related behavior will be done from within this
163     * call, so be careful about where this is called from.
164     *
165     * @return Returns true if there were any pending transactions to be
166     * executed.
167     */
168    public abstract boolean executePendingTransactions();
169
170    /**
171     * Finds a fragment that was identified by the given id either when inflated
172     * from XML or as the container ID when added in a transaction.  This first
173     * searches through fragments that are currently added to the manager's
174     * activity; if no such fragment is found, then all fragments currently
175     * on the back stack associated with this ID are searched.
176     * @return The fragment if found or null otherwise.
177     */
178    public abstract Fragment findFragmentById(@IdRes int id);
179
180    /**
181     * Finds a fragment that was identified by the given tag either when inflated
182     * from XML or as supplied when added in a transaction.  This first
183     * searches through fragments that are currently added to the manager's
184     * activity; if no such fragment is found, then all fragments currently
185     * on the back stack are searched.
186     * @return The fragment if found or null otherwise.
187     */
188    public abstract Fragment findFragmentByTag(String tag);
189
190    /**
191     * Flag for {@link #popBackStack(String, int)}
192     * and {@link #popBackStack(int, int)}: If set, and the name or ID of
193     * a back stack entry has been supplied, then all matching entries will
194     * be consumed until one that doesn't match is found or the bottom of
195     * the stack is reached.  Otherwise, all entries up to but not including that entry
196     * will be removed.
197     */
198    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
199
200    /**
201     * Pop the top state off the back stack.  Returns true if there was one
202     * to pop, else false.  This function is asynchronous -- it enqueues the
203     * request to pop, but the action will not be performed until the application
204     * returns to its event loop.
205     */
206    public abstract void popBackStack();
207
208    /**
209     * Like {@link #popBackStack()}, but performs the operation immediately
210     * inside of the call.  This is like calling {@link #executePendingTransactions()}
211     * afterwards.
212     * @return Returns true if there was something popped, else false.
213     */
214    public abstract boolean popBackStackImmediate();
215
216    /**
217     * Pop the last fragment transition from the manager's fragment
218     * back stack.  If there is nothing to pop, false is returned.
219     * This function is asynchronous -- it enqueues the
220     * request to pop, but the action will not be performed until the application
221     * returns to its event loop.
222     *
223     * @param name If non-null, this is the name of a previous back state
224     * to look for; if found, all states up to that state will be popped.  The
225     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
226     * the named state itself is popped. If null, only the top state is popped.
227     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
228     */
229    public abstract void popBackStack(String name, int flags);
230
231    /**
232     * Like {@link #popBackStack(String, int)}, but performs the operation immediately
233     * inside of the call.  This is like calling {@link #executePendingTransactions()}
234     * afterwards.
235     * @return Returns true if there was something popped, else false.
236     */
237    public abstract boolean popBackStackImmediate(String name, int flags);
238
239    /**
240     * Pop all back stack states up to the one with the given identifier.
241     * This function is asynchronous -- it enqueues the
242     * request to pop, but the action will not be performed until the application
243     * returns to its event loop.
244     *
245     * @param id Identifier of the stated to be popped. If no identifier exists,
246     * false is returned.
247     * The identifier is the number returned by
248     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
249     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
250     * the named state itself is popped.
251     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
252     */
253    public abstract void popBackStack(int id, int flags);
254
255    /**
256     * Like {@link #popBackStack(int, int)}, but performs the operation immediately
257     * inside of the call.  This is like calling {@link #executePendingTransactions()}
258     * afterwards.
259     * @return Returns true if there was something popped, else false.
260     */
261    public abstract boolean popBackStackImmediate(int id, int flags);
262
263    /**
264     * Return the number of entries currently in the back stack.
265     */
266    public abstract int getBackStackEntryCount();
267
268    /**
269     * Return the BackStackEntry at index <var>index</var> in the back stack;
270     * entries start index 0 being the bottom of the stack.
271     */
272    public abstract BackStackEntry getBackStackEntryAt(int index);
273
274    /**
275     * Add a new listener for changes to the fragment back stack.
276     */
277    public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
278
279    /**
280     * Remove a listener that was previously added with
281     * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
282     */
283    public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
284
285    /**
286     * Put a reference to a fragment in a Bundle.  This Bundle can be
287     * persisted as saved state, and when later restoring
288     * {@link #getFragment(Bundle, String)} will return the current
289     * instance of the same fragment.
290     *
291     * @param bundle The bundle in which to put the fragment reference.
292     * @param key The name of the entry in the bundle.
293     * @param fragment The Fragment whose reference is to be stored.
294     */
295    public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
296
297    /**
298     * Retrieve the current Fragment instance for a reference previously
299     * placed with {@link #putFragment(Bundle, String, Fragment)}.
300     *
301     * @param bundle The bundle from which to retrieve the fragment reference.
302     * @param key The name of the entry in the bundle.
303     * @return Returns the current Fragment instance that is associated with
304     * the given reference.
305     */
306    public abstract Fragment getFragment(Bundle bundle, String key);
307
308    /**
309     * Get a list of all fragments that have been added to the fragment manager.
310     *
311     * @return The list of all fragments or null if none.
312     * @hide
313     */
314    public abstract List<Fragment> getFragments();
315
316    /**
317     * Save the current instance state of the given Fragment.  This can be
318     * used later when creating a new instance of the Fragment and adding
319     * it to the fragment manager, to have it create itself to match the
320     * current state returned here.  Note that there are limits on how
321     * this can be used:
322     *
323     * <ul>
324     * <li>The Fragment must currently be attached to the FragmentManager.
325     * <li>A new Fragment created using this saved state must be the same class
326     * type as the Fragment it was created from.
327     * <li>The saved state can not contain dependencies on other fragments --
328     * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to
329     * store a fragment reference because that reference may not be valid when
330     * this saved state is later used.  Likewise the Fragment's target and
331     * result code are not included in this state.
332     * </ul>
333     *
334     * @param f The Fragment whose state is to be saved.
335     * @return The generated state.  This will be null if there was no
336     * interesting state created by the fragment.
337     */
338    public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
339
340    /**
341     * Returns true if the final {@link android.app.Activity#onDestroy() Activity.onDestroy()}
342     * call has been made on the FragmentManager's Activity, so this instance is now dead.
343     */
344    public abstract boolean isDestroyed();
345
346    /**
347     * Print the FragmentManager's state into the given stream.
348     *
349     * @param prefix Text to print at the front of each line.
350     * @param fd The raw file descriptor that the dump is being sent to.
351     * @param writer A PrintWriter to which the dump is to be set.
352     * @param args Additional arguments to the dump request.
353     */
354    public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
355
356    /**
357     * Control whether the framework's internal fragment manager debugging
358     * logs are turned on.  If enabled, you will see output in logcat as
359     * the framework performs fragment operations.
360     */
361    public static void enableDebugLogging(boolean enabled) {
362        FragmentManagerImpl.DEBUG = enabled;
363    }
364}
365
366final class FragmentManagerState implements Parcelable {
367    FragmentState[] mActive;
368    int[] mAdded;
369    BackStackState[] mBackStack;
370
371    public FragmentManagerState() {
372    }
373
374    public FragmentManagerState(Parcel in) {
375        mActive = in.createTypedArray(FragmentState.CREATOR);
376        mAdded = in.createIntArray();
377        mBackStack = in.createTypedArray(BackStackState.CREATOR);
378    }
379
380    public int describeContents() {
381        return 0;
382    }
383
384    public void writeToParcel(Parcel dest, int flags) {
385        dest.writeTypedArray(mActive, flags);
386        dest.writeIntArray(mAdded);
387        dest.writeTypedArray(mBackStack, flags);
388    }
389
390    public static final Parcelable.Creator<FragmentManagerState> CREATOR
391            = new Parcelable.Creator<FragmentManagerState>() {
392        public FragmentManagerState createFromParcel(Parcel in) {
393            return new FragmentManagerState(in);
394        }
395
396        public FragmentManagerState[] newArray(int size) {
397            return new FragmentManagerState[size];
398        }
399    };
400}
401
402/**
403 * Container for fragments associated with an activity.
404 */
405final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
406    static boolean DEBUG = false;
407    static final String TAG = "FragmentManager";
408
409    static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11;
410
411    static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
412    static final String TARGET_STATE_TAG = "android:target_state";
413    static final String VIEW_STATE_TAG = "android:view_state";
414    static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
415
416
417    static class AnimateOnHWLayerIfNeededListener implements AnimationListener {
418        private AnimationListener mOrignalListener = null;
419        private boolean mShouldRunOnHWLayer = false;
420        private View mView = null;
421        public AnimateOnHWLayerIfNeededListener(final View v, Animation anim) {
422            if (v == null || anim == null) {
423                return;
424            }
425            mView = v;
426        }
427
428        public AnimateOnHWLayerIfNeededListener(final View v, Animation anim,
429                AnimationListener listener) {
430            if (v == null || anim == null) {
431                return;
432            }
433            mOrignalListener = listener;
434            mView = v;
435        }
436
437        @Override
438        @CallSuper
439        public void onAnimationStart(Animation animation) {
440            if (mView != null) {
441                mShouldRunOnHWLayer = shouldRunOnHWLayer(mView, animation);
442                if (mShouldRunOnHWLayer) {
443                    mView.post(new Runnable() {
444                        @Override
445                        public void run() {
446                            ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_HARDWARE, null);
447                        }
448                    });
449                }
450            }
451            if (mOrignalListener != null) {
452                mOrignalListener.onAnimationStart(animation);
453            }
454        }
455
456        @Override
457        @CallSuper
458        public void onAnimationEnd(Animation animation) {
459            if (mView != null && mShouldRunOnHWLayer) {
460                mView.post(new Runnable() {
461                    @Override
462                    public void run() {
463                        ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_NONE, null);
464                    }
465                });
466            }
467            if (mOrignalListener != null) {
468                mOrignalListener.onAnimationEnd(animation);
469            }
470        }
471
472        @Override
473        public void onAnimationRepeat(Animation animation) {
474            if (mOrignalListener != null) {
475                mOrignalListener.onAnimationRepeat(animation);
476            }
477        }
478    }
479
480    ArrayList<Runnable> mPendingActions;
481    Runnable[] mTmpActions;
482    boolean mExecutingActions;
483
484    ArrayList<Fragment> mActive;
485    ArrayList<Fragment> mAdded;
486    ArrayList<Integer> mAvailIndices;
487    ArrayList<BackStackRecord> mBackStack;
488    ArrayList<Fragment> mCreatedMenus;
489
490    // Must be accessed while locked.
491    ArrayList<BackStackRecord> mBackStackIndices;
492    ArrayList<Integer> mAvailBackStackIndices;
493
494    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
495
496    int mCurState = Fragment.INITIALIZING;
497    FragmentHostCallback mHost;
498    FragmentController mController;
499    FragmentContainer mContainer;
500    Fragment mParent;
501
502    static Field sAnimationListenerField = null;
503
504    boolean mNeedMenuInvalidate;
505    boolean mStateSaved;
506    boolean mDestroyed;
507    String mNoTransactionsBecause;
508    boolean mHavePendingDeferredStart;
509
510    // Temporary vars for state save and restore.
511    Bundle mStateBundle = null;
512    SparseArray<Parcelable> mStateArray = null;
513
514    Runnable mExecCommit = new Runnable() {
515        @Override
516        public void run() {
517            execPendingActions();
518        }
519    };
520
521    static boolean modifiesAlpha(Animation anim) {
522        if (anim instanceof AlphaAnimation) {
523            return true;
524        } else if (anim instanceof AnimationSet) {
525            List<Animation> anims = ((AnimationSet) anim).getAnimations();
526            for (int i = 0; i < anims.size(); i++) {
527                if (anims.get(i) instanceof AlphaAnimation) {
528                    return true;
529                }
530            }
531        }
532        return false;
533    }
534
535    static boolean shouldRunOnHWLayer(View v, Animation anim) {
536        return Build.VERSION.SDK_INT >= 19
537                && ViewCompat.getLayerType(v) == ViewCompat.LAYER_TYPE_NONE
538                && ViewCompat.hasOverlappingRendering(v)
539                && modifiesAlpha(anim);
540    }
541
542    private void throwException(RuntimeException ex) {
543        Log.e(TAG, ex.getMessage());
544        Log.e(TAG, "Activity state:");
545        LogWriter logw = new LogWriter(TAG);
546        PrintWriter pw = new PrintWriter(logw);
547        if (mHost != null) {
548            try {
549                mHost.onDump("  ", null, pw, new String[] { });
550            } catch (Exception e) {
551                Log.e(TAG, "Failed dumping state", e);
552            }
553        } else {
554            try {
555                dump("  ", null, pw, new String[] { });
556            } catch (Exception e) {
557                Log.e(TAG, "Failed dumping state", e);
558            }
559        }
560        throw ex;
561    }
562
563    @Override
564    public FragmentTransaction beginTransaction() {
565        return new BackStackRecord(this);
566    }
567
568    @Override
569    public boolean executePendingTransactions() {
570        return execPendingActions();
571    }
572
573    @Override
574    public void popBackStack() {
575        enqueueAction(new Runnable() {
576            @Override public void run() {
577                popBackStackState(mHost.getHandler(), null, -1, 0);
578            }
579        }, false);
580    }
581
582    @Override
583    public boolean popBackStackImmediate() {
584        checkStateLoss();
585        executePendingTransactions();
586        return popBackStackState(mHost.getHandler(), null, -1, 0);
587    }
588
589    @Override
590    public void popBackStack(final String name, final int flags) {
591        enqueueAction(new Runnable() {
592            @Override public void run() {
593                popBackStackState(mHost.getHandler(), name, -1, flags);
594            }
595        }, false);
596    }
597
598    @Override
599    public boolean popBackStackImmediate(String name, int flags) {
600        checkStateLoss();
601        executePendingTransactions();
602        return popBackStackState(mHost.getHandler(), name, -1, flags);
603    }
604
605    @Override
606    public void popBackStack(final int id, final int flags) {
607        if (id < 0) {
608            throw new IllegalArgumentException("Bad id: " + id);
609        }
610        enqueueAction(new Runnable() {
611            @Override public void run() {
612                popBackStackState(mHost.getHandler(), null, id, flags);
613            }
614        }, false);
615    }
616
617    @Override
618    public boolean popBackStackImmediate(int id, int flags) {
619        checkStateLoss();
620        executePendingTransactions();
621        if (id < 0) {
622            throw new IllegalArgumentException("Bad id: " + id);
623        }
624        return popBackStackState(mHost.getHandler(), null, id, flags);
625    }
626
627    @Override
628    public int getBackStackEntryCount() {
629        return mBackStack != null ? mBackStack.size() : 0;
630    }
631
632    @Override
633    public BackStackEntry getBackStackEntryAt(int index) {
634        return mBackStack.get(index);
635    }
636
637    @Override
638    public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
639        if (mBackStackChangeListeners == null) {
640            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
641        }
642        mBackStackChangeListeners.add(listener);
643    }
644
645    @Override
646    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
647        if (mBackStackChangeListeners != null) {
648            mBackStackChangeListeners.remove(listener);
649        }
650    }
651
652    @Override
653    public void putFragment(Bundle bundle, String key, Fragment fragment) {
654        if (fragment.mIndex < 0) {
655            throwException(new IllegalStateException("Fragment " + fragment
656                    + " is not currently in the FragmentManager"));
657        }
658        bundle.putInt(key, fragment.mIndex);
659    }
660
661    @Override
662    public Fragment getFragment(Bundle bundle, String key) {
663        int index = bundle.getInt(key, -1);
664        if (index == -1) {
665            return null;
666        }
667        if (index >= mActive.size()) {
668            throwException(new IllegalStateException("Fragment no longer exists for key "
669                    + key + ": index " + index));
670        }
671        Fragment f = mActive.get(index);
672        if (f == null) {
673            throwException(new IllegalStateException("Fragment no longer exists for key "
674                    + key + ": index " + index));
675        }
676        return f;
677    }
678
679    @Override
680    public List<Fragment> getFragments() {
681        return mActive;
682    }
683
684    @Override
685    public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) {
686        if (fragment.mIndex < 0) {
687            throwException( new IllegalStateException("Fragment " + fragment
688                    + " is not currently in the FragmentManager"));
689        }
690        if (fragment.mState > Fragment.INITIALIZING) {
691            Bundle result = saveFragmentBasicState(fragment);
692            return result != null ? new Fragment.SavedState(result) : null;
693        }
694        return null;
695    }
696
697    @Override
698    public boolean isDestroyed() {
699        return mDestroyed;
700    }
701
702    @Override
703    public String toString() {
704        StringBuilder sb = new StringBuilder(128);
705        sb.append("FragmentManager{");
706        sb.append(Integer.toHexString(System.identityHashCode(this)));
707        sb.append(" in ");
708        if (mParent != null) {
709            DebugUtils.buildShortClassTag(mParent, sb);
710        } else {
711            DebugUtils.buildShortClassTag(mHost, sb);
712        }
713        sb.append("}}");
714        return sb.toString();
715    }
716
717    @Override
718    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
719        String innerPrefix = prefix + "    ";
720
721        int N;
722        if (mActive != null) {
723            N = mActive.size();
724            if (N > 0) {
725                writer.print(prefix); writer.print("Active Fragments in ");
726                        writer.print(Integer.toHexString(System.identityHashCode(this)));
727                        writer.println(":");
728                for (int i=0; i<N; i++) {
729                    Fragment f = mActive.get(i);
730                    writer.print(prefix); writer.print("  #"); writer.print(i);
731                            writer.print(": "); writer.println(f);
732                    if (f != null) {
733                        f.dump(innerPrefix, fd, writer, args);
734                    }
735                }
736            }
737        }
738
739        if (mAdded != null) {
740            N = mAdded.size();
741            if (N > 0) {
742                writer.print(prefix); writer.println("Added Fragments:");
743                for (int i=0; i<N; i++) {
744                    Fragment f = mAdded.get(i);
745                    writer.print(prefix); writer.print("  #"); writer.print(i);
746                            writer.print(": "); writer.println(f.toString());
747                }
748            }
749        }
750
751        if (mCreatedMenus != null) {
752            N = mCreatedMenus.size();
753            if (N > 0) {
754                writer.print(prefix); writer.println("Fragments Created Menus:");
755                for (int i=0; i<N; i++) {
756                    Fragment f = mCreatedMenus.get(i);
757                    writer.print(prefix); writer.print("  #"); writer.print(i);
758                            writer.print(": "); writer.println(f.toString());
759                }
760            }
761        }
762
763        if (mBackStack != null) {
764            N = mBackStack.size();
765            if (N > 0) {
766                writer.print(prefix); writer.println("Back Stack:");
767                for (int i=0; i<N; i++) {
768                    BackStackRecord bs = mBackStack.get(i);
769                    writer.print(prefix); writer.print("  #"); writer.print(i);
770                            writer.print(": "); writer.println(bs.toString());
771                    bs.dump(innerPrefix, fd, writer, args);
772                }
773            }
774        }
775
776        synchronized (this) {
777            if (mBackStackIndices != null) {
778                N = mBackStackIndices.size();
779                if (N > 0) {
780                    writer.print(prefix); writer.println("Back Stack Indices:");
781                    for (int i=0; i<N; i++) {
782                        BackStackRecord bs = mBackStackIndices.get(i);
783                        writer.print(prefix); writer.print("  #"); writer.print(i);
784                                writer.print(": "); writer.println(bs);
785                    }
786                }
787            }
788
789            if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) {
790                writer.print(prefix); writer.print("mAvailBackStackIndices: ");
791                        writer.println(Arrays.toString(mAvailBackStackIndices.toArray()));
792            }
793        }
794
795        if (mPendingActions != null) {
796            N = mPendingActions.size();
797            if (N > 0) {
798                writer.print(prefix); writer.println("Pending Actions:");
799                for (int i=0; i<N; i++) {
800                    Runnable r = mPendingActions.get(i);
801                    writer.print(prefix); writer.print("  #"); writer.print(i);
802                            writer.print(": "); writer.println(r);
803                }
804            }
805        }
806
807        writer.print(prefix); writer.println("FragmentManager misc state:");
808        writer.print(prefix); writer.print("  mHost="); writer.println(mHost);
809        writer.print(prefix); writer.print("  mContainer="); writer.println(mContainer);
810        if (mParent != null) {
811            writer.print(prefix); writer.print("  mParent="); writer.println(mParent);
812        }
813        writer.print(prefix); writer.print("  mCurState="); writer.print(mCurState);
814                writer.print(" mStateSaved="); writer.print(mStateSaved);
815                writer.print(" mDestroyed="); writer.println(mDestroyed);
816        if (mNeedMenuInvalidate) {
817            writer.print(prefix); writer.print("  mNeedMenuInvalidate=");
818                    writer.println(mNeedMenuInvalidate);
819        }
820        if (mNoTransactionsBecause != null) {
821            writer.print(prefix); writer.print("  mNoTransactionsBecause=");
822                    writer.println(mNoTransactionsBecause);
823        }
824        if (mAvailIndices != null && mAvailIndices.size() > 0) {
825            writer.print(prefix); writer.print("  mAvailIndices: ");
826                    writer.println(Arrays.toString(mAvailIndices.toArray()));
827        }
828    }
829
830    static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
831    static final Interpolator DECELERATE_CUBIC = new DecelerateInterpolator(1.5f);
832    static final Interpolator ACCELERATE_QUINT = new AccelerateInterpolator(2.5f);
833    static final Interpolator ACCELERATE_CUBIC = new AccelerateInterpolator(1.5f);
834
835    static final int ANIM_DUR = 220;
836
837    static Animation makeOpenCloseAnimation(Context context, float startScale,
838            float endScale, float startAlpha, float endAlpha) {
839        AnimationSet set = new AnimationSet(false);
840        ScaleAnimation scale = new ScaleAnimation(startScale, endScale, startScale, endScale,
841                Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f);
842        scale.setInterpolator(DECELERATE_QUINT);
843        scale.setDuration(ANIM_DUR);
844        set.addAnimation(scale);
845        AlphaAnimation alpha = new AlphaAnimation(startAlpha, endAlpha);
846        alpha.setInterpolator(DECELERATE_CUBIC);
847        alpha.setDuration(ANIM_DUR);
848        set.addAnimation(alpha);
849        return set;
850    }
851
852    static Animation makeFadeAnimation(Context context, float start, float end) {
853        AlphaAnimation anim = new AlphaAnimation(start, end);
854        anim.setInterpolator(DECELERATE_CUBIC);
855        anim.setDuration(ANIM_DUR);
856        return anim;
857    }
858
859    Animation loadAnimation(Fragment fragment, int transit, boolean enter,
860            int transitionStyle) {
861        Animation animObj = fragment.onCreateAnimation(transit, enter,
862                fragment.mNextAnim);
863        if (animObj != null) {
864            return animObj;
865        }
866
867        if (fragment.mNextAnim != 0) {
868            Animation anim = AnimationUtils.loadAnimation(mHost.getContext(), fragment.mNextAnim);
869            if (anim != null) {
870                return anim;
871            }
872        }
873
874        if (transit == 0) {
875            return null;
876        }
877
878        int styleIndex = transitToStyleIndex(transit, enter);
879        if (styleIndex < 0) {
880            return null;
881        }
882
883        switch (styleIndex) {
884            case ANIM_STYLE_OPEN_ENTER:
885                return makeOpenCloseAnimation(mHost.getContext(), 1.125f, 1.0f, 0, 1);
886            case ANIM_STYLE_OPEN_EXIT:
887                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, .975f, 1, 0);
888            case ANIM_STYLE_CLOSE_ENTER:
889                return makeOpenCloseAnimation(mHost.getContext(), .975f, 1.0f, 0, 1);
890            case ANIM_STYLE_CLOSE_EXIT:
891                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, 1.075f, 1, 0);
892            case ANIM_STYLE_FADE_ENTER:
893                return makeFadeAnimation(mHost.getContext(), 0, 1);
894            case ANIM_STYLE_FADE_EXIT:
895                return makeFadeAnimation(mHost.getContext(), 1, 0);
896        }
897
898        if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
899            transitionStyle = mHost.onGetWindowAnimations();
900        }
901        if (transitionStyle == 0) {
902            return null;
903        }
904
905        //TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
906        //        com.android.internal.R.styleable.FragmentAnimation);
907        //int anim = attrs.getResourceId(styleIndex, 0);
908        //attrs.recycle();
909
910        //if (anim == 0) {
911        //    return null;
912        //}
913
914        //return AnimatorInflater.loadAnimator(mActivity, anim);
915        return null;
916    }
917
918    public void performPendingDeferredStart(Fragment f) {
919        if (f.mDeferStart) {
920            if (mExecutingActions) {
921                // Wait until we're done executing our pending transactions
922                mHavePendingDeferredStart = true;
923                return;
924            }
925            f.mDeferStart = false;
926            moveToState(f, mCurState, 0, 0, false);
927        }
928    }
929
930    /**
931     * Sets the to be animated view on hardware layer during the animation. Note
932     * that calling this will replace any existing animation listener on the animation
933     * with a new one, as animations do not support more than one listeners. Therefore,
934     * animations that already have listeners should do the layer change operations
935     * in their existing listeners, rather than calling this function.
936     */
937    private void setHWLayerAnimListenerIfAlpha(final View v, Animation anim) {
938        if (v == null || anim == null) {
939            return;
940        }
941        if (shouldRunOnHWLayer(v, anim)) {
942            AnimationListener originalListener = null;
943            try {
944                if (sAnimationListenerField == null) {
945                    sAnimationListenerField = Animation.class.getDeclaredField("mListener");
946                    sAnimationListenerField.setAccessible(true);
947                }
948                originalListener = (AnimationListener) sAnimationListenerField.get(anim);
949            } catch (NoSuchFieldException e) {
950                Log.e(TAG, "No field with the name mListener is found in Animation class", e);
951            } catch (IllegalAccessException e) {
952                Log.e(TAG, "Cannot access Animation's mListener field", e);
953            }
954            // If there's already a listener set on the animation, we need wrap the new listener
955            // around the existing listener, so that they will both get animation listener
956            // callbacks.
957            anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, anim,
958                    originalListener));
959        }
960    }
961
962    void moveToState(Fragment f, int newState, int transit, int transitionStyle,
963            boolean keepActive) {
964        // Fragments that are not currently added will sit in the onCreate() state.
965        if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
966            newState = Fragment.CREATED;
967        }
968        if (f.mRemoving && newState > f.mState) {
969            // While removing a fragment, we can't change it to a higher state.
970            newState = f.mState;
971        }
972        // Defer start if requested; don't allow it to move to STARTED or higher
973        // if it's not already started.
974        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
975            newState = Fragment.STOPPED;
976        }
977        if (f.mState < newState) {
978            // For fragments that are created from a layout, when restoring from
979            // state we don't want to allow them to be created until they are
980            // being reloaded from the layout.
981            if (f.mFromLayout && !f.mInLayout) {
982                return;
983            }
984            if (f.mAnimatingAway != null) {
985                // The fragment is currently being animated...  but!  Now we
986                // want to move our state back up.  Give up on waiting for the
987                // animation, move to whatever the final state should be once
988                // the animation is done, and then we can proceed from there.
989                f.mAnimatingAway = null;
990                moveToState(f, f.mStateAfterAnimating, 0, 0, true);
991            }
992            switch (f.mState) {
993                case Fragment.INITIALIZING:
994                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
995                    if (f.mSavedFragmentState != null) {
996                        f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
997                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
998                                FragmentManagerImpl.VIEW_STATE_TAG);
999                        f.mTarget = getFragment(f.mSavedFragmentState,
1000                                FragmentManagerImpl.TARGET_STATE_TAG);
1001                        if (f.mTarget != null) {
1002                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
1003                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
1004                        }
1005                        f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
1006                                FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
1007                        if (!f.mUserVisibleHint) {
1008                            f.mDeferStart = true;
1009                            if (newState > Fragment.STOPPED) {
1010                                newState = Fragment.STOPPED;
1011                            }
1012                        }
1013                    }
1014                    f.mHost = mHost;
1015                    f.mParentFragment = mParent;
1016                    f.mFragmentManager = mParent != null
1017                            ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
1018                    f.mCalled = false;
1019                    f.onAttach(mHost.getContext());
1020                    if (!f.mCalled) {
1021                        throw new SuperNotCalledException("Fragment " + f
1022                                + " did not call through to super.onAttach()");
1023                    }
1024                    if (f.mParentFragment == null) {
1025                        mHost.onAttachFragment(f);
1026                    }
1027
1028                    if (!f.mRetaining) {
1029                        f.performCreate(f.mSavedFragmentState);
1030                    }
1031                    f.mRetaining = false;
1032                    if (f.mFromLayout) {
1033                        // For fragments that are part of the content view
1034                        // layout, we need to instantiate the view immediately
1035                        // and the inflater will take care of adding it.
1036                        f.mView = f.performCreateView(f.getLayoutInflater(
1037                                f.mSavedFragmentState), null, f.mSavedFragmentState);
1038                        if (f.mView != null) {
1039                            f.mInnerView = f.mView;
1040                            if (Build.VERSION.SDK_INT >= 11) {
1041                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
1042                            } else {
1043                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
1044                            }
1045                            if (f.mHidden) f.mView.setVisibility(View.GONE);
1046                            f.onViewCreated(f.mView, f.mSavedFragmentState);
1047                        } else {
1048                            f.mInnerView = null;
1049                        }
1050                    }
1051                case Fragment.CREATED:
1052                    if (newState > Fragment.CREATED) {
1053                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
1054                        if (!f.mFromLayout) {
1055                            ViewGroup container = null;
1056                            if (f.mContainerId != 0) {
1057                                container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
1058                                if (container == null && !f.mRestored) {
1059                                    throwException(new IllegalArgumentException(
1060                                            "No view found for id 0x"
1061                                            + Integer.toHexString(f.mContainerId) + " ("
1062                                            + f.getResources().getResourceName(f.mContainerId)
1063                                            + ") for fragment " + f));
1064                                }
1065                            }
1066                            f.mContainer = container;
1067                            f.mView = f.performCreateView(f.getLayoutInflater(
1068                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
1069                            if (f.mView != null) {
1070                                f.mInnerView = f.mView;
1071                                if (Build.VERSION.SDK_INT >= 11) {
1072                                    ViewCompat.setSaveFromParentEnabled(f.mView, false);
1073                                } else {
1074                                    f.mView = NoSaveStateFrameLayout.wrap(f.mView);
1075                                }
1076                                if (container != null) {
1077                                    Animation anim = loadAnimation(f, transit, true,
1078                                            transitionStyle);
1079                                    if (anim != null) {
1080                                        setHWLayerAnimListenerIfAlpha(f.mView, anim);
1081                                        f.mView.startAnimation(anim);
1082                                    }
1083                                    container.addView(f.mView);
1084                                }
1085                                if (f.mHidden) f.mView.setVisibility(View.GONE);
1086                                f.onViewCreated(f.mView, f.mSavedFragmentState);
1087                            } else {
1088                                f.mInnerView = null;
1089                            }
1090                        }
1091
1092                        f.performActivityCreated(f.mSavedFragmentState);
1093                        if (f.mView != null) {
1094                            f.restoreViewState(f.mSavedFragmentState);
1095                        }
1096                        f.mSavedFragmentState = null;
1097                    }
1098                case Fragment.ACTIVITY_CREATED:
1099                case Fragment.STOPPED:
1100                    if (newState > Fragment.STOPPED) {
1101                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
1102                        f.performStart();
1103                    }
1104                case Fragment.STARTED:
1105                    if (newState > Fragment.STARTED) {
1106                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
1107                        f.performResume();
1108                        f.mSavedFragmentState = null;
1109                        f.mSavedViewState = null;
1110                    }
1111            }
1112        } else if (f.mState > newState) {
1113            switch (f.mState) {
1114                case Fragment.RESUMED:
1115                    if (newState < Fragment.RESUMED) {
1116                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
1117                        f.performPause();
1118                    }
1119                case Fragment.STARTED:
1120                    if (newState < Fragment.STARTED) {
1121                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
1122                        f.performStop();
1123                    }
1124                case Fragment.STOPPED:
1125                    if (newState < Fragment.STOPPED) {
1126                        if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
1127                        f.performReallyStop();
1128                    }
1129                case Fragment.ACTIVITY_CREATED:
1130                    if (newState < Fragment.ACTIVITY_CREATED) {
1131                        if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
1132                        if (f.mView != null) {
1133                            // Need to save the current view state if not
1134                            // done already.
1135                            if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
1136                                saveFragmentViewState(f);
1137                            }
1138                        }
1139                        f.performDestroyView();
1140                        if (f.mView != null && f.mContainer != null) {
1141                            Animation anim = null;
1142                            if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
1143                                anim = loadAnimation(f, transit, false,
1144                                        transitionStyle);
1145                            }
1146                            if (anim != null) {
1147                                final Fragment fragment = f;
1148                                f.mAnimatingAway = f.mView;
1149                                f.mStateAfterAnimating = newState;
1150                                final View viewToAnimate = f.mView;
1151                                anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
1152                                        viewToAnimate, anim) {
1153                                    @Override
1154                                    public void onAnimationEnd(Animation animation) {
1155                                        super.onAnimationEnd(animation);
1156                                        if (fragment.mAnimatingAway != null) {
1157                                            fragment.mAnimatingAway = null;
1158                                            moveToState(fragment, fragment.mStateAfterAnimating,
1159                                                    0, 0, false);
1160                                        }
1161                                    }
1162                                });
1163                                f.mView.startAnimation(anim);
1164                            }
1165                            f.mContainer.removeView(f.mView);
1166                        }
1167                        f.mContainer = null;
1168                        f.mView = null;
1169                        f.mInnerView = null;
1170                    }
1171                case Fragment.CREATED:
1172                    if (newState < Fragment.CREATED) {
1173                        if (mDestroyed) {
1174                            if (f.mAnimatingAway != null) {
1175                                // The fragment's containing activity is
1176                                // being destroyed, but this fragment is
1177                                // currently animating away.  Stop the
1178                                // animation right now -- it is not needed,
1179                                // and we can't wait any more on destroying
1180                                // the fragment.
1181                                View v = f.mAnimatingAway;
1182                                f.mAnimatingAway = null;
1183                                v.clearAnimation();
1184                            }
1185                        }
1186                        if (f.mAnimatingAway != null) {
1187                            // We are waiting for the fragment's view to finish
1188                            // animating away.  Just make a note of the state
1189                            // the fragment now should move to once the animation
1190                            // is done.
1191                            f.mStateAfterAnimating = newState;
1192                            newState = Fragment.CREATED;
1193                        } else {
1194                            if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
1195                            if (!f.mRetaining) {
1196                                f.performDestroy();
1197                            } else {
1198                                f.mState = Fragment.INITIALIZING;
1199                            }
1200
1201                            f.mCalled = false;
1202                            f.onDetach();
1203                            if (!f.mCalled) {
1204                                throw new SuperNotCalledException("Fragment " + f
1205                                        + " did not call through to super.onDetach()");
1206                            }
1207                            if (!keepActive) {
1208                                if (!f.mRetaining) {
1209                                    makeInactive(f);
1210                                } else {
1211                                    f.mHost = null;
1212                                    f.mParentFragment = null;
1213                                    f.mFragmentManager = null;
1214                                    f.mChildFragmentManager = null;
1215                                }
1216                            }
1217                        }
1218                    }
1219            }
1220        }
1221
1222        if (f.mState != newState) {
1223            Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
1224                    + "expected state " + newState + " found " + f.mState);
1225            f.mState = newState;
1226        }
1227    }
1228
1229    void moveToState(Fragment f) {
1230        moveToState(f, mCurState, 0, 0, false);
1231    }
1232
1233    void moveToState(int newState, boolean always) {
1234        moveToState(newState, 0, 0, always);
1235    }
1236
1237    void moveToState(int newState, int transit, int transitStyle, boolean always) {
1238        if (mHost == null && newState != Fragment.INITIALIZING) {
1239            throw new IllegalStateException("No host");
1240        }
1241
1242        if (!always && mCurState == newState) {
1243            return;
1244        }
1245
1246        mCurState = newState;
1247        if (mActive != null) {
1248            boolean loadersRunning = false;
1249            for (int i=0; i<mActive.size(); i++) {
1250                Fragment f = mActive.get(i);
1251                if (f != null) {
1252                    moveToState(f, newState, transit, transitStyle, false);
1253                    if (f.mLoaderManager != null) {
1254                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1255                    }
1256                }
1257            }
1258
1259            if (!loadersRunning) {
1260                startPendingDeferredFragments();
1261            }
1262
1263            if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
1264                mHost.onSupportInvalidateOptionsMenu();
1265                mNeedMenuInvalidate = false;
1266            }
1267        }
1268    }
1269
1270    void startPendingDeferredFragments() {
1271        if (mActive == null) return;
1272
1273        for (int i=0; i<mActive.size(); i++) {
1274            Fragment f = mActive.get(i);
1275            if (f != null) {
1276                performPendingDeferredStart(f);
1277            }
1278        }
1279    }
1280
1281    void makeActive(Fragment f) {
1282        if (f.mIndex >= 0) {
1283            return;
1284        }
1285
1286        if (mAvailIndices == null || mAvailIndices.size() <= 0) {
1287            if (mActive == null) {
1288                mActive = new ArrayList<Fragment>();
1289            }
1290            f.setIndex(mActive.size(), mParent);
1291            mActive.add(f);
1292
1293        } else {
1294            f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
1295            mActive.set(f.mIndex, f);
1296        }
1297        if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
1298    }
1299
1300    void makeInactive(Fragment f) {
1301        if (f.mIndex < 0) {
1302            return;
1303        }
1304
1305        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
1306        mActive.set(f.mIndex, null);
1307        if (mAvailIndices == null) {
1308            mAvailIndices = new ArrayList<Integer>();
1309        }
1310        mAvailIndices.add(f.mIndex);
1311        mHost.inactivateFragment(f.mWho);
1312        f.initState();
1313    }
1314
1315    public void addFragment(Fragment fragment, boolean moveToStateNow) {
1316        if (mAdded == null) {
1317            mAdded = new ArrayList<Fragment>();
1318        }
1319        if (DEBUG) Log.v(TAG, "add: " + fragment);
1320        makeActive(fragment);
1321        if (!fragment.mDetached) {
1322            if (mAdded.contains(fragment)) {
1323                throw new IllegalStateException("Fragment already added: " + fragment);
1324            }
1325            mAdded.add(fragment);
1326            fragment.mAdded = true;
1327            fragment.mRemoving = false;
1328            if (fragment.mHasMenu && fragment.mMenuVisible) {
1329                mNeedMenuInvalidate = true;
1330            }
1331            if (moveToStateNow) {
1332                moveToState(fragment);
1333            }
1334        }
1335    }
1336
1337    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
1338        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
1339        final boolean inactive = !fragment.isInBackStack();
1340        if (!fragment.mDetached || inactive) {
1341            if (mAdded != null) {
1342                mAdded.remove(fragment);
1343            }
1344            if (fragment.mHasMenu && fragment.mMenuVisible) {
1345                mNeedMenuInvalidate = true;
1346            }
1347            fragment.mAdded = false;
1348            fragment.mRemoving = true;
1349            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
1350                    transition, transitionStyle, false);
1351        }
1352    }
1353
1354    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
1355        if (DEBUG) Log.v(TAG, "hide: " + fragment);
1356        if (!fragment.mHidden) {
1357            fragment.mHidden = true;
1358            if (fragment.mView != null) {
1359                Animation anim = loadAnimation(fragment, transition, false,
1360                        transitionStyle);
1361                if (anim != null) {
1362                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1363                    fragment.mView.startAnimation(anim);
1364                }
1365                fragment.mView.setVisibility(View.GONE);
1366            }
1367            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
1368                mNeedMenuInvalidate = true;
1369            }
1370            fragment.onHiddenChanged(true);
1371        }
1372    }
1373
1374    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
1375        if (DEBUG) Log.v(TAG, "show: " + fragment);
1376        if (fragment.mHidden) {
1377            fragment.mHidden = false;
1378            if (fragment.mView != null) {
1379                Animation anim = loadAnimation(fragment, transition, true,
1380                        transitionStyle);
1381                if (anim != null) {
1382                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1383                    fragment.mView.startAnimation(anim);
1384                }
1385                fragment.mView.setVisibility(View.VISIBLE);
1386            }
1387            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
1388                mNeedMenuInvalidate = true;
1389            }
1390            fragment.onHiddenChanged(false);
1391        }
1392    }
1393
1394    public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
1395        if (DEBUG) Log.v(TAG, "detach: " + fragment);
1396        if (!fragment.mDetached) {
1397            fragment.mDetached = true;
1398            if (fragment.mAdded) {
1399                // We are not already in back stack, so need to remove the fragment.
1400                if (mAdded != null) {
1401                    if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
1402                    mAdded.remove(fragment);
1403                }
1404                if (fragment.mHasMenu && fragment.mMenuVisible) {
1405                    mNeedMenuInvalidate = true;
1406                }
1407                fragment.mAdded = false;
1408                moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
1409            }
1410        }
1411    }
1412
1413    public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
1414        if (DEBUG) Log.v(TAG, "attach: " + fragment);
1415        if (fragment.mDetached) {
1416            fragment.mDetached = false;
1417            if (!fragment.mAdded) {
1418                if (mAdded == null) {
1419                    mAdded = new ArrayList<Fragment>();
1420                }
1421                if (mAdded.contains(fragment)) {
1422                    throw new IllegalStateException("Fragment already added: " + fragment);
1423                }
1424                if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
1425                mAdded.add(fragment);
1426                fragment.mAdded = true;
1427                if (fragment.mHasMenu && fragment.mMenuVisible) {
1428                    mNeedMenuInvalidate = true;
1429                }
1430                moveToState(fragment, mCurState, transition, transitionStyle, false);
1431            }
1432        }
1433    }
1434
1435    public Fragment findFragmentById(int id) {
1436        if (mAdded != null) {
1437            // First look through added fragments.
1438            for (int i=mAdded.size()-1; i>=0; i--) {
1439                Fragment f = mAdded.get(i);
1440                if (f != null && f.mFragmentId == id) {
1441                    return f;
1442                }
1443            }
1444        }
1445        if (mActive != null) {
1446            // Now for any known fragment.
1447            for (int i=mActive.size()-1; i>=0; i--) {
1448                Fragment f = mActive.get(i);
1449                if (f != null && f.mFragmentId == id) {
1450                    return f;
1451                }
1452            }
1453        }
1454        return null;
1455    }
1456
1457    public Fragment findFragmentByTag(String tag) {
1458        if (mAdded != null && tag != null) {
1459            // First look through added fragments.
1460            for (int i=mAdded.size()-1; i>=0; i--) {
1461                Fragment f = mAdded.get(i);
1462                if (f != null && tag.equals(f.mTag)) {
1463                    return f;
1464                }
1465            }
1466        }
1467        if (mActive != null && tag != null) {
1468            // Now for any known fragment.
1469            for (int i=mActive.size()-1; i>=0; i--) {
1470                Fragment f = mActive.get(i);
1471                if (f != null && tag.equals(f.mTag)) {
1472                    return f;
1473                }
1474            }
1475        }
1476        return null;
1477    }
1478
1479    public Fragment findFragmentByWho(String who) {
1480        if (mActive != null && who != null) {
1481            for (int i=mActive.size()-1; i>=0; i--) {
1482                Fragment f = mActive.get(i);
1483                if (f != null && (f=f.findFragmentByWho(who)) != null) {
1484                    return f;
1485                }
1486            }
1487        }
1488        return null;
1489    }
1490
1491    private void checkStateLoss() {
1492        if (mStateSaved) {
1493            throw new IllegalStateException(
1494                    "Can not perform this action after onSaveInstanceState");
1495        }
1496        if (mNoTransactionsBecause != null) {
1497            throw new IllegalStateException(
1498                    "Can not perform this action inside of " + mNoTransactionsBecause);
1499        }
1500    }
1501
1502    /**
1503     * Adds an action to the queue of pending actions.
1504     *
1505     * @param action the action to add
1506     * @param allowStateLoss whether to allow loss of state information
1507     * @throws IllegalStateException if the activity has been destroyed
1508     */
1509    public void enqueueAction(Runnable action, boolean allowStateLoss) {
1510        if (!allowStateLoss) {
1511            checkStateLoss();
1512        }
1513        synchronized (this) {
1514            if (mDestroyed || mHost == null) {
1515                throw new IllegalStateException("Activity has been destroyed");
1516            }
1517            if (mPendingActions == null) {
1518                mPendingActions = new ArrayList<Runnable>();
1519            }
1520            mPendingActions.add(action);
1521            if (mPendingActions.size() == 1) {
1522                mHost.getHandler().removeCallbacks(mExecCommit);
1523                mHost.getHandler().post(mExecCommit);
1524            }
1525        }
1526    }
1527
1528    public int allocBackStackIndex(BackStackRecord bse) {
1529        synchronized (this) {
1530            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
1531                if (mBackStackIndices == null) {
1532                    mBackStackIndices = new ArrayList<BackStackRecord>();
1533                }
1534                int index = mBackStackIndices.size();
1535                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
1536                mBackStackIndices.add(bse);
1537                return index;
1538
1539            } else {
1540                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
1541                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
1542                mBackStackIndices.set(index, bse);
1543                return index;
1544            }
1545        }
1546    }
1547
1548    public void setBackStackIndex(int index, BackStackRecord bse) {
1549        synchronized (this) {
1550            if (mBackStackIndices == null) {
1551                mBackStackIndices = new ArrayList<BackStackRecord>();
1552            }
1553            int N = mBackStackIndices.size();
1554            if (index < N) {
1555                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
1556                mBackStackIndices.set(index, bse);
1557            } else {
1558                while (N < index) {
1559                    mBackStackIndices.add(null);
1560                    if (mAvailBackStackIndices == null) {
1561                        mAvailBackStackIndices = new ArrayList<Integer>();
1562                    }
1563                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
1564                    mAvailBackStackIndices.add(N);
1565                    N++;
1566                }
1567                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
1568                mBackStackIndices.add(bse);
1569            }
1570        }
1571    }
1572
1573    public void freeBackStackIndex(int index) {
1574        synchronized (this) {
1575            mBackStackIndices.set(index, null);
1576            if (mAvailBackStackIndices == null) {
1577                mAvailBackStackIndices = new ArrayList<Integer>();
1578            }
1579            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
1580            mAvailBackStackIndices.add(index);
1581        }
1582    }
1583
1584    public void execSingleAction(Runnable action, boolean allowStateLoss) {
1585        if (mExecutingActions) {
1586            throw new IllegalStateException("FragmentManager is already executing transactions");
1587        }
1588
1589        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
1590            throw new IllegalStateException("Must be called from main thread of fragment host");
1591        }
1592
1593        if (allowStateLoss) {
1594            checkStateLoss();
1595        }
1596
1597        mExecutingActions = true;
1598        action.run();
1599        mExecutingActions = false;
1600
1601        doPendingDeferredStart();
1602    }
1603
1604    /**
1605     * Only call from main thread!
1606     */
1607    public boolean execPendingActions() {
1608        if (mExecutingActions) {
1609            throw new IllegalStateException("FragmentManager is already executing transactions");
1610        }
1611
1612        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
1613            throw new IllegalStateException("Must be called from main thread of fragment host");
1614        }
1615
1616        boolean didSomething = false;
1617
1618        while (true) {
1619            int numActions;
1620
1621            synchronized (this) {
1622                if (mPendingActions == null || mPendingActions.size() == 0) {
1623                    break;
1624                }
1625
1626                numActions = mPendingActions.size();
1627                if (mTmpActions == null || mTmpActions.length < numActions) {
1628                    mTmpActions = new Runnable[numActions];
1629                }
1630                mPendingActions.toArray(mTmpActions);
1631                mPendingActions.clear();
1632                mHost.getHandler().removeCallbacks(mExecCommit);
1633            }
1634
1635            mExecutingActions = true;
1636            for (int i=0; i<numActions; i++) {
1637                mTmpActions[i].run();
1638                mTmpActions[i] = null;
1639            }
1640            mExecutingActions = false;
1641            didSomething = true;
1642        }
1643
1644        doPendingDeferredStart();
1645
1646        return didSomething;
1647    }
1648
1649    void doPendingDeferredStart() {
1650        if (mHavePendingDeferredStart) {
1651            boolean loadersRunning = false;
1652            for (int i = 0; i < mActive.size(); i++) {
1653                Fragment f = mActive.get(i);
1654                if (f != null && f.mLoaderManager != null) {
1655                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1656                }
1657            }
1658            if (!loadersRunning) {
1659                mHavePendingDeferredStart = false;
1660                startPendingDeferredFragments();
1661            }
1662        }
1663    }
1664
1665    void reportBackStackChanged() {
1666        if (mBackStackChangeListeners != null) {
1667            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
1668                mBackStackChangeListeners.get(i).onBackStackChanged();
1669            }
1670        }
1671    }
1672
1673    void addBackStackState(BackStackRecord state) {
1674        if (mBackStack == null) {
1675            mBackStack = new ArrayList<BackStackRecord>();
1676        }
1677        mBackStack.add(state);
1678        reportBackStackChanged();
1679    }
1680
1681    @SuppressWarnings("unused")
1682    boolean popBackStackState(Handler handler, String name, int id, int flags) {
1683        if (mBackStack == null) {
1684            return false;
1685        }
1686        if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
1687            int last = mBackStack.size()-1;
1688            if (last < 0) {
1689                return false;
1690            }
1691            final BackStackRecord bss = mBackStack.remove(last);
1692            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
1693            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
1694            if (mCurState >= Fragment.CREATED) {
1695                bss.calculateBackFragments(firstOutFragments, lastInFragments);
1696            }
1697            bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
1698            reportBackStackChanged();
1699        } else {
1700            int index = -1;
1701            if (name != null || id >= 0) {
1702                // If a name or ID is specified, look for that place in
1703                // the stack.
1704                index = mBackStack.size()-1;
1705                while (index >= 0) {
1706                    BackStackRecord bss = mBackStack.get(index);
1707                    if (name != null && name.equals(bss.getName())) {
1708                        break;
1709                    }
1710                    if (id >= 0 && id == bss.mIndex) {
1711                        break;
1712                    }
1713                    index--;
1714                }
1715                if (index < 0) {
1716                    return false;
1717                }
1718                if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
1719                    index--;
1720                    // Consume all following entries that match.
1721                    while (index >= 0) {
1722                        BackStackRecord bss = mBackStack.get(index);
1723                        if ((name != null && name.equals(bss.getName()))
1724                                || (id >= 0 && id == bss.mIndex)) {
1725                            index--;
1726                            continue;
1727                        }
1728                        break;
1729                    }
1730                }
1731            }
1732            if (index == mBackStack.size()-1) {
1733                return false;
1734            }
1735            final ArrayList<BackStackRecord> states
1736                    = new ArrayList<BackStackRecord>();
1737            for (int i=mBackStack.size()-1; i>index; i--) {
1738                states.add(mBackStack.remove(i));
1739            }
1740            final int LAST = states.size()-1;
1741            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
1742            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
1743            if (mCurState >= Fragment.CREATED) {
1744                for (int i = 0; i <= LAST; i++) {
1745                    states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
1746                }
1747            }
1748            BackStackRecord.TransitionState state = null;
1749            for (int i=0; i<=LAST; i++) {
1750                if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
1751                state = states.get(i).popFromBackStack(i == LAST, state,
1752                        firstOutFragments, lastInFragments);
1753            }
1754            reportBackStackChanged();
1755        }
1756        return true;
1757    }
1758
1759    FragmentManagerNonConfig retainNonConfig() {
1760        ArrayList<Fragment> fragments = null;
1761        ArrayList<FragmentManagerNonConfig> childFragments = null;
1762        if (mActive != null) {
1763            for (int i=0; i<mActive.size(); i++) {
1764                Fragment f = mActive.get(i);
1765                if (f != null) {
1766                    if (f.mRetainInstance) {
1767                        if (fragments == null) {
1768                            fragments = new ArrayList<Fragment>();
1769                        }
1770                        fragments.add(f);
1771                        f.mRetaining = true;
1772                        f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
1773                        if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
1774                    }
1775                    boolean addedChild = false;
1776                    if (f.mChildFragmentManager != null) {
1777                        FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
1778                        if (child != null) {
1779                            if (childFragments == null) {
1780                                childFragments = new ArrayList<FragmentManagerNonConfig>();
1781                                for (int j = 0; j < i; j++) {
1782                                    childFragments.add(null);
1783                                }
1784                            }
1785                            childFragments.add(child);
1786                            addedChild = true;
1787                        }
1788                    }
1789                    if (childFragments != null && !addedChild) {
1790                        childFragments.add(null);
1791                    }
1792                }
1793            }
1794        }
1795        if (fragments == null && childFragments == null) {
1796            return null;
1797        }
1798        return new FragmentManagerNonConfig(fragments, childFragments);
1799    }
1800
1801    void saveFragmentViewState(Fragment f) {
1802        if (f.mInnerView == null) {
1803            return;
1804        }
1805        if (mStateArray == null) {
1806            mStateArray = new SparseArray<Parcelable>();
1807        } else {
1808            mStateArray.clear();
1809        }
1810        f.mInnerView.saveHierarchyState(mStateArray);
1811        if (mStateArray.size() > 0) {
1812            f.mSavedViewState = mStateArray;
1813            mStateArray = null;
1814        }
1815    }
1816
1817    Bundle saveFragmentBasicState(Fragment f) {
1818        Bundle result = null;
1819
1820        if (mStateBundle == null) {
1821            mStateBundle = new Bundle();
1822        }
1823        f.performSaveInstanceState(mStateBundle);
1824        if (!mStateBundle.isEmpty()) {
1825            result = mStateBundle;
1826            mStateBundle = null;
1827        }
1828
1829        if (f.mView != null) {
1830            saveFragmentViewState(f);
1831        }
1832        if (f.mSavedViewState != null) {
1833            if (result == null) {
1834                result = new Bundle();
1835            }
1836            result.putSparseParcelableArray(
1837                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
1838        }
1839        if (!f.mUserVisibleHint) {
1840            if (result == null) {
1841                result = new Bundle();
1842            }
1843            // Only add this if it's not the default value
1844            result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
1845        }
1846
1847        return result;
1848    }
1849
1850    Parcelable saveAllState() {
1851        // Make sure all pending operations have now been executed to get
1852        // our state update-to-date.
1853        execPendingActions();
1854
1855        if (HONEYCOMB) {
1856            // As of Honeycomb, we save state after pausing.  Prior to that
1857            // it is before pausing.  With fragments this is an issue, since
1858            // there are many things you may do after pausing but before
1859            // stopping that change the fragment state.  For those older
1860            // devices, we will not at this point say that we have saved
1861            // the state, so we will allow them to continue doing fragment
1862            // transactions.  This retains the same semantics as Honeycomb,
1863            // though you do have the risk of losing the very most recent state
1864            // if the process is killed...  we'll live with that.
1865            mStateSaved = true;
1866        }
1867
1868        if (mActive == null || mActive.size() <= 0) {
1869            return null;
1870        }
1871
1872        // First collect all active fragments.
1873        int N = mActive.size();
1874        FragmentState[] active = new FragmentState[N];
1875        boolean haveFragments = false;
1876        for (int i=0; i<N; i++) {
1877            Fragment f = mActive.get(i);
1878            if (f != null) {
1879                if (f.mIndex < 0) {
1880                    throwException(new IllegalStateException(
1881                            "Failure saving state: active " + f
1882                            + " has cleared index: " + f.mIndex));
1883                }
1884
1885                haveFragments = true;
1886
1887                FragmentState fs = new FragmentState(f);
1888                active[i] = fs;
1889
1890                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
1891                    fs.mSavedFragmentState = saveFragmentBasicState(f);
1892
1893                    if (f.mTarget != null) {
1894                        if (f.mTarget.mIndex < 0) {
1895                            throwException(new IllegalStateException(
1896                                    "Failure saving state: " + f
1897                                    + " has target not in fragment manager: " + f.mTarget));
1898                        }
1899                        if (fs.mSavedFragmentState == null) {
1900                            fs.mSavedFragmentState = new Bundle();
1901                        }
1902                        putFragment(fs.mSavedFragmentState,
1903                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
1904                        if (f.mTargetRequestCode != 0) {
1905                            fs.mSavedFragmentState.putInt(
1906                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
1907                                    f.mTargetRequestCode);
1908                        }
1909                    }
1910
1911                } else {
1912                    fs.mSavedFragmentState = f.mSavedFragmentState;
1913                }
1914
1915                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
1916                        + fs.mSavedFragmentState);
1917            }
1918        }
1919
1920        if (!haveFragments) {
1921            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
1922            return null;
1923        }
1924
1925        int[] added = null;
1926        BackStackState[] backStack = null;
1927
1928        // Build list of currently added fragments.
1929        if (mAdded != null) {
1930            N = mAdded.size();
1931            if (N > 0) {
1932                added = new int[N];
1933                for (int i=0; i<N; i++) {
1934                    added[i] = mAdded.get(i).mIndex;
1935                    if (added[i] < 0) {
1936                        throwException(new IllegalStateException(
1937                                "Failure saving state: active " + mAdded.get(i)
1938                                + " has cleared index: " + added[i]));
1939                    }
1940                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
1941                            + ": " + mAdded.get(i));
1942                }
1943            }
1944        }
1945
1946        // Now save back stack.
1947        if (mBackStack != null) {
1948            N = mBackStack.size();
1949            if (N > 0) {
1950                backStack = new BackStackState[N];
1951                for (int i=0; i<N; i++) {
1952                    backStack[i] = new BackStackState(mBackStack.get(i));
1953                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
1954                            + ": " + mBackStack.get(i));
1955                }
1956            }
1957        }
1958
1959        FragmentManagerState fms = new FragmentManagerState();
1960        fms.mActive = active;
1961        fms.mAdded = added;
1962        fms.mBackStack = backStack;
1963        return fms;
1964    }
1965
1966    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
1967        // If there is no saved state at all, then there can not be
1968        // any nonConfig fragments either, so that is that.
1969        if (state == null) return;
1970        FragmentManagerState fms = (FragmentManagerState)state;
1971        if (fms.mActive == null) return;
1972
1973        List<FragmentManagerNonConfig> childNonConfigs = null;
1974
1975        // First re-attach any non-config instances we are retaining back
1976        // to their saved state, so we don't try to instantiate them again.
1977        if (nonConfig != null) {
1978            List<Fragment> nonConfigFragments = nonConfig.getFragments();
1979            childNonConfigs = nonConfig.getChildNonConfigs();
1980            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
1981            for (int i = 0; i < count; i++) {
1982                Fragment f = nonConfigFragments.get(i);
1983                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
1984                FragmentState fs = fms.mActive[f.mIndex];
1985                fs.mInstance = f;
1986                f.mSavedViewState = null;
1987                f.mBackStackNesting = 0;
1988                f.mInLayout = false;
1989                f.mAdded = false;
1990                f.mTarget = null;
1991                if (fs.mSavedFragmentState != null) {
1992                    fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
1993                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
1994                            FragmentManagerImpl.VIEW_STATE_TAG);
1995                    f.mSavedFragmentState = fs.mSavedFragmentState;
1996                }
1997            }
1998        }
1999
2000        // Build the full list of active fragments, instantiating them from
2001        // their saved state.
2002        mActive = new ArrayList<>(fms.mActive.length);
2003        if (mAvailIndices != null) {
2004            mAvailIndices.clear();
2005        }
2006        for (int i=0; i<fms.mActive.length; i++) {
2007            FragmentState fs = fms.mActive[i];
2008            if (fs != null) {
2009                FragmentManagerNonConfig childNonConfig = null;
2010                if (childNonConfigs != null && i < childNonConfigs.size()) {
2011                    childNonConfig = childNonConfigs.get(i);
2012                }
2013                Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
2014                if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
2015                mActive.add(f);
2016                // Now that the fragment is instantiated (or came from being
2017                // retained above), clear mInstance in case we end up re-restoring
2018                // from this FragmentState again.
2019                fs.mInstance = null;
2020            } else {
2021                mActive.add(null);
2022                if (mAvailIndices == null) {
2023                    mAvailIndices = new ArrayList<Integer>();
2024                }
2025                if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
2026                mAvailIndices.add(i);
2027            }
2028        }
2029
2030        // Update the target of all retained fragments.
2031        if (nonConfig != null) {
2032            List<Fragment> nonConfigFragments = nonConfig.getFragments();
2033            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
2034            for (int i = 0; i < count; i++) {
2035                Fragment f = nonConfigFragments.get(i);
2036                if (f.mTargetIndex >= 0) {
2037                    if (f.mTargetIndex < mActive.size()) {
2038                        f.mTarget = mActive.get(f.mTargetIndex);
2039                    } else {
2040                        Log.w(TAG, "Re-attaching retained fragment " + f
2041                                + " target no longer exists: " + f.mTargetIndex);
2042                        f.mTarget = null;
2043                    }
2044                }
2045            }
2046        }
2047
2048        // Build the list of currently added fragments.
2049        if (fms.mAdded != null) {
2050            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
2051            for (int i=0; i<fms.mAdded.length; i++) {
2052                Fragment f = mActive.get(fms.mAdded[i]);
2053                if (f == null) {
2054                    throwException(new IllegalStateException(
2055                            "No instantiated fragment for index #" + fms.mAdded[i]));
2056                }
2057                f.mAdded = true;
2058                if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
2059                if (mAdded.contains(f)) {
2060                    throw new IllegalStateException("Already added!");
2061                }
2062                mAdded.add(f);
2063            }
2064        } else {
2065            mAdded = null;
2066        }
2067
2068        // Build the back stack.
2069        if (fms.mBackStack != null) {
2070            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
2071            for (int i=0; i<fms.mBackStack.length; i++) {
2072                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
2073                if (DEBUG) {
2074                    Log.v(TAG, "restoreAllState: back stack #" + i
2075                        + " (index " + bse.mIndex + "): " + bse);
2076                    LogWriter logw = new LogWriter(TAG);
2077                    PrintWriter pw = new PrintWriter(logw);
2078                    bse.dump("  ", pw, false);
2079                }
2080                mBackStack.add(bse);
2081                if (bse.mIndex >= 0) {
2082                    setBackStackIndex(bse.mIndex, bse);
2083                }
2084            }
2085        } else {
2086            mBackStack = null;
2087        }
2088    }
2089
2090    public void attachController(FragmentHostCallback host,
2091            FragmentContainer container, Fragment parent) {
2092        if (mHost != null) throw new IllegalStateException("Already attached");
2093        mHost = host;
2094        mContainer = container;
2095        mParent = parent;
2096    }
2097
2098    public void noteStateNotSaved() {
2099        mStateSaved = false;
2100    }
2101
2102    public void dispatchCreate() {
2103        mStateSaved = false;
2104        moveToState(Fragment.CREATED, false);
2105    }
2106
2107    public void dispatchActivityCreated() {
2108        mStateSaved = false;
2109        moveToState(Fragment.ACTIVITY_CREATED, false);
2110    }
2111
2112    public void dispatchStart() {
2113        mStateSaved = false;
2114        moveToState(Fragment.STARTED, false);
2115    }
2116
2117    public void dispatchResume() {
2118        mStateSaved = false;
2119        moveToState(Fragment.RESUMED, false);
2120    }
2121
2122    public void dispatchPause() {
2123        moveToState(Fragment.STARTED, false);
2124    }
2125
2126    public void dispatchStop() {
2127        // See saveAllState() for the explanation of this.  We do this for
2128        // all platform versions, to keep our behavior more consistent between
2129        // them.
2130        mStateSaved = true;
2131
2132        moveToState(Fragment.STOPPED, false);
2133    }
2134
2135    public void dispatchReallyStop() {
2136        moveToState(Fragment.ACTIVITY_CREATED, false);
2137    }
2138
2139    public void dispatchDestroyView() {
2140        moveToState(Fragment.CREATED, false);
2141    }
2142
2143    public void dispatchDestroy() {
2144        mDestroyed = true;
2145        execPendingActions();
2146        moveToState(Fragment.INITIALIZING, false);
2147        mHost = null;
2148        mContainer = null;
2149        mParent = null;
2150    }
2151
2152    public void dispatchConfigurationChanged(Configuration newConfig) {
2153        if (mAdded != null) {
2154            for (int i=0; i<mAdded.size(); i++) {
2155                Fragment f = mAdded.get(i);
2156                if (f != null) {
2157                    f.performConfigurationChanged(newConfig);
2158                }
2159            }
2160        }
2161    }
2162
2163    public void dispatchLowMemory() {
2164        if (mAdded != null) {
2165            for (int i=0; i<mAdded.size(); i++) {
2166                Fragment f = mAdded.get(i);
2167                if (f != null) {
2168                    f.performLowMemory();
2169                }
2170            }
2171        }
2172    }
2173
2174    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
2175        boolean show = false;
2176        ArrayList<Fragment> newMenus = null;
2177        if (mAdded != null) {
2178            for (int i=0; i<mAdded.size(); i++) {
2179                Fragment f = mAdded.get(i);
2180                if (f != null) {
2181                    if (f.performCreateOptionsMenu(menu, inflater)) {
2182                        show = true;
2183                        if (newMenus == null) {
2184                            newMenus = new ArrayList<Fragment>();
2185                        }
2186                        newMenus.add(f);
2187                    }
2188                }
2189            }
2190        }
2191
2192        if (mCreatedMenus != null) {
2193            for (int i=0; i<mCreatedMenus.size(); i++) {
2194                Fragment f = mCreatedMenus.get(i);
2195                if (newMenus == null || !newMenus.contains(f)) {
2196                    f.onDestroyOptionsMenu();
2197                }
2198            }
2199        }
2200
2201        mCreatedMenus = newMenus;
2202
2203        return show;
2204    }
2205
2206    public boolean dispatchPrepareOptionsMenu(Menu menu) {
2207        boolean show = false;
2208        if (mAdded != null) {
2209            for (int i=0; i<mAdded.size(); i++) {
2210                Fragment f = mAdded.get(i);
2211                if (f != null) {
2212                    if (f.performPrepareOptionsMenu(menu)) {
2213                        show = true;
2214                    }
2215                }
2216            }
2217        }
2218        return show;
2219    }
2220
2221    public boolean dispatchOptionsItemSelected(MenuItem item) {
2222        if (mAdded != null) {
2223            for (int i=0; i<mAdded.size(); i++) {
2224                Fragment f = mAdded.get(i);
2225                if (f != null) {
2226                    if (f.performOptionsItemSelected(item)) {
2227                        return true;
2228                    }
2229                }
2230            }
2231        }
2232        return false;
2233    }
2234
2235    public boolean dispatchContextItemSelected(MenuItem item) {
2236        if (mAdded != null) {
2237            for (int i=0; i<mAdded.size(); i++) {
2238                Fragment f = mAdded.get(i);
2239                if (f != null) {
2240                    if (f.performContextItemSelected(item)) {
2241                        return true;
2242                    }
2243                }
2244            }
2245        }
2246        return false;
2247    }
2248
2249    public void dispatchOptionsMenuClosed(Menu menu) {
2250        if (mAdded != null) {
2251            for (int i=0; i<mAdded.size(); i++) {
2252                Fragment f = mAdded.get(i);
2253                if (f != null) {
2254                    f.performOptionsMenuClosed(menu);
2255                }
2256            }
2257        }
2258    }
2259
2260    public static int reverseTransit(int transit) {
2261        int rev = 0;
2262        switch (transit) {
2263            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
2264                rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
2265                break;
2266            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
2267                rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
2268                break;
2269            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
2270                rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
2271                break;
2272        }
2273        return rev;
2274
2275    }
2276
2277    public static final int ANIM_STYLE_OPEN_ENTER = 1;
2278    public static final int ANIM_STYLE_OPEN_EXIT = 2;
2279    public static final int ANIM_STYLE_CLOSE_ENTER = 3;
2280    public static final int ANIM_STYLE_CLOSE_EXIT = 4;
2281    public static final int ANIM_STYLE_FADE_ENTER = 5;
2282    public static final int ANIM_STYLE_FADE_EXIT = 6;
2283
2284    public static int transitToStyleIndex(int transit, boolean enter) {
2285        int animAttr = -1;
2286        switch (transit) {
2287            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
2288                animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT;
2289                break;
2290            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
2291                animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT;
2292                break;
2293            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
2294                animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT;
2295                break;
2296        }
2297        return animAttr;
2298    }
2299
2300    @Override
2301    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
2302        if (!"fragment".equals(name)) {
2303            return null;
2304        }
2305
2306        String fname = attrs.getAttributeValue(null, "class");
2307        TypedArray a =  context.obtainStyledAttributes(attrs, FragmentTag.Fragment);
2308        if (fname == null) {
2309            fname = a.getString(FragmentTag.Fragment_name);
2310        }
2311        int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID);
2312        String tag = a.getString(FragmentTag.Fragment_tag);
2313        a.recycle();
2314
2315        if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) {
2316            // Invalid support lib fragment; let the device's framework handle it.
2317            // This will allow android.app.Fragments to do the right thing.
2318            return null;
2319        }
2320
2321        int containerId = parent != null ? parent.getId() : 0;
2322        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
2323            throw new IllegalArgumentException(attrs.getPositionDescription()
2324                    + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
2325        }
2326
2327        // If we restored from a previous state, we may already have
2328        // instantiated this fragment from the state and should use
2329        // that instance instead of making a new one.
2330        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
2331        if (fragment == null && tag != null) {
2332            fragment = findFragmentByTag(tag);
2333        }
2334        if (fragment == null && containerId != View.NO_ID) {
2335            fragment = findFragmentById(containerId);
2336        }
2337
2338        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
2339                + Integer.toHexString(id) + " fname=" + fname
2340                + " existing=" + fragment);
2341        if (fragment == null) {
2342            fragment = Fragment.instantiate(context, fname);
2343            fragment.mFromLayout = true;
2344            fragment.mFragmentId = id != 0 ? id : containerId;
2345            fragment.mContainerId = containerId;
2346            fragment.mTag = tag;
2347            fragment.mInLayout = true;
2348            fragment.mFragmentManager = this;
2349            fragment.mHost = mHost;
2350            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
2351            addFragment(fragment, true);
2352
2353        } else if (fragment.mInLayout) {
2354            // A fragment already exists and it is not one we restored from
2355            // previous state.
2356            throw new IllegalArgumentException(attrs.getPositionDescription()
2357                    + ": Duplicate id 0x" + Integer.toHexString(id)
2358                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
2359                    + " with another fragment for " + fname);
2360        } else {
2361            // This fragment was retained from a previous instance; get it
2362            // going now.
2363            fragment.mInLayout = true;
2364            fragment.mHost = mHost;
2365            // If this fragment is newly instantiated (either right now, or
2366            // from last saved state), then give it the attributes to
2367            // initialize itself.
2368            if (!fragment.mRetaining) {
2369                fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
2370            }
2371        }
2372
2373        // If we haven't finished entering the CREATED state ourselves yet,
2374        // push the inflated child fragment along.
2375        if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
2376            moveToState(fragment, Fragment.CREATED, 0, 0, false);
2377        } else {
2378            moveToState(fragment);
2379        }
2380
2381        if (fragment.mView == null) {
2382            throw new IllegalStateException("Fragment " + fname
2383                    + " did not create a view.");
2384        }
2385        if (id != 0) {
2386            fragment.mView.setId(id);
2387        }
2388        if (fragment.mView.getTag() == null) {
2389            fragment.mView.setTag(tag);
2390        }
2391        return fragment.mView;
2392    }
2393
2394    LayoutInflaterFactory getLayoutInflaterFactory() {
2395        return this;
2396    }
2397
2398    static class FragmentTag {
2399        public static final int[] Fragment = {
2400                0x01010003, 0x010100d0, 0x010100d1
2401        };
2402        public static final int Fragment_id = 1;
2403        public static final int Fragment_name = 0;
2404        public static final int Fragment_tag = 2;
2405    }
2406}
2407