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