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