FragmentManager.java revision 639bc5a1802f720574af5a9d0140eee41988b828
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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.content.Context;
22import android.content.res.Configuration;
23import android.content.res.Resources.NotFoundException;
24import android.content.res.TypedArray;
25import android.os.Build;
26import android.os.Bundle;
27import android.os.Looper;
28import android.os.Parcel;
29import android.os.Parcelable;
30import android.support.annotation.CallSuper;
31import android.support.annotation.IdRes;
32import android.support.annotation.RestrictTo;
33import android.support.annotation.StringRes;
34import android.support.v4.os.BuildCompat;
35import android.support.v4.util.ArraySet;
36import android.support.v4.util.DebugUtils;
37import android.support.v4.util.LogWriter;
38import android.support.v4.util.Pair;
39import android.support.v4.view.LayoutInflaterFactory;
40import android.support.v4.view.ViewCompat;
41import android.util.AttributeSet;
42import android.util.Log;
43import android.util.SparseArray;
44import android.view.LayoutInflater;
45import android.view.Menu;
46import android.view.MenuInflater;
47import android.view.MenuItem;
48import android.view.View;
49import android.view.ViewGroup;
50import android.view.animation.AccelerateInterpolator;
51import android.view.animation.AlphaAnimation;
52import android.view.animation.Animation;
53import android.view.animation.Animation.AnimationListener;
54import android.view.animation.AnimationSet;
55import android.view.animation.AnimationUtils;
56import android.view.animation.DecelerateInterpolator;
57import android.view.animation.Interpolator;
58import android.view.animation.ScaleAnimation;
59
60import java.io.FileDescriptor;
61import java.io.PrintWriter;
62import java.lang.reflect.Field;
63import java.util.ArrayList;
64import java.util.Arrays;
65import java.util.List;
66import java.util.concurrent.CopyOnWriteArrayList;
67
68/**
69 * Static library support version of the framework's {@link android.app.FragmentManager}.
70 * Used to write apps that run on platforms prior to Android 3.0.  When running
71 * on Android 3.0 or above, this implementation is still used; it does not try
72 * to switch to the framework's implementation.  See the framework {@link FragmentManager}
73 * documentation for a class overview.
74 *
75 * <p>Your activity must derive from {@link FragmentActivity} to use this. From such an activity,
76 * you can acquire the {@link FragmentManager} by calling
77 * {@link FragmentActivity#getSupportFragmentManager}.
78 */
79public abstract class FragmentManager {
80    /**
81     * Representation of an entry on the fragment back stack, as created
82     * with {@link FragmentTransaction#addToBackStack(String)
83     * FragmentTransaction.addToBackStack()}.  Entries can later be
84     * retrieved with {@link FragmentManager#getBackStackEntryAt(int)
85     * FragmentManager.getBackStackEntryAt()}.
86     *
87     * <p>Note that you should never hold on to a BackStackEntry object;
88     * the identifier as returned by {@link #getId} is the only thing that
89     * will be persisted across activity instances.
90     */
91    public interface BackStackEntry {
92        /**
93         * Return the unique identifier for the entry.  This is the only
94         * representation of the entry that will persist across activity
95         * instances.
96         */
97        public int getId();
98
99        /**
100         * Get the name that was supplied to
101         * {@link FragmentTransaction#addToBackStack(String)
102         * FragmentTransaction.addToBackStack(String)} when creating this entry.
103         */
104        public String getName();
105
106        /**
107         * Return the full bread crumb title resource identifier for the entry,
108         * or 0 if it does not have one.
109         */
110        @StringRes
111        public int getBreadCrumbTitleRes();
112
113        /**
114         * Return the short bread crumb title resource identifier for the entry,
115         * or 0 if it does not have one.
116         */
117        @StringRes
118        public int getBreadCrumbShortTitleRes();
119
120        /**
121         * Return the full bread crumb title for the entry, or null if it
122         * does not have one.
123         */
124        public CharSequence getBreadCrumbTitle();
125
126        /**
127         * Return the short bread crumb title for the entry, or null if it
128         * does not have one.
129         */
130        public CharSequence getBreadCrumbShortTitle();
131    }
132
133    /**
134     * Interface to watch for changes to the back stack.
135     */
136    public interface OnBackStackChangedListener {
137        /**
138         * Called whenever the contents of the back stack change.
139         */
140        public void onBackStackChanged();
141    }
142
143    /**
144     * Start a series of edit operations on the Fragments associated with
145     * this FragmentManager.
146     *
147     * <p>Note: A fragment transaction can only be created/committed prior
148     * to an activity saving its state.  If you try to commit a transaction
149     * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()}
150     * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart}
151     * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error.
152     * This is because the framework takes care of saving your current fragments
153     * in the state, and if changes are made after the state is saved then they
154     * will be lost.</p>
155     */
156    public abstract FragmentTransaction beginTransaction();
157
158    /**
159     * @hide -- remove once prebuilts are in.
160     * @deprecated
161     */
162    @RestrictTo(LIBRARY_GROUP)
163    @Deprecated
164    public FragmentTransaction openTransaction() {
165        return beginTransaction();
166    }
167
168    /**
169     * After a {@link FragmentTransaction} is committed with
170     * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it
171     * is scheduled to be executed asynchronously on the process's main thread.
172     * If you want to immediately executing any such pending operations, you
173     * can call this function (only from the main thread) to do so.  Note that
174     * all callbacks and other related behavior will be done from within this
175     * call, so be careful about where this is called from.
176     *
177     * <p>If you are committing a single transaction that does not modify the
178     * fragment back stack, strongly consider using
179     * {@link FragmentTransaction#commitNow()} instead. This can help avoid
180     * unwanted side effects when other code in your app has pending committed
181     * transactions that expect different timing.</p>
182     * <p>
183     * This also forces the start of any postponed Transactions where
184     * {@link Fragment#postponeEnterTransition()} has been called.
185     *
186     * @return Returns true if there were any pending transactions to be
187     * executed.
188     */
189    public abstract boolean executePendingTransactions();
190
191    /**
192     * Finds a fragment that was identified by the given id either when inflated
193     * from XML or as the container ID when added in a transaction.  This first
194     * searches through fragments that are currently added to the manager's
195     * activity; if no such fragment is found, then all fragments currently
196     * on the back stack associated with this ID are searched.
197     * @return The fragment if found or null otherwise.
198     */
199    public abstract Fragment findFragmentById(@IdRes int id);
200
201    /**
202     * Finds a fragment that was identified by the given tag either when inflated
203     * from XML or as supplied when added in a transaction.  This first
204     * searches through fragments that are currently added to the manager's
205     * activity; if no such fragment is found, then all fragments currently
206     * on the back stack are searched.
207     * @return The fragment if found or null otherwise.
208     */
209    public abstract Fragment findFragmentByTag(String tag);
210
211    /**
212     * Flag for {@link #popBackStack(String, int)}
213     * and {@link #popBackStack(int, int)}: If set, and the name or ID of
214     * a back stack entry has been supplied, then all matching entries will
215     * be consumed until one that doesn't match is found or the bottom of
216     * the stack is reached.  Otherwise, all entries up to but not including that entry
217     * will be removed.
218     */
219    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
220
221    /**
222     * Pop the top state off the back stack.  Returns true if there was one
223     * to pop, else false.  This function is asynchronous -- it enqueues the
224     * request to pop, but the action will not be performed until the application
225     * returns to its event loop.
226     */
227    public abstract void popBackStack();
228
229    /**
230     * Like {@link #popBackStack()}, but performs the operation immediately
231     * inside of the call.  This is like calling {@link #executePendingTransactions()}
232     * afterwards without forcing the start of postponed Transactions.
233     * @return Returns true if there was something popped, else false.
234     */
235    public abstract boolean popBackStackImmediate();
236
237    /**
238     * Pop the last fragment transition from the manager's fragment
239     * back stack.  If there is nothing to pop, false is returned.
240     * This function is asynchronous -- it enqueues the
241     * request to pop, but the action will not be performed until the application
242     * returns to its event loop.
243     *
244     * @param name If non-null, this is the name of a previous back state
245     * to look for; if found, all states up to that state will be popped.  The
246     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
247     * the named state itself is popped. If null, only the top state is popped.
248     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
249     */
250    public abstract void popBackStack(String name, int flags);
251
252    /**
253     * Like {@link #popBackStack(String, int)}, but performs the operation immediately
254     * inside of the call.  This is like calling {@link #executePendingTransactions()}
255     * afterwards without forcing the start of postponed Transactions.
256     * @return Returns true if there was something popped, else false.
257     */
258    public abstract boolean popBackStackImmediate(String name, int flags);
259
260    /**
261     * Pop all back stack states up to the one with the given identifier.
262     * This function is asynchronous -- it enqueues the
263     * request to pop, but the action will not be performed until the application
264     * returns to its event loop.
265     *
266     * @param id Identifier of the stated to be popped. If no identifier exists,
267     * false is returned.
268     * The identifier is the number returned by
269     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
270     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
271     * the named state itself is popped.
272     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
273     */
274    public abstract void popBackStack(int id, int flags);
275
276    /**
277     * Like {@link #popBackStack(int, int)}, but performs the operation immediately
278     * inside of the call.  This is like calling {@link #executePendingTransactions()}
279     * afterwards without forcing the start of postponed Transactions.
280     * @return Returns true if there was something popped, else false.
281     */
282    public abstract boolean popBackStackImmediate(int id, int flags);
283
284    /**
285     * Return the number of entries currently in the back stack.
286     */
287    public abstract int getBackStackEntryCount();
288
289    /**
290     * Return the BackStackEntry at index <var>index</var> in the back stack;
291     * entries start index 0 being the bottom of the stack.
292     */
293    public abstract BackStackEntry getBackStackEntryAt(int index);
294
295    /**
296     * Add a new listener for changes to the fragment back stack.
297     */
298    public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
299
300    /**
301     * Remove a listener that was previously added with
302     * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
303     */
304    public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
305
306    /**
307     * Put a reference to a fragment in a Bundle.  This Bundle can be
308     * persisted as saved state, and when later restoring
309     * {@link #getFragment(Bundle, String)} will return the current
310     * instance of the same fragment.
311     *
312     * @param bundle The bundle in which to put the fragment reference.
313     * @param key The name of the entry in the bundle.
314     * @param fragment The Fragment whose reference is to be stored.
315     */
316    public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
317
318    /**
319     * Retrieve the current Fragment instance for a reference previously
320     * placed with {@link #putFragment(Bundle, String, Fragment)}.
321     *
322     * @param bundle The bundle from which to retrieve the fragment reference.
323     * @param key The name of the entry in the bundle.
324     * @return Returns the current Fragment instance that is associated with
325     * the given reference.
326     */
327    public abstract Fragment getFragment(Bundle bundle, String key);
328
329    /**
330     * Get a list of all fragments that have been added to the fragment manager.
331     *
332     * @return The list of all fragments or null if none.
333     * @hide
334     */
335    @RestrictTo(LIBRARY_GROUP)
336    public abstract List<Fragment> getFragments();
337
338    /**
339     * Save the current instance state of the given Fragment.  This can be
340     * used later when creating a new instance of the Fragment and adding
341     * it to the fragment manager, to have it create itself to match the
342     * current state returned here.  Note that there are limits on how
343     * this can be used:
344     *
345     * <ul>
346     * <li>The Fragment must currently be attached to the FragmentManager.
347     * <li>A new Fragment created using this saved state must be the same class
348     * type as the Fragment it was created from.
349     * <li>The saved state can not contain dependencies on other fragments --
350     * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to
351     * store a fragment reference because that reference may not be valid when
352     * this saved state is later used.  Likewise the Fragment's target and
353     * result code are not included in this state.
354     * </ul>
355     *
356     * @param f The Fragment whose state is to be saved.
357     * @return The generated state.  This will be null if there was no
358     * interesting state created by the fragment.
359     */
360    public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
361
362    /**
363     * Returns true if the final {@link android.app.Activity#onDestroy() Activity.onDestroy()}
364     * call has been made on the FragmentManager's Activity, so this instance is now dead.
365     */
366    public abstract boolean isDestroyed();
367
368    /**
369     * Registers a {@link FragmentLifecycleCallbacks} to listen to fragment lifecycle events
370     * happening in this FragmentManager. All registered callbacks will be automatically
371     * unregistered when this FragmentManager is destroyed.
372     *
373     * @param cb Callbacks to register
374     * @param recursive true to automatically register this callback for all child FragmentManagers
375     */
376    public abstract void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
377            boolean recursive);
378
379    /**
380     * Unregisters a previously registered {@link FragmentLifecycleCallbacks}. If the callback
381     * was not previously registered this call has no effect. All registered callbacks will be
382     * automatically unregistered when this FragmentManager is destroyed.
383     *
384     * @param cb Callbacks to unregister
385     */
386    public abstract void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb);
387
388    /**
389     * Print the FragmentManager's state into the given stream.
390     *
391     * @param prefix Text to print at the front of each line.
392     * @param fd The raw file descriptor that the dump is being sent to.
393     * @param writer A PrintWriter to which the dump is to be set.
394     * @param args Additional arguments to the dump request.
395     */
396    public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
397
398    /**
399     * Control whether the framework's internal fragment manager debugging
400     * logs are turned on.  If enabled, you will see output in logcat as
401     * the framework performs fragment operations.
402     */
403    public static void enableDebugLogging(boolean enabled) {
404        FragmentManagerImpl.DEBUG = enabled;
405    }
406
407    /**
408     * Callback interface for listening to fragment state changes that happen
409     * within a given FragmentManager.
410     */
411    public abstract class FragmentLifecycleCallbacks {
412        /**
413         * Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
414         * This is a good time to inject any required dependencies for the fragment before any of
415         * the fragment's lifecycle methods are invoked.
416         *
417         * @param fm Host FragmentManager
418         * @param f Fragment changing state
419         * @param context Context that the Fragment is being attached to
420         */
421        public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {}
422
423        /**
424         * Called after the fragment has been attached to its host. Its host will have had
425         * <code>onAttachFragment</code> called before this call happens.
426         *
427         * @param fm Host FragmentManager
428         * @param f Fragment changing state
429         * @param context Context that the Fragment was attached to
430         */
431        public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {}
432
433        /**
434         * Called after the fragment has returned from the FragmentManager's call to
435         * {@link Fragment#onCreate(Bundle)}. This will only happen once for any given
436         * fragment instance, though the fragment may be attached and detached multiple times.
437         *
438         * @param fm Host FragmentManager
439         * @param f Fragment changing state
440         * @param savedInstanceState Saved instance bundle from a previous instance
441         */
442        public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}
443
444        /**
445         * Called after the fragment has returned from the FragmentManager's call to
446         * {@link Fragment#onActivityCreated(Bundle)}. This will only happen once for any given
447         * fragment instance, though the fragment may be attached and detached multiple times.
448         *
449         * @param fm Host FragmentManager
450         * @param f Fragment changing state
451         * @param savedInstanceState Saved instance bundle from a previous instance
452         */
453        public void onFragmentActivityCreated(FragmentManager fm, Fragment f,
454                Bundle savedInstanceState) {}
455
456        /**
457         * Called after the fragment has returned a non-null view from the FragmentManager's
458         * request to {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
459         *
460         * @param fm Host FragmentManager
461         * @param f Fragment that created and owns the view
462         * @param v View returned by the fragment
463         * @param savedInstanceState Saved instance bundle from a previous instance
464         */
465        public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
466                Bundle savedInstanceState) {}
467
468        /**
469         * Called after the fragment has returned from the FragmentManager's call to
470         * {@link Fragment#onStart()}.
471         *
472         * @param fm Host FragmentManager
473         * @param f Fragment changing state
474         */
475        public void onFragmentStarted(FragmentManager fm, Fragment f) {}
476
477        /**
478         * Called after the fragment has returned from the FragmentManager's call to
479         * {@link Fragment#onResume()}.
480         *
481         * @param fm Host FragmentManager
482         * @param f Fragment changing state
483         */
484        public void onFragmentResumed(FragmentManager fm, Fragment f) {}
485
486        /**
487         * Called after the fragment has returned from the FragmentManager's call to
488         * {@link Fragment#onPause()}.
489         *
490         * @param fm Host FragmentManager
491         * @param f Fragment changing state
492         */
493        public void onFragmentPaused(FragmentManager fm, Fragment f) {}
494
495        /**
496         * Called after the fragment has returned from the FragmentManager's call to
497         * {@link Fragment#onStop()}.
498         *
499         * @param fm Host FragmentManager
500         * @param f Fragment changing state
501         */
502        public void onFragmentStopped(FragmentManager fm, Fragment f) {}
503
504        /**
505         * Called after the fragment has returned from the FragmentManager's call to
506         * {@link Fragment#onSaveInstanceState(Bundle)}.
507         *
508         * @param fm Host FragmentManager
509         * @param f Fragment changing state
510         * @param outState Saved state bundle for the fragment
511         */
512        public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {}
513
514        /**
515         * Called after the fragment has returned from the FragmentManager's call to
516         * {@link Fragment#onDestroyView()}.
517         *
518         * @param fm Host FragmentManager
519         * @param f Fragment changing state
520         */
521        public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}
522
523        /**
524         * Called after the fragment has returned from the FragmentManager's call to
525         * {@link Fragment#onDestroy()}.
526         *
527         * @param fm Host FragmentManager
528         * @param f Fragment changing state
529         */
530        public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}
531
532        /**
533         * Called after the fragment has returned from the FragmentManager's call to
534         * {@link Fragment#onDetach()}.
535         *
536         * @param fm Host FragmentManager
537         * @param f Fragment changing state
538         */
539        public void onFragmentDetached(FragmentManager fm, Fragment f) {}
540    }
541}
542
543final class FragmentManagerState implements Parcelable {
544    FragmentState[] mActive;
545    int[] mAdded;
546    BackStackState[] mBackStack;
547
548    public FragmentManagerState() {
549    }
550
551    public FragmentManagerState(Parcel in) {
552        mActive = in.createTypedArray(FragmentState.CREATOR);
553        mAdded = in.createIntArray();
554        mBackStack = in.createTypedArray(BackStackState.CREATOR);
555    }
556
557    @Override
558    public int describeContents() {
559        return 0;
560    }
561
562    @Override
563    public void writeToParcel(Parcel dest, int flags) {
564        dest.writeTypedArray(mActive, flags);
565        dest.writeIntArray(mAdded);
566        dest.writeTypedArray(mBackStack, flags);
567    }
568
569    public static final Parcelable.Creator<FragmentManagerState> CREATOR
570            = new Parcelable.Creator<FragmentManagerState>() {
571        @Override
572        public FragmentManagerState createFromParcel(Parcel in) {
573            return new FragmentManagerState(in);
574        }
575
576        @Override
577        public FragmentManagerState[] newArray(int size) {
578            return new FragmentManagerState[size];
579        }
580    };
581}
582
583/**
584 * Container for fragments associated with an activity.
585 */
586final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
587    static boolean DEBUG = false;
588    static final String TAG = "FragmentManager";
589
590    static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11;
591
592    static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
593    static final String TARGET_STATE_TAG = "android:target_state";
594    static final String VIEW_STATE_TAG = "android:view_state";
595    static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
596
597    static class AnimateOnHWLayerIfNeededListener implements AnimationListener {
598        private AnimationListener mOriginalListener;
599        private boolean mShouldRunOnHWLayer;
600        View mView;
601
602        public AnimateOnHWLayerIfNeededListener(final View v, Animation anim) {
603            if (v == null || anim == null) {
604                return;
605            }
606            mView = v;
607        }
608
609        public AnimateOnHWLayerIfNeededListener(final View v, Animation anim,
610                AnimationListener listener) {
611            if (v == null || anim == null) {
612                return;
613            }
614            mOriginalListener = listener;
615            mView = v;
616            mShouldRunOnHWLayer = true;
617        }
618
619        @Override
620        @CallSuper
621        public void onAnimationStart(Animation animation) {
622            if (mOriginalListener != null) {
623                mOriginalListener.onAnimationStart(animation);
624            }
625        }
626
627        @Override
628        @CallSuper
629        public void onAnimationEnd(Animation animation) {
630            if (mView != null && mShouldRunOnHWLayer) {
631                // If we're attached to a window, assume we're in the normal performTraversals
632                // drawing path for Animations running. It's not safe to change the layer type
633                // during drawing, so post it to the View to run later. If we're not attached
634                // or we're running on N and above, post it to the view. If we're not on N and
635                // not attached, do it right now since existing platform versions don't run the
636                // hwui renderer for detached views off the UI thread making changing layer type
637                // safe, but posting may not be.
638                // Prior to N posting to a detached view from a non-Looper thread could cause
639                // leaks, since the thread-local run queue on a non-Looper thread would never
640                // be flushed.
641                if (ViewCompat.isAttachedToWindow(mView) || BuildCompat.isAtLeastN()) {
642                    mView.post(new Runnable() {
643                        @Override
644                        public void run() {
645                            ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_NONE, null);
646                        }
647                    });
648                } else {
649                    ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_NONE, null);
650                }
651            }
652            if (mOriginalListener != null) {
653                mOriginalListener.onAnimationEnd(animation);
654            }
655        }
656
657        @Override
658        public void onAnimationRepeat(Animation animation) {
659            if (mOriginalListener != null) {
660                mOriginalListener.onAnimationRepeat(animation);
661            }
662        }
663    }
664
665    ArrayList<OpGenerator> mPendingActions;
666    Runnable[] mTmpActions;
667    boolean mExecutingActions;
668
669    ArrayList<Fragment> mActive;
670    ArrayList<Fragment> mAdded;
671    ArrayList<Integer> mAvailIndices;
672    ArrayList<BackStackRecord> mBackStack;
673    ArrayList<Fragment> mCreatedMenus;
674
675    // Must be accessed while locked.
676    ArrayList<BackStackRecord> mBackStackIndices;
677    ArrayList<Integer> mAvailBackStackIndices;
678
679    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
680    private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks;
681
682    int mCurState = Fragment.INITIALIZING;
683    FragmentHostCallback mHost;
684    FragmentContainer mContainer;
685    Fragment mParent;
686
687    static Field sAnimationListenerField = null;
688
689    boolean mNeedMenuInvalidate;
690    boolean mStateSaved;
691    boolean mDestroyed;
692    String mNoTransactionsBecause;
693    boolean mHavePendingDeferredStart;
694
695    // Temporary vars for optimizing execution of BackStackRecords:
696    ArrayList<BackStackRecord> mTmpRecords;
697    ArrayList<Boolean> mTmpIsPop;
698    ArrayList<Fragment> mTmpAddedFragments;
699
700    // Temporary vars for state save and restore.
701    Bundle mStateBundle = null;
702    SparseArray<Parcelable> mStateArray = null;
703
704    // Postponed transactions.
705    ArrayList<StartEnterTransitionListener> mPostponedTransactions;
706
707    Runnable mExecCommit = new Runnable() {
708        @Override
709        public void run() {
710            execPendingActions();
711        }
712    };
713
714    static boolean modifiesAlpha(Animation anim) {
715        if (anim instanceof AlphaAnimation) {
716            return true;
717        } else if (anim instanceof AnimationSet) {
718            List<Animation> anims = ((AnimationSet) anim).getAnimations();
719            for (int i = 0; i < anims.size(); i++) {
720                if (anims.get(i) instanceof AlphaAnimation) {
721                    return true;
722                }
723            }
724        }
725        return false;
726    }
727
728    static boolean shouldRunOnHWLayer(View v, Animation anim) {
729        return Build.VERSION.SDK_INT >= 19
730                && ViewCompat.getLayerType(v) == ViewCompat.LAYER_TYPE_NONE
731                && ViewCompat.hasOverlappingRendering(v)
732                && modifiesAlpha(anim);
733    }
734
735    private void throwException(RuntimeException ex) {
736        Log.e(TAG, ex.getMessage());
737        Log.e(TAG, "Activity state:");
738        LogWriter logw = new LogWriter(TAG);
739        PrintWriter pw = new PrintWriter(logw);
740        if (mHost != null) {
741            try {
742                mHost.onDump("  ", null, pw, new String[] { });
743            } catch (Exception e) {
744                Log.e(TAG, "Failed dumping state", e);
745            }
746        } else {
747            try {
748                dump("  ", null, pw, new String[] { });
749            } catch (Exception e) {
750                Log.e(TAG, "Failed dumping state", e);
751            }
752        }
753        throw ex;
754    }
755
756    @Override
757    public FragmentTransaction beginTransaction() {
758        return new BackStackRecord(this);
759    }
760
761    @Override
762    public boolean executePendingTransactions() {
763        boolean updates = execPendingActions();
764        forcePostponedTransactions();
765        return updates;
766    }
767
768    @Override
769    public void popBackStack() {
770        enqueueAction(new PopBackStackState(null, -1, 0), false);
771    }
772
773    @Override
774    public boolean popBackStackImmediate() {
775        checkStateLoss();
776        return popBackStackImmediate(null, -1, 0);
777    }
778
779    @Override
780    public void popBackStack(final String name, final int flags) {
781        enqueueAction(new PopBackStackState(name, -1, flags), false);
782    }
783
784    @Override
785    public boolean popBackStackImmediate(String name, int flags) {
786        checkStateLoss();
787        return popBackStackImmediate(name, -1, flags);
788    }
789
790    @Override
791    public void popBackStack(final int id, final int flags) {
792        if (id < 0) {
793            throw new IllegalArgumentException("Bad id: " + id);
794        }
795        enqueueAction(new PopBackStackState(null, id, flags), false);
796    }
797
798    @Override
799    public boolean popBackStackImmediate(int id, int flags) {
800        checkStateLoss();
801        execPendingActions();
802        if (id < 0) {
803            throw new IllegalArgumentException("Bad id: " + id);
804        }
805        return popBackStackImmediate(null, id, flags);
806    }
807
808    /**
809     * Used by all public popBackStackImmediate methods, this executes pending transactions and
810     * returns true if the pop action did anything, regardless of what other pending
811     * transactions did.
812     *
813     * @return true if the pop operation did anything or false otherwise.
814     */
815    private boolean popBackStackImmediate(String name, int id, int flags) {
816        execPendingActions();
817        ensureExecReady(true);
818
819        boolean executePop = popBackStackState(mTmpRecords, mTmpIsPop, name, id, flags);
820        if (executePop) {
821            mExecutingActions = true;
822            try {
823                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
824            } finally {
825                cleanupExec();
826            }
827        }
828
829        doPendingDeferredStart();
830        return executePop;
831    }
832
833    @Override
834    public int getBackStackEntryCount() {
835        return mBackStack != null ? mBackStack.size() : 0;
836    }
837
838    @Override
839    public BackStackEntry getBackStackEntryAt(int index) {
840        return mBackStack.get(index);
841    }
842
843    @Override
844    public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
845        if (mBackStackChangeListeners == null) {
846            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
847        }
848        mBackStackChangeListeners.add(listener);
849    }
850
851    @Override
852    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
853        if (mBackStackChangeListeners != null) {
854            mBackStackChangeListeners.remove(listener);
855        }
856    }
857
858    @Override
859    public void putFragment(Bundle bundle, String key, Fragment fragment) {
860        if (fragment.mIndex < 0) {
861            throwException(new IllegalStateException("Fragment " + fragment
862                    + " is not currently in the FragmentManager"));
863        }
864        bundle.putInt(key, fragment.mIndex);
865    }
866
867    @Override
868    public Fragment getFragment(Bundle bundle, String key) {
869        int index = bundle.getInt(key, -1);
870        if (index == -1) {
871            return null;
872        }
873        if (index >= mActive.size()) {
874            throwException(new IllegalStateException("Fragment no longer exists for key "
875                    + key + ": index " + index));
876        }
877        Fragment f = mActive.get(index);
878        if (f == null) {
879            throwException(new IllegalStateException("Fragment no longer exists for key "
880                    + key + ": index " + index));
881        }
882        return f;
883    }
884
885    @Override
886    public List<Fragment> getFragments() {
887        return mActive;
888    }
889
890    @Override
891    public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) {
892        if (fragment.mIndex < 0) {
893            throwException( new IllegalStateException("Fragment " + fragment
894                    + " is not currently in the FragmentManager"));
895        }
896        if (fragment.mState > Fragment.INITIALIZING) {
897            Bundle result = saveFragmentBasicState(fragment);
898            return result != null ? new Fragment.SavedState(result) : null;
899        }
900        return null;
901    }
902
903    @Override
904    public boolean isDestroyed() {
905        return mDestroyed;
906    }
907
908    @Override
909    public String toString() {
910        StringBuilder sb = new StringBuilder(128);
911        sb.append("FragmentManager{");
912        sb.append(Integer.toHexString(System.identityHashCode(this)));
913        sb.append(" in ");
914        if (mParent != null) {
915            DebugUtils.buildShortClassTag(mParent, sb);
916        } else {
917            DebugUtils.buildShortClassTag(mHost, sb);
918        }
919        sb.append("}}");
920        return sb.toString();
921    }
922
923    @Override
924    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
925        String innerPrefix = prefix + "    ";
926
927        int N;
928        if (mActive != null) {
929            N = mActive.size();
930            if (N > 0) {
931                writer.print(prefix); writer.print("Active Fragments in ");
932                        writer.print(Integer.toHexString(System.identityHashCode(this)));
933                        writer.println(":");
934                for (int i=0; i<N; i++) {
935                    Fragment f = mActive.get(i);
936                    writer.print(prefix); writer.print("  #"); writer.print(i);
937                            writer.print(": "); writer.println(f);
938                    if (f != null) {
939                        f.dump(innerPrefix, fd, writer, args);
940                    }
941                }
942            }
943        }
944
945        if (mAdded != null) {
946            N = mAdded.size();
947            if (N > 0) {
948                writer.print(prefix); writer.println("Added Fragments:");
949                for (int i=0; i<N; i++) {
950                    Fragment f = mAdded.get(i);
951                    writer.print(prefix); writer.print("  #"); writer.print(i);
952                            writer.print(": "); writer.println(f.toString());
953                }
954            }
955        }
956
957        if (mCreatedMenus != null) {
958            N = mCreatedMenus.size();
959            if (N > 0) {
960                writer.print(prefix); writer.println("Fragments Created Menus:");
961                for (int i=0; i<N; i++) {
962                    Fragment f = mCreatedMenus.get(i);
963                    writer.print(prefix); writer.print("  #"); writer.print(i);
964                            writer.print(": "); writer.println(f.toString());
965                }
966            }
967        }
968
969        if (mBackStack != null) {
970            N = mBackStack.size();
971            if (N > 0) {
972                writer.print(prefix); writer.println("Back Stack:");
973                for (int i=0; i<N; i++) {
974                    BackStackRecord bs = mBackStack.get(i);
975                    writer.print(prefix); writer.print("  #"); writer.print(i);
976                            writer.print(": "); writer.println(bs.toString());
977                    bs.dump(innerPrefix, fd, writer, args);
978                }
979            }
980        }
981
982        synchronized (this) {
983            if (mBackStackIndices != null) {
984                N = mBackStackIndices.size();
985                if (N > 0) {
986                    writer.print(prefix); writer.println("Back Stack Indices:");
987                    for (int i=0; i<N; i++) {
988                        BackStackRecord bs = mBackStackIndices.get(i);
989                        writer.print(prefix); writer.print("  #"); writer.print(i);
990                                writer.print(": "); writer.println(bs);
991                    }
992                }
993            }
994
995            if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) {
996                writer.print(prefix); writer.print("mAvailBackStackIndices: ");
997                        writer.println(Arrays.toString(mAvailBackStackIndices.toArray()));
998            }
999        }
1000
1001        if (mPendingActions != null) {
1002            N = mPendingActions.size();
1003            if (N > 0) {
1004                writer.print(prefix); writer.println("Pending Actions:");
1005                for (int i=0; i<N; i++) {
1006                    OpGenerator r = mPendingActions.get(i);
1007                    writer.print(prefix); writer.print("  #"); writer.print(i);
1008                            writer.print(": "); writer.println(r);
1009                }
1010            }
1011        }
1012
1013        writer.print(prefix); writer.println("FragmentManager misc state:");
1014        writer.print(prefix); writer.print("  mHost="); writer.println(mHost);
1015        writer.print(prefix); writer.print("  mContainer="); writer.println(mContainer);
1016        if (mParent != null) {
1017            writer.print(prefix); writer.print("  mParent="); writer.println(mParent);
1018        }
1019        writer.print(prefix); writer.print("  mCurState="); writer.print(mCurState);
1020                writer.print(" mStateSaved="); writer.print(mStateSaved);
1021                writer.print(" mDestroyed="); writer.println(mDestroyed);
1022        if (mNeedMenuInvalidate) {
1023            writer.print(prefix); writer.print("  mNeedMenuInvalidate=");
1024                    writer.println(mNeedMenuInvalidate);
1025        }
1026        if (mNoTransactionsBecause != null) {
1027            writer.print(prefix); writer.print("  mNoTransactionsBecause=");
1028                    writer.println(mNoTransactionsBecause);
1029        }
1030        if (mAvailIndices != null && mAvailIndices.size() > 0) {
1031            writer.print(prefix); writer.print("  mAvailIndices: ");
1032                    writer.println(Arrays.toString(mAvailIndices.toArray()));
1033        }
1034    }
1035
1036    static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
1037    static final Interpolator DECELERATE_CUBIC = new DecelerateInterpolator(1.5f);
1038    static final Interpolator ACCELERATE_QUINT = new AccelerateInterpolator(2.5f);
1039    static final Interpolator ACCELERATE_CUBIC = new AccelerateInterpolator(1.5f);
1040
1041    static final int ANIM_DUR = 220;
1042
1043    static Animation makeOpenCloseAnimation(Context context, float startScale,
1044            float endScale, float startAlpha, float endAlpha) {
1045        AnimationSet set = new AnimationSet(false);
1046        ScaleAnimation scale = new ScaleAnimation(startScale, endScale, startScale, endScale,
1047                Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f);
1048        scale.setInterpolator(DECELERATE_QUINT);
1049        scale.setDuration(ANIM_DUR);
1050        set.addAnimation(scale);
1051        AlphaAnimation alpha = new AlphaAnimation(startAlpha, endAlpha);
1052        alpha.setInterpolator(DECELERATE_CUBIC);
1053        alpha.setDuration(ANIM_DUR);
1054        set.addAnimation(alpha);
1055        return set;
1056    }
1057
1058    static Animation makeFadeAnimation(Context context, float start, float end) {
1059        AlphaAnimation anim = new AlphaAnimation(start, end);
1060        anim.setInterpolator(DECELERATE_CUBIC);
1061        anim.setDuration(ANIM_DUR);
1062        return anim;
1063    }
1064
1065    Animation loadAnimation(Fragment fragment, int transit, boolean enter,
1066            int transitionStyle) {
1067        Animation animObj = fragment.onCreateAnimation(transit, enter, fragment.getNextAnim());
1068        if (animObj != null) {
1069            return animObj;
1070        }
1071
1072        if (fragment.getNextAnim() != 0) {
1073            Animation anim = AnimationUtils.loadAnimation(mHost.getContext(),
1074                    fragment.getNextAnim());
1075            if (anim != null) {
1076                return anim;
1077            }
1078        }
1079
1080        if (transit == 0) {
1081            return null;
1082        }
1083
1084        int styleIndex = transitToStyleIndex(transit, enter);
1085        if (styleIndex < 0) {
1086            return null;
1087        }
1088
1089        switch (styleIndex) {
1090            case ANIM_STYLE_OPEN_ENTER:
1091                return makeOpenCloseAnimation(mHost.getContext(), 1.125f, 1.0f, 0, 1);
1092            case ANIM_STYLE_OPEN_EXIT:
1093                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, .975f, 1, 0);
1094            case ANIM_STYLE_CLOSE_ENTER:
1095                return makeOpenCloseAnimation(mHost.getContext(), .975f, 1.0f, 0, 1);
1096            case ANIM_STYLE_CLOSE_EXIT:
1097                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, 1.075f, 1, 0);
1098            case ANIM_STYLE_FADE_ENTER:
1099                return makeFadeAnimation(mHost.getContext(), 0, 1);
1100            case ANIM_STYLE_FADE_EXIT:
1101                return makeFadeAnimation(mHost.getContext(), 1, 0);
1102        }
1103
1104        if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
1105            transitionStyle = mHost.onGetWindowAnimations();
1106        }
1107        if (transitionStyle == 0) {
1108            return null;
1109        }
1110
1111        //TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
1112        //        com.android.internal.R.styleable.FragmentAnimation);
1113        //int anim = attrs.getResourceId(styleIndex, 0);
1114        //attrs.recycle();
1115
1116        //if (anim == 0) {
1117        //    return null;
1118        //}
1119
1120        //return AnimatorInflater.loadAnimator(mActivity, anim);
1121        return null;
1122    }
1123
1124    public void performPendingDeferredStart(Fragment f) {
1125        if (f.mDeferStart) {
1126            if (mExecutingActions) {
1127                // Wait until we're done executing our pending transactions
1128                mHavePendingDeferredStart = true;
1129                return;
1130            }
1131            f.mDeferStart = false;
1132            moveToState(f, mCurState, 0, 0, false);
1133        }
1134    }
1135
1136    /**
1137     * Sets the to be animated view on hardware layer during the animation. Note
1138     * that calling this will replace any existing animation listener on the animation
1139     * with a new one, as animations do not support more than one listeners. Therefore,
1140     * animations that already have listeners should do the layer change operations
1141     * in their existing listeners, rather than calling this function.
1142     */
1143    private void setHWLayerAnimListenerIfAlpha(final View v, Animation anim) {
1144        if (v == null || anim == null) {
1145            return;
1146        }
1147        if (shouldRunOnHWLayer(v, anim)) {
1148            AnimationListener originalListener = null;
1149            try {
1150                if (sAnimationListenerField == null) {
1151                    sAnimationListenerField = Animation.class.getDeclaredField("mListener");
1152                    sAnimationListenerField.setAccessible(true);
1153                }
1154                originalListener = (AnimationListener) sAnimationListenerField.get(anim);
1155            } catch (NoSuchFieldException e) {
1156                Log.e(TAG, "No field with the name mListener is found in Animation class", e);
1157            } catch (IllegalAccessException e) {
1158                Log.e(TAG, "Cannot access Animation's mListener field", e);
1159            }
1160            // If there's already a listener set on the animation, we need wrap the new listener
1161            // around the existing listener, so that they will both get animation listener
1162            // callbacks.
1163            ViewCompat.setLayerType(v, ViewCompat.LAYER_TYPE_HARDWARE, null);
1164            anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, anim,
1165                    originalListener));
1166        }
1167    }
1168
1169    boolean isStateAtLeast(int state) {
1170        return mCurState >= state;
1171    }
1172
1173    void moveToState(Fragment f, int newState, int transit, int transitionStyle,
1174            boolean keepActive) {
1175        // Fragments that are not currently added will sit in the onCreate() state.
1176        if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
1177            newState = Fragment.CREATED;
1178        }
1179        if (f.mRemoving && newState > f.mState) {
1180            // While removing a fragment, we can't change it to a higher state.
1181            newState = f.mState;
1182        }
1183        // Defer start if requested; don't allow it to move to STARTED or higher
1184        // if it's not already started.
1185        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
1186            newState = Fragment.STOPPED;
1187        }
1188        if (f.mState < newState) {
1189            // For fragments that are created from a layout, when restoring from
1190            // state we don't want to allow them to be created until they are
1191            // being reloaded from the layout.
1192            if (f.mFromLayout && !f.mInLayout) {
1193                return;
1194            }
1195            if (f.getAnimatingAway() != null) {
1196                // The fragment is currently being animated...  but!  Now we
1197                // want to move our state back up.  Give up on waiting for the
1198                // animation, move to whatever the final state should be once
1199                // the animation is done, and then we can proceed from there.
1200                f.setAnimatingAway(null);
1201                moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
1202            }
1203            switch (f.mState) {
1204                case Fragment.INITIALIZING:
1205                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
1206                    if (f.mSavedFragmentState != null) {
1207                        f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
1208                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
1209                                FragmentManagerImpl.VIEW_STATE_TAG);
1210                        f.mTarget = getFragment(f.mSavedFragmentState,
1211                                FragmentManagerImpl.TARGET_STATE_TAG);
1212                        if (f.mTarget != null) {
1213                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
1214                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
1215                        }
1216                        f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
1217                                FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
1218                        if (!f.mUserVisibleHint) {
1219                            f.mDeferStart = true;
1220                            if (newState > Fragment.STOPPED) {
1221                                newState = Fragment.STOPPED;
1222                            }
1223                        }
1224                    }
1225                    f.mHost = mHost;
1226                    f.mParentFragment = mParent;
1227                    f.mFragmentManager = mParent != null
1228                            ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
1229                    dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
1230                    f.mCalled = false;
1231                    f.onAttach(mHost.getContext());
1232                    if (!f.mCalled) {
1233                        throw new SuperNotCalledException("Fragment " + f
1234                                + " did not call through to super.onAttach()");
1235                    }
1236                    if (f.mParentFragment == null) {
1237                        mHost.onAttachFragment(f);
1238                    } else {
1239                        f.mParentFragment.onAttachFragment(f);
1240                    }
1241                    dispatchOnFragmentAttached(f, mHost.getContext(), false);
1242
1243                    if (!f.mRetaining) {
1244                        f.performCreate(f.mSavedFragmentState);
1245                        dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
1246                    } else {
1247                        f.restoreChildFragmentState(f.mSavedFragmentState);
1248                        f.mState = Fragment.CREATED;
1249                    }
1250                    f.mRetaining = false;
1251                    if (f.mFromLayout) {
1252                        // For fragments that are part of the content view
1253                        // layout, we need to instantiate the view immediately
1254                        // and the inflater will take care of adding it.
1255                        f.mView = f.performCreateView(f.getLayoutInflater(
1256                                f.mSavedFragmentState), null, f.mSavedFragmentState);
1257                        if (f.mView != null) {
1258                            f.mInnerView = f.mView;
1259                            if (Build.VERSION.SDK_INT >= 11) {
1260                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
1261                            } else {
1262                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
1263                            }
1264                            if (f.mHidden) f.mView.setVisibility(View.GONE);
1265                            f.onViewCreated(f.mView, f.mSavedFragmentState);
1266                            dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
1267                        } else {
1268                            f.mInnerView = null;
1269                        }
1270                    }
1271                case Fragment.CREATED:
1272                    if (newState > Fragment.CREATED) {
1273                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
1274                        if (!f.mFromLayout) {
1275                            ViewGroup container = null;
1276                            if (f.mContainerId != 0) {
1277                                if (f.mContainerId == View.NO_ID) {
1278                                    throwException(new IllegalArgumentException(
1279                                            "Cannot create fragment "
1280                                                    + f
1281                                                    + " for a container view with no id"));
1282                                }
1283                                container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
1284                                if (container == null && !f.mRestored) {
1285                                    String resName;
1286                                    try {
1287                                        resName = f.getResources().getResourceName(f.mContainerId);
1288                                    } catch (NotFoundException e) {
1289                                        resName = "unknown";
1290                                    }
1291                                    throwException(new IllegalArgumentException(
1292                                            "No view found for id 0x"
1293                                            + Integer.toHexString(f.mContainerId) + " ("
1294                                            + resName
1295                                            + ") for fragment " + f));
1296                                }
1297                            }
1298                            f.mContainer = container;
1299                            f.mView = f.performCreateView(f.getLayoutInflater(
1300                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
1301                            if (f.mView != null) {
1302                                f.mInnerView = f.mView;
1303                                if (Build.VERSION.SDK_INT >= 11) {
1304                                    ViewCompat.setSaveFromParentEnabled(f.mView, false);
1305                                } else {
1306                                    f.mView = NoSaveStateFrameLayout.wrap(f.mView);
1307                                }
1308                                if (container != null) {
1309                                    container.addView(f.mView);
1310                                }
1311                                if (f.mHidden) {
1312                                    f.mView.setVisibility(View.GONE);
1313                                }
1314                                f.onViewCreated(f.mView, f.mSavedFragmentState);
1315                                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
1316                                        false);
1317                                // Only animate the view if it is visible. This is done after
1318                                // dispatchOnFragmentViewCreated in case visibility is changed
1319                                f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
1320                                        && f.mContainer != null;
1321                            } else {
1322                                f.mInnerView = null;
1323                            }
1324                        }
1325
1326                        f.performActivityCreated(f.mSavedFragmentState);
1327                        dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
1328                        if (f.mView != null) {
1329                            f.restoreViewState(f.mSavedFragmentState);
1330                        }
1331                        f.mSavedFragmentState = null;
1332                    }
1333                case Fragment.ACTIVITY_CREATED:
1334                    if (newState > Fragment.ACTIVITY_CREATED) {
1335                        f.mState = Fragment.STOPPED;
1336                    }
1337                case Fragment.STOPPED:
1338                    if (newState > Fragment.STOPPED) {
1339                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
1340                        f.performStart();
1341                        dispatchOnFragmentStarted(f, false);
1342                    }
1343                case Fragment.STARTED:
1344                    if (newState > Fragment.STARTED) {
1345                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
1346                        f.performResume();
1347                        dispatchOnFragmentResumed(f, false);
1348                        f.mSavedFragmentState = null;
1349                        f.mSavedViewState = null;
1350                    }
1351            }
1352        } else if (f.mState > newState) {
1353            switch (f.mState) {
1354                case Fragment.RESUMED:
1355                    if (newState < Fragment.RESUMED) {
1356                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
1357                        f.performPause();
1358                        dispatchOnFragmentPaused(f, false);
1359                    }
1360                case Fragment.STARTED:
1361                    if (newState < Fragment.STARTED) {
1362                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
1363                        f.performStop();
1364                        dispatchOnFragmentStopped(f, false);
1365                    }
1366                case Fragment.STOPPED:
1367                    if (newState < Fragment.STOPPED) {
1368                        if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
1369                        f.performReallyStop();
1370                    }
1371                case Fragment.ACTIVITY_CREATED:
1372                    if (newState < Fragment.ACTIVITY_CREATED) {
1373                        if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
1374                        if (f.mView != null) {
1375                            // Need to save the current view state if not
1376                            // done already.
1377                            if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
1378                                saveFragmentViewState(f);
1379                            }
1380                        }
1381                        f.performDestroyView();
1382                        dispatchOnFragmentViewDestroyed(f, false);
1383                        if (f.mView != null && f.mContainer != null) {
1384                            Animation anim = null;
1385                            if (mCurState > Fragment.INITIALIZING && !mDestroyed
1386                                    && f.mView.getVisibility() == View.VISIBLE
1387                                    && f.mPostponedAlpha >= 0) {
1388                                anim = loadAnimation(f, transit, false,
1389                                        transitionStyle);
1390                            }
1391                            f.mPostponedAlpha = 0;
1392                            if (anim != null) {
1393                                final Fragment fragment = f;
1394                                f.setAnimatingAway(f.mView);
1395                                f.setStateAfterAnimating(newState);
1396                                final View viewToAnimate = f.mView;
1397                                anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
1398                                        viewToAnimate, anim) {
1399                                    @Override
1400                                    public void onAnimationEnd(Animation animation) {
1401                                        super.onAnimationEnd(animation);
1402                                        if (fragment.getAnimatingAway() != null) {
1403                                            fragment.setAnimatingAway(null);
1404                                            moveToState(fragment, fragment.getStateAfterAnimating(),
1405                                                    0, 0, false);
1406                                        }
1407                                    }
1408                                });
1409                                f.mView.startAnimation(anim);
1410                            }
1411                            f.mContainer.removeView(f.mView);
1412                        }
1413                        f.mContainer = null;
1414                        f.mView = null;
1415                        f.mInnerView = null;
1416                    }
1417                case Fragment.CREATED:
1418                    if (newState < Fragment.CREATED) {
1419                        if (mDestroyed) {
1420                            if (f.getAnimatingAway() != null) {
1421                                // The fragment's containing activity is
1422                                // being destroyed, but this fragment is
1423                                // currently animating away.  Stop the
1424                                // animation right now -- it is not needed,
1425                                // and we can't wait any more on destroying
1426                                // the fragment.
1427                                View v = f.getAnimatingAway();
1428                                f.setAnimatingAway(null);
1429                                v.clearAnimation();
1430                            }
1431                        }
1432                        if (f.getAnimatingAway() != null) {
1433                            // We are waiting for the fragment's view to finish
1434                            // animating away.  Just make a note of the state
1435                            // the fragment now should move to once the animation
1436                            // is done.
1437                            f.setStateAfterAnimating(newState);
1438                            newState = Fragment.CREATED;
1439                        } else {
1440                            if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
1441                            if (!f.mRetaining) {
1442                                f.performDestroy();
1443                                dispatchOnFragmentDestroyed(f, false);
1444                            } else {
1445                                f.mState = Fragment.INITIALIZING;
1446                            }
1447
1448                            f.performDetach();
1449                            dispatchOnFragmentDetached(f, false);
1450                            if (!keepActive) {
1451                                if (!f.mRetaining) {
1452                                    makeInactive(f);
1453                                } else {
1454                                    f.mHost = null;
1455                                    f.mParentFragment = null;
1456                                    f.mFragmentManager = null;
1457                                }
1458                            }
1459                        }
1460                    }
1461            }
1462        }
1463
1464        if (f.mState != newState) {
1465            Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
1466                    + "expected state " + newState + " found " + f.mState);
1467            f.mState = newState;
1468        }
1469    }
1470
1471    void moveToState(Fragment f) {
1472        moveToState(f, mCurState, 0, 0, false);
1473    }
1474
1475    /**
1476     * Fragments that have been shown or hidden don't have their visibility changed or
1477     * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
1478     * calls. After fragments are brought to their final state in
1479     * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or
1480     * hidden must have their visibility changed and their animations started here.
1481     *
1482     * @param fragment The fragment with mHiddenChanged = true that should change its View's
1483     *                 visibility and start the show or hide animation.
1484     */
1485    void completeShowHideFragment(final Fragment fragment) {
1486        if (fragment.mView != null) {
1487            Animation anim = loadAnimation(fragment, fragment.getNextTransition(),
1488                    !fragment.mHidden, fragment.getNextTransitionStyle());
1489            if (anim != null) {
1490                setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1491                fragment.mView.startAnimation(anim);
1492                setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1493                anim.start();
1494            }
1495            final int visibility = fragment.mHidden && !fragment.isHideReplaced()
1496                    ? View.GONE
1497                    : View.VISIBLE;
1498            fragment.mView.setVisibility(visibility);
1499            if (fragment.isHideReplaced()) {
1500                fragment.setHideReplaced(false);
1501            }
1502        }
1503        if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
1504            mNeedMenuInvalidate = true;
1505        }
1506        fragment.mHiddenChanged = false;
1507        fragment.onHiddenChanged(fragment.mHidden);
1508    }
1509
1510    /**
1511     * Moves a fragment to its expected final state or the fragment manager's state, depending
1512     * on whether the fragment manager's state is raised properly.
1513     *
1514     * @param f The fragment to change.
1515     */
1516    void moveFragmentToExpectedState(Fragment f) {
1517        if (f == null) {
1518            return;
1519        }
1520        int nextState = mCurState;
1521        if (f.mRemoving) {
1522            if (f.isInBackStack()) {
1523                nextState = Math.min(nextState, Fragment.CREATED);
1524            } else {
1525                nextState = Math.min(nextState, Fragment.INITIALIZING);
1526            }
1527        }
1528        moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
1529
1530        if (f.mView != null) {
1531            // Move the view if it is out of order
1532            Fragment underFragment = findFragmentUnder(f);
1533            if (underFragment != null) {
1534                final View underView = underFragment.mView;
1535                // make sure this fragment is in the right order.
1536                final ViewGroup container = f.mContainer;
1537                int underIndex = container.indexOfChild(underView);
1538                int viewIndex = container.indexOfChild(f.mView);
1539                if (viewIndex < underIndex) {
1540                    container.removeViewAt(viewIndex);
1541                    container.addView(f.mView, underIndex);
1542                }
1543            }
1544            if (f.mIsNewlyAdded && f.mContainer != null) {
1545                // Make it visible and run the animations
1546                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
1547                    f.mView.setVisibility(View.VISIBLE);
1548                } else if (f.mPostponedAlpha > 0f) {
1549                    f.mView.setAlpha(f.mPostponedAlpha);
1550                }
1551                f.mPostponedAlpha = 0f;
1552                f.mIsNewlyAdded = false;
1553                // run animations:
1554                Animation anim = loadAnimation(f, f.getNextTransition(), true,
1555                        f.getNextTransitionStyle());
1556                if (anim != null) {
1557                    setHWLayerAnimListenerIfAlpha(f.mView, anim);
1558                    f.mView.startAnimation(anim);
1559                }
1560            }
1561        }
1562        if (f.mHiddenChanged) {
1563            completeShowHideFragment(f);
1564        }
1565    }
1566
1567    /**
1568     * Changes the state of the fragment manager to {@code newState}. If the fragment manager
1569     * changes state or {@code always} is {@code true}, any fragments within it have their
1570     * states updated as well.
1571     *
1572     * @param newState The new state for the fragment manager
1573     * @param always If {@code true}, all fragments update their state, even
1574     *               if {@code newState} matches the current fragment manager's state.
1575     */
1576    void moveToState(int newState, boolean always) {
1577        if (mHost == null && newState != Fragment.INITIALIZING) {
1578            throw new IllegalStateException("No activity");
1579        }
1580
1581        if (!always && newState == mCurState) {
1582            return;
1583        }
1584
1585        mCurState = newState;
1586
1587        if (mActive != null) {
1588            boolean loadersRunning = false;
1589
1590            // Must add them in the proper order. mActive fragments may be out of order
1591            if (mAdded != null) {
1592                final int numAdded = mAdded.size();
1593                for (int i = 0; i < numAdded; i++) {
1594                    Fragment f = mAdded.get(i);
1595                    moveFragmentToExpectedState(f);
1596                    if (f.mLoaderManager != null) {
1597                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1598                    }
1599                }
1600            }
1601
1602            // Now iterate through all active fragments. These will include those that are removed
1603            // and detached.
1604            final int numActive = mActive.size();
1605            for (int i = 0; i < numActive; i++) {
1606                Fragment f = mActive.get(i);
1607                if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
1608                    moveFragmentToExpectedState(f);
1609                    if (f.mLoaderManager != null) {
1610                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1611                    }
1612                }
1613            }
1614
1615            if (!loadersRunning) {
1616                startPendingDeferredFragments();
1617            }
1618
1619            if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
1620                mHost.onSupportInvalidateOptionsMenu();
1621                mNeedMenuInvalidate = false;
1622            }
1623        }
1624    }
1625
1626    void startPendingDeferredFragments() {
1627        if (mActive == null) return;
1628
1629        for (int i=0; i<mActive.size(); i++) {
1630            Fragment f = mActive.get(i);
1631            if (f != null) {
1632                performPendingDeferredStart(f);
1633            }
1634        }
1635    }
1636
1637    void makeActive(Fragment f) {
1638        if (f.mIndex >= 0) {
1639            return;
1640        }
1641
1642        if (mAvailIndices == null || mAvailIndices.size() <= 0) {
1643            if (mActive == null) {
1644                mActive = new ArrayList<Fragment>();
1645            }
1646            f.setIndex(mActive.size(), mParent);
1647            mActive.add(f);
1648
1649        } else {
1650            f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
1651            mActive.set(f.mIndex, f);
1652        }
1653        if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
1654    }
1655
1656    void makeInactive(Fragment f) {
1657        if (f.mIndex < 0) {
1658            return;
1659        }
1660
1661        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
1662        mActive.set(f.mIndex, null);
1663        if (mAvailIndices == null) {
1664            mAvailIndices = new ArrayList<Integer>();
1665        }
1666        mAvailIndices.add(f.mIndex);
1667        mHost.inactivateFragment(f.mWho);
1668        f.initState();
1669    }
1670
1671    public void addFragment(Fragment fragment, boolean moveToStateNow) {
1672        if (mAdded == null) {
1673            mAdded = new ArrayList<Fragment>();
1674        }
1675        if (DEBUG) Log.v(TAG, "add: " + fragment);
1676        makeActive(fragment);
1677        if (!fragment.mDetached) {
1678            if (mAdded.contains(fragment)) {
1679                throw new IllegalStateException("Fragment already added: " + fragment);
1680            }
1681            mAdded.add(fragment);
1682            fragment.mAdded = true;
1683            fragment.mRemoving = false;
1684            if (fragment.mView == null) {
1685                fragment.mHiddenChanged = false;
1686            }
1687            if (fragment.mHasMenu && fragment.mMenuVisible) {
1688                mNeedMenuInvalidate = true;
1689            }
1690            if (moveToStateNow) {
1691                moveToState(fragment);
1692            }
1693        }
1694    }
1695
1696    public void removeFragment(Fragment fragment) {
1697        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
1698        final boolean inactive = !fragment.isInBackStack();
1699        if (!fragment.mDetached || inactive) {
1700            if (mAdded != null) {
1701                mAdded.remove(fragment);
1702            }
1703            if (fragment.mHasMenu && fragment.mMenuVisible) {
1704                mNeedMenuInvalidate = true;
1705            }
1706            fragment.mAdded = false;
1707            fragment.mRemoving = true;
1708        }
1709    }
1710
1711    /**
1712     * Marks a fragment as hidden to be later animated in with
1713     * {@link #completeShowHideFragment(Fragment)}.
1714     *
1715     * @param fragment The fragment to be shown.
1716     */
1717    public void hideFragment(Fragment fragment) {
1718        if (DEBUG) Log.v(TAG, "hide: " + fragment);
1719        if (!fragment.mHidden) {
1720            fragment.mHidden = true;
1721            // Toggle hidden changed so that if a fragment goes through show/hide/show
1722            // it doesn't go through the animation.
1723            fragment.mHiddenChanged = !fragment.mHiddenChanged;
1724        }
1725    }
1726
1727    /**
1728     * Marks a fragment as shown to be later animated in with
1729     * {@link #completeShowHideFragment(Fragment)}.
1730     *
1731     * @param fragment The fragment to be shown.
1732     */
1733    public void showFragment(Fragment fragment) {
1734        if (DEBUG) Log.v(TAG, "show: " + fragment);
1735        if (fragment.mHidden) {
1736            fragment.mHidden = false;
1737            // Toggle hidden changed so that if a fragment goes through show/hide/show
1738            // it doesn't go through the animation.
1739            fragment.mHiddenChanged = !fragment.mHiddenChanged;
1740        }
1741    }
1742
1743    public void detachFragment(Fragment fragment) {
1744        if (DEBUG) Log.v(TAG, "detach: " + fragment);
1745        if (!fragment.mDetached) {
1746            fragment.mDetached = true;
1747            if (fragment.mAdded) {
1748                // We are not already in back stack, so need to remove the fragment.
1749                if (mAdded != null) {
1750                    if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
1751                    mAdded.remove(fragment);
1752                }
1753                if (fragment.mHasMenu && fragment.mMenuVisible) {
1754                    mNeedMenuInvalidate = true;
1755                }
1756                fragment.mAdded = false;
1757            }
1758        }
1759    }
1760
1761    public void attachFragment(Fragment fragment) {
1762        if (DEBUG) Log.v(TAG, "attach: " + fragment);
1763        if (fragment.mDetached) {
1764            fragment.mDetached = false;
1765            if (!fragment.mAdded) {
1766                if (mAdded == null) {
1767                    mAdded = new ArrayList<Fragment>();
1768                }
1769                if (mAdded.contains(fragment)) {
1770                    throw new IllegalStateException("Fragment already added: " + fragment);
1771                }
1772                if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
1773                mAdded.add(fragment);
1774                fragment.mAdded = true;
1775                if (fragment.mHasMenu && fragment.mMenuVisible) {
1776                    mNeedMenuInvalidate = true;
1777                }
1778            }
1779        }
1780    }
1781
1782    @Override
1783    public Fragment findFragmentById(int id) {
1784        if (mAdded != null) {
1785            // First look through added fragments.
1786            for (int i=mAdded.size()-1; i>=0; i--) {
1787                Fragment f = mAdded.get(i);
1788                if (f != null && f.mFragmentId == id) {
1789                    return f;
1790                }
1791            }
1792        }
1793        if (mActive != null) {
1794            // Now for any known fragment.
1795            for (int i=mActive.size()-1; i>=0; i--) {
1796                Fragment f = mActive.get(i);
1797                if (f != null && f.mFragmentId == id) {
1798                    return f;
1799                }
1800            }
1801        }
1802        return null;
1803    }
1804
1805    @Override
1806    public Fragment findFragmentByTag(String tag) {
1807        if (mAdded != null && tag != null) {
1808            // First look through added fragments.
1809            for (int i=mAdded.size()-1; i>=0; i--) {
1810                Fragment f = mAdded.get(i);
1811                if (f != null && tag.equals(f.mTag)) {
1812                    return f;
1813                }
1814            }
1815        }
1816        if (mActive != null && tag != null) {
1817            // Now for any known fragment.
1818            for (int i=mActive.size()-1; i>=0; i--) {
1819                Fragment f = mActive.get(i);
1820                if (f != null && tag.equals(f.mTag)) {
1821                    return f;
1822                }
1823            }
1824        }
1825        return null;
1826    }
1827
1828    public Fragment findFragmentByWho(String who) {
1829        if (mActive != null && who != null) {
1830            for (int i=mActive.size()-1; i>=0; i--) {
1831                Fragment f = mActive.get(i);
1832                if (f != null && (f=f.findFragmentByWho(who)) != null) {
1833                    return f;
1834                }
1835            }
1836        }
1837        return null;
1838    }
1839
1840    private void checkStateLoss() {
1841        if (mStateSaved) {
1842            throw new IllegalStateException(
1843                    "Can not perform this action after onSaveInstanceState");
1844        }
1845        if (mNoTransactionsBecause != null) {
1846            throw new IllegalStateException(
1847                    "Can not perform this action inside of " + mNoTransactionsBecause);
1848        }
1849    }
1850
1851    /**
1852     * Adds an action to the queue of pending actions.
1853     *
1854     * @param action the action to add
1855     * @param allowStateLoss whether to allow loss of state information
1856     * @throws IllegalStateException if the activity has been destroyed
1857     */
1858    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
1859        if (!allowStateLoss) {
1860            checkStateLoss();
1861        }
1862        synchronized (this) {
1863            if (mDestroyed || mHost == null) {
1864                throw new IllegalStateException("Activity has been destroyed");
1865            }
1866            if (mPendingActions == null) {
1867                mPendingActions = new ArrayList<>();
1868            }
1869            mPendingActions.add(action);
1870            scheduleCommit();
1871        }
1872    }
1873
1874    /**
1875     * Schedules the execution when one hasn't been scheduled already. This should happen
1876     * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
1877     * a postponed transaction has been started with
1878     * {@link Fragment#startPostponedEnterTransition()}
1879     */
1880    private void scheduleCommit() {
1881        synchronized (this) {
1882            boolean postponeReady =
1883                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
1884            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
1885            if (postponeReady || pendingReady) {
1886                mHost.getHandler().removeCallbacks(mExecCommit);
1887                mHost.getHandler().post(mExecCommit);
1888            }
1889        }
1890    }
1891
1892    public int allocBackStackIndex(BackStackRecord bse) {
1893        synchronized (this) {
1894            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
1895                if (mBackStackIndices == null) {
1896                    mBackStackIndices = new ArrayList<BackStackRecord>();
1897                }
1898                int index = mBackStackIndices.size();
1899                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
1900                mBackStackIndices.add(bse);
1901                return index;
1902
1903            } else {
1904                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
1905                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
1906                mBackStackIndices.set(index, bse);
1907                return index;
1908            }
1909        }
1910    }
1911
1912    public void setBackStackIndex(int index, BackStackRecord bse) {
1913        synchronized (this) {
1914            if (mBackStackIndices == null) {
1915                mBackStackIndices = new ArrayList<BackStackRecord>();
1916            }
1917            int N = mBackStackIndices.size();
1918            if (index < N) {
1919                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
1920                mBackStackIndices.set(index, bse);
1921            } else {
1922                while (N < index) {
1923                    mBackStackIndices.add(null);
1924                    if (mAvailBackStackIndices == null) {
1925                        mAvailBackStackIndices = new ArrayList<Integer>();
1926                    }
1927                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
1928                    mAvailBackStackIndices.add(N);
1929                    N++;
1930                }
1931                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
1932                mBackStackIndices.add(bse);
1933            }
1934        }
1935    }
1936
1937    public void freeBackStackIndex(int index) {
1938        synchronized (this) {
1939            mBackStackIndices.set(index, null);
1940            if (mAvailBackStackIndices == null) {
1941                mAvailBackStackIndices = new ArrayList<Integer>();
1942            }
1943            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
1944            mAvailBackStackIndices.add(index);
1945        }
1946    }
1947
1948    /**
1949     * Broken out from exec*, this prepares for gathering and executing operations.
1950     *
1951     * @param allowStateLoss true if state loss should be ignored or false if it should be
1952     *                       checked.
1953     */
1954    private void ensureExecReady(boolean allowStateLoss) {
1955        if (mExecutingActions) {
1956            throw new IllegalStateException("FragmentManager is already executing transactions");
1957        }
1958
1959        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
1960            throw new IllegalStateException("Must be called from main thread of fragment host");
1961        }
1962
1963        if (!allowStateLoss) {
1964            checkStateLoss();
1965        }
1966
1967        if (mTmpRecords == null) {
1968            mTmpRecords = new ArrayList<>();
1969            mTmpIsPop = new ArrayList<>();
1970        }
1971        executePostponedTransaction(null, null);
1972    }
1973
1974    public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
1975        ensureExecReady(allowStateLoss);
1976        if (action.generateOps(mTmpRecords, mTmpIsPop)) {
1977            mExecutingActions = true;
1978            try {
1979                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
1980            } finally {
1981                cleanupExec();
1982            }
1983        }
1984
1985        doPendingDeferredStart();
1986    }
1987
1988    /**
1989     * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures
1990     * used in executing operations.
1991     */
1992    private void cleanupExec() {
1993        mExecutingActions = false;
1994        mTmpIsPop.clear();
1995        mTmpRecords.clear();
1996    }
1997
1998    /**
1999     * Only call from main thread!
2000     */
2001    public boolean execPendingActions() {
2002        ensureExecReady(true);
2003
2004        boolean didSomething = false;
2005        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
2006            mExecutingActions = true;
2007            try {
2008                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
2009            } finally {
2010                cleanupExec();
2011            }
2012            didSomething = true;
2013        }
2014
2015        doPendingDeferredStart();
2016
2017        return didSomething;
2018    }
2019
2020    /**
2021     * Complete the execution of transactions that have previously been postponed, but are
2022     * now ready.
2023     */
2024    private void executePostponedTransaction(ArrayList<BackStackRecord> records,
2025            ArrayList<Boolean> isRecordPop) {
2026        int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size();
2027        for (int i = 0; i < numPostponed; i++) {
2028            StartEnterTransitionListener listener = mPostponedTransactions.get(i);
2029            if (records != null && !listener.mIsBack) {
2030                int index = records.indexOf(listener.mRecord);
2031                if (index != -1 && isRecordPop.get(index)) {
2032                    listener.cancelTransaction();
2033                    continue;
2034                }
2035            }
2036            if (listener.isReady() || (records != null
2037                    && listener.mRecord.interactsWith(records, 0, records.size()))) {
2038                mPostponedTransactions.remove(i);
2039                i--;
2040                numPostponed--;
2041                int index;
2042                if (records != null && !listener.mIsBack
2043                        && (index = records.indexOf(listener.mRecord)) != -1
2044                        && isRecordPop.get(index)) {
2045                    // This is popping a postponed transaction
2046                    listener.cancelTransaction();
2047                } else {
2048                    listener.completeTransaction();
2049                }
2050            }
2051        }
2052    }
2053
2054    /**
2055     * Optimizes BackStackRecord operations. This method merges operations of proximate records
2056     * that allow optimization. See {@link FragmentTransaction#setAllowOptimization(boolean)}.
2057     * <p>
2058     * For example, a transaction that adds to the back stack and then another that pops that
2059     * back stack record will be optimized.
2060     * <p>
2061     * Likewise, two transactions committed that are executed at the same time will be optimized
2062     * as well as two pop operations executed together.
2063     *
2064     * @param records The records pending execution
2065     * @param isRecordPop The direction that these records are being run.
2066     */
2067    private void optimizeAndExecuteOps(ArrayList<BackStackRecord> records,
2068            ArrayList<Boolean> isRecordPop) {
2069        if (records == null || records.isEmpty()) {
2070            return;
2071        }
2072
2073        if (isRecordPop == null || records.size() != isRecordPop.size()) {
2074            throw new IllegalStateException("Internal error with the back stack records");
2075        }
2076
2077        // Force start of any postponed transactions that interact with scheduled transactions:
2078        executePostponedTransaction(records, isRecordPop);
2079
2080        final int numRecords = records.size();
2081        int startIndex = 0;
2082        for (int recordNum = 0; recordNum < numRecords; recordNum++) {
2083            final boolean canOptimize = records.get(recordNum).mAllowOptimization;
2084            if (!canOptimize) {
2085                // execute all previous transactions
2086                if (startIndex != recordNum) {
2087                    executeOpsTogether(records, isRecordPop, startIndex, recordNum);
2088                }
2089                // execute all unoptimized together
2090                int optimizeEnd;
2091                for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
2092                    if (records.get(optimizeEnd).mAllowOptimization) {
2093                        break;
2094                    }
2095                }
2096                executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
2097                startIndex = optimizeEnd;
2098                recordNum = optimizeEnd - 1;
2099            }
2100        }
2101        if (startIndex != numRecords) {
2102            executeOpsTogether(records, isRecordPop, startIndex, numRecords);
2103        }
2104    }
2105
2106    /**
2107     * Optimizes a subset of a list of BackStackRecords, all of which either allow optimization or
2108     * do not allow optimization.
2109     * @param records A list of BackStackRecords that are to be optimized
2110     * @param isRecordPop The direction that these records are being run.
2111     * @param startIndex The index of the first record in <code>records</code> to be optimized
2112     * @param endIndex One more than the final record index in <code>records</code> to optimize.
2113     */
2114    private void executeOpsTogether(ArrayList<BackStackRecord> records,
2115            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
2116        final boolean allowOptimization = records.get(startIndex).mAllowOptimization;
2117        boolean addToBackStack = false;
2118        if (mTmpAddedFragments == null) {
2119            mTmpAddedFragments = new ArrayList<>();
2120        } else {
2121            mTmpAddedFragments.clear();
2122        }
2123        if (mAdded != null) {
2124            mTmpAddedFragments.addAll(mAdded);
2125        }
2126        for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
2127            final BackStackRecord record = records.get(recordNum);
2128            final boolean isPop = isRecordPop.get(recordNum);
2129            if (!isPop) {
2130                record.expandReplaceOps(mTmpAddedFragments);
2131            }
2132            final int bumpAmount = isPop ? -1 : 1;
2133            record.bumpBackStackNesting(bumpAmount);
2134            addToBackStack = addToBackStack || record.mAddToBackStack;
2135        }
2136        mTmpAddedFragments.clear();
2137
2138        if (!allowOptimization) {
2139            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
2140                    false);
2141        }
2142        executeOps(records, isRecordPop, startIndex, endIndex);
2143
2144        int postponeIndex = endIndex;
2145        if (allowOptimization) {
2146            ArraySet<Fragment> addedFragments = new ArraySet<>();
2147            addAddedFragments(addedFragments);
2148            postponeIndex = postponePostponableTransactions(records, isRecordPop,
2149                    startIndex, endIndex, addedFragments);
2150            makeRemovedFragmentsInvisible(addedFragments);
2151        }
2152
2153        if (postponeIndex != startIndex && allowOptimization) {
2154            // need to run something now
2155            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
2156                    postponeIndex, true);
2157            moveToState(mCurState, true);
2158        }
2159
2160        for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
2161            final BackStackRecord record = records.get(recordNum);
2162            final boolean isPop = isRecordPop.get(recordNum);
2163            if (isPop && record.mIndex >= 0) {
2164                freeBackStackIndex(record.mIndex);
2165                record.mIndex = -1;
2166            }
2167        }
2168        if (addToBackStack) {
2169            reportBackStackChanged();
2170        }
2171    }
2172
2173    /**
2174     * Any fragments that were removed because they have been postponed should have their views
2175     * made invisible by setting their alpha to 0 on API >= 11 or setting visibility to INVISIBLE
2176     * on API < 11.
2177     *
2178     * @param fragments The fragments that were added during operation execution. Only the ones
2179     *                  that are no longer added will have their alpha changed.
2180     */
2181    private void makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments) {
2182        final int numAdded = fragments.size();
2183        for (int i = 0; i < numAdded; i++) {
2184            final Fragment fragment = fragments.valueAt(i);
2185            if (!fragment.mAdded) {
2186                final View view = fragment.getView();
2187                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
2188                    fragment.getView().setVisibility(View.INVISIBLE);
2189                } else {
2190                    fragment.mPostponedAlpha = view.getAlpha();
2191                    view.setAlpha(0f);
2192                }
2193            }
2194        }
2195    }
2196
2197    /**
2198     * Examine all transactions and determine which ones are marked as postponed. Those will
2199     * have their operations rolled back and moved to the end of the record list (up to endIndex).
2200     * It will also add the postponed transaction to the queue.
2201     *
2202     * @param records A list of BackStackRecords that should be checked.
2203     * @param isRecordPop The direction that these records are being run.
2204     * @param startIndex The index of the first record in <code>records</code> to be checked
2205     * @param endIndex One more than the final record index in <code>records</code> to be checked.
2206     * @return The index of the first postponed transaction or endIndex if no transaction was
2207     * postponed.
2208     */
2209    private int postponePostponableTransactions(ArrayList<BackStackRecord> records,
2210            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex,
2211            ArraySet<Fragment> added) {
2212        int postponeIndex = endIndex;
2213        for (int i = endIndex - 1; i >= startIndex; i--) {
2214            final BackStackRecord record = records.get(i);
2215            final boolean isPop = isRecordPop.get(i);
2216            boolean isPostponed = record.isPostponed()
2217                    && !record.interactsWith(records, i + 1, endIndex);
2218            if (isPostponed) {
2219                if (mPostponedTransactions == null) {
2220                    mPostponedTransactions = new ArrayList<>();
2221                }
2222                StartEnterTransitionListener listener =
2223                        new StartEnterTransitionListener(record, isPop);
2224                mPostponedTransactions.add(listener);
2225                record.setOnStartPostponedListener(listener);
2226
2227                // roll back the transaction
2228                if (isPop) {
2229                    record.executeOps();
2230                } else {
2231                    record.executePopOps();
2232                }
2233
2234                // move to the end
2235                postponeIndex--;
2236                if (i != postponeIndex) {
2237                    records.remove(i);
2238                    records.add(postponeIndex, record);
2239                }
2240
2241                // different views may be visible now
2242                addAddedFragments(added);
2243            }
2244        }
2245        return postponeIndex;
2246    }
2247
2248    /**
2249     * When a postponed transaction is ready to be started, this completes the transaction,
2250     * removing, hiding, or showing views as well as starting the animations and transitions.
2251     * <p>
2252     * {@code runtransitions} is set to false when the transaction postponement was interrupted
2253     * abnormally -- normally by a new transaction being started that affects the postponed
2254     * transaction.
2255     *
2256     * @param record The transaction to run
2257     * @param isPop true if record is popping or false if it is adding
2258     * @param runTransitions true if the fragment transition should be run or false otherwise.
2259     * @param moveToState true if the state should be changed after executing the operations.
2260     *                    This is false when the transaction is canceled when a postponed
2261     *                    transaction is popped.
2262     */
2263    private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions,
2264            boolean moveToState) {
2265        ArrayList<BackStackRecord> records = new ArrayList<>(1);
2266        ArrayList<Boolean> isRecordPop = new ArrayList<>(1);
2267        records.add(record);
2268        isRecordPop.add(isPop);
2269        executeOps(records, isRecordPop, 0, 1);
2270        if (runTransitions) {
2271            FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
2272        }
2273        if (moveToState) {
2274            moveToState(mCurState, true);
2275        }
2276
2277        if (mActive != null) {
2278            final int numActive = mActive.size();
2279            for (int i = 0; i < numActive; i++) {
2280                // Allow added fragments to be removed during the pop since we aren't going
2281                // to move them to the final state with moveToState(mCurState).
2282                Fragment fragment = mActive.get(i);
2283                if (fragment != null && fragment.mView != null && fragment.mIsNewlyAdded
2284                        && record.interactsWith(fragment.mContainerId)) {
2285                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
2286                            && fragment.mPostponedAlpha > 0) {
2287                        fragment.mView.setAlpha(fragment.mPostponedAlpha);
2288                    }
2289                    if (moveToState) {
2290                        fragment.mPostponedAlpha = 0;
2291                    } else {
2292                        fragment.mPostponedAlpha = -1;
2293                        fragment.mIsNewlyAdded = false;
2294                    }
2295                }
2296            }
2297        }
2298    }
2299
2300    /**
2301     * Find a fragment within the fragment's container whose View should be below the passed
2302     * fragment. {@code null} is returned when the fragment has no View or if there should be
2303     * no fragment with a View below the given fragment.
2304     *
2305     * As an example, if mAdded has two Fragments with Views sharing the same container:
2306     * FragmentA
2307     * FragmentB
2308     *
2309     * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA
2310     * had no View, null would be returned.
2311     *
2312     * @param f The fragment that may be on top of another fragment.
2313     * @return The fragment with a View under f, if one exists or null if f has no View or
2314     * there are no fragments with Views in the same container.
2315     */
2316    private Fragment findFragmentUnder(Fragment f) {
2317        final ViewGroup container = f.mContainer;
2318        final View view = f.mView;
2319
2320        if (container == null || view == null) {
2321            return null;
2322        }
2323
2324        final int fragmentIndex = mAdded.indexOf(f);
2325        for (int i = fragmentIndex - 1; i >= 0; i--) {
2326            Fragment underFragment = mAdded.get(i);
2327            if (underFragment.mContainer == container && underFragment.mView != null) {
2328                // Found the fragment under this one
2329                return underFragment;
2330            }
2331        }
2332        return null;
2333    }
2334
2335    /**
2336     * Run the operations in the BackStackRecords, either to push or pop.
2337     *
2338     * @param records The list of records whose operations should be run.
2339     * @param isRecordPop The direction that these records are being run.
2340     * @param startIndex The index of the first entry in records to run.
2341     * @param endIndex One past the index of the final entry in records to run.
2342     */
2343    private static void executeOps(ArrayList<BackStackRecord> records,
2344            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
2345        for (int i = startIndex; i < endIndex; i++) {
2346            final BackStackRecord record = records.get(i);
2347            final boolean isPop = isRecordPop.get(i);
2348            if (isPop) {
2349                record.executePopOps();
2350            } else {
2351                record.executeOps();
2352            }
2353        }
2354    }
2355
2356    /**
2357     * Ensure that fragments that are added are moved to at least the CREATED state.
2358     * Any newly-added Views are inserted into {@code added} so that the Transaction can be
2359     * postponed with {@link Fragment#postponeEnterTransition()}. They will later be made
2360     * invisible (by setting their alpha to 0) if they have been removed when postponed.
2361     */
2362    private void addAddedFragments(ArraySet<Fragment> added) {
2363        if (mCurState < Fragment.CREATED) {
2364            return;
2365        }
2366        // We want to leave the fragment in the started state
2367        final int state = Math.min(mCurState, Fragment.STARTED);
2368        final int numAdded = mAdded == null ? 0 : mAdded.size();
2369        for (int i = 0; i < numAdded; i++) {
2370            Fragment fragment = mAdded.get(i);
2371            if (fragment.mState < state) {
2372                moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(),
2373                        false);
2374                if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) {
2375                    added.add(fragment);
2376                }
2377            }
2378        }
2379    }
2380
2381    /**
2382     * Starts all postponed transactions regardless of whether they are ready or not.
2383     */
2384    private void forcePostponedTransactions() {
2385        if (mPostponedTransactions != null) {
2386            while (!mPostponedTransactions.isEmpty()) {
2387                mPostponedTransactions.remove(0).completeTransaction();
2388            }
2389        }
2390    }
2391
2392    /**
2393     * Ends the animations of fragments so that they immediately reach the end state.
2394     * This is used prior to saving the state so that the correct state is saved.
2395     */
2396    private void endAnimatingAwayFragments() {
2397        final int numFragments = mActive == null ? 0 : mActive.size();
2398        for (int i = 0; i < numFragments; i++) {
2399            Fragment fragment = mActive.get(i);
2400            if (fragment != null && fragment.getAnimatingAway() != null) {
2401                // Give up waiting for the animation and just end it.
2402                final int stateAfterAnimating = fragment.getStateAfterAnimating();
2403                final View animatingAway = fragment.getAnimatingAway();
2404                fragment.setAnimatingAway(null);
2405                Animation animation = animatingAway.getAnimation();
2406                if (animation != null) {
2407                    animation.cancel();
2408                }
2409                moveToState(fragment, stateAfterAnimating, 0, 0, false);
2410            }
2411        }
2412    }
2413
2414    /**
2415     * Adds all records in the pending actions to records and whether they are add or pop
2416     * operations to isPop. After executing, the pending actions will be empty.
2417     *
2418     * @param records All pending actions will generate BackStackRecords added to this.
2419     *                This contains the transactions, in order, to execute.
2420     * @param isPop All pending actions will generate booleans to add to this. This contains
2421     *              an entry for each entry in records to indicate whether or not it is a
2422     *              pop action.
2423     */
2424    private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
2425            ArrayList<Boolean> isPop) {
2426        int numActions;
2427        synchronized (this) {
2428            if (mPendingActions == null || mPendingActions.size() == 0) {
2429                return false;
2430            }
2431
2432            numActions = mPendingActions.size();
2433            for (int i = 0; i < numActions; i++) {
2434                mPendingActions.get(i).generateOps(records, isPop);
2435            }
2436            mPendingActions.clear();
2437            mHost.getHandler().removeCallbacks(mExecCommit);
2438        }
2439        return numActions > 0;
2440    }
2441
2442    void doPendingDeferredStart() {
2443        if (mHavePendingDeferredStart) {
2444            boolean loadersRunning = false;
2445            for (int i = 0; i < mActive.size(); i++) {
2446                Fragment f = mActive.get(i);
2447                if (f != null && f.mLoaderManager != null) {
2448                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
2449                }
2450            }
2451            if (!loadersRunning) {
2452                mHavePendingDeferredStart = false;
2453                startPendingDeferredFragments();
2454            }
2455        }
2456    }
2457
2458    void reportBackStackChanged() {
2459        if (mBackStackChangeListeners != null) {
2460            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
2461                mBackStackChangeListeners.get(i).onBackStackChanged();
2462            }
2463        }
2464    }
2465
2466    void addBackStackState(BackStackRecord state) {
2467        if (mBackStack == null) {
2468            mBackStack = new ArrayList<BackStackRecord>();
2469        }
2470        mBackStack.add(state);
2471        reportBackStackChanged();
2472    }
2473
2474    @SuppressWarnings("unused")
2475    boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
2476            String name, int id, int flags) {
2477        if (mBackStack == null) {
2478            return false;
2479        }
2480        if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
2481            int last = mBackStack.size() - 1;
2482            if (last < 0) {
2483                return false;
2484            }
2485            records.add(mBackStack.remove(last));
2486            isRecordPop.add(true);
2487        } else {
2488            int index = -1;
2489            if (name != null || id >= 0) {
2490                // If a name or ID is specified, look for that place in
2491                // the stack.
2492                index = mBackStack.size()-1;
2493                while (index >= 0) {
2494                    BackStackRecord bss = mBackStack.get(index);
2495                    if (name != null && name.equals(bss.getName())) {
2496                        break;
2497                    }
2498                    if (id >= 0 && id == bss.mIndex) {
2499                        break;
2500                    }
2501                    index--;
2502                }
2503                if (index < 0) {
2504                    return false;
2505                }
2506                if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
2507                    index--;
2508                    // Consume all following entries that match.
2509                    while (index >= 0) {
2510                        BackStackRecord bss = mBackStack.get(index);
2511                        if ((name != null && name.equals(bss.getName()))
2512                                || (id >= 0 && id == bss.mIndex)) {
2513                            index--;
2514                            continue;
2515                        }
2516                        break;
2517                    }
2518                }
2519            }
2520            if (index == mBackStack.size()-1) {
2521                return false;
2522            }
2523            for (int i = mBackStack.size() - 1; i > index; i--) {
2524                records.add(mBackStack.remove(i));
2525                isRecordPop.add(true);
2526            }
2527        }
2528        return true;
2529    }
2530
2531    FragmentManagerNonConfig retainNonConfig() {
2532        ArrayList<Fragment> fragments = null;
2533        ArrayList<FragmentManagerNonConfig> childFragments = null;
2534        if (mActive != null) {
2535            for (int i=0; i<mActive.size(); i++) {
2536                Fragment f = mActive.get(i);
2537                if (f != null) {
2538                    if (f.mRetainInstance) {
2539                        if (fragments == null) {
2540                            fragments = new ArrayList<Fragment>();
2541                        }
2542                        fragments.add(f);
2543                        f.mRetaining = true;
2544                        f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
2545                        if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
2546                    }
2547                    boolean addedChild = false;
2548                    if (f.mChildFragmentManager != null) {
2549                        FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
2550                        if (child != null) {
2551                            if (childFragments == null) {
2552                                childFragments = new ArrayList<FragmentManagerNonConfig>();
2553                                for (int j = 0; j < i; j++) {
2554                                    childFragments.add(null);
2555                                }
2556                            }
2557                            childFragments.add(child);
2558                            addedChild = true;
2559                        }
2560                    }
2561                    if (childFragments != null && !addedChild) {
2562                        childFragments.add(null);
2563                    }
2564                }
2565            }
2566        }
2567        if (fragments == null && childFragments == null) {
2568            return null;
2569        }
2570        return new FragmentManagerNonConfig(fragments, childFragments);
2571    }
2572
2573    void saveFragmentViewState(Fragment f) {
2574        if (f.mInnerView == null) {
2575            return;
2576        }
2577        if (mStateArray == null) {
2578            mStateArray = new SparseArray<Parcelable>();
2579        } else {
2580            mStateArray.clear();
2581        }
2582        f.mInnerView.saveHierarchyState(mStateArray);
2583        if (mStateArray.size() > 0) {
2584            f.mSavedViewState = mStateArray;
2585            mStateArray = null;
2586        }
2587    }
2588
2589    Bundle saveFragmentBasicState(Fragment f) {
2590        Bundle result = null;
2591
2592        if (mStateBundle == null) {
2593            mStateBundle = new Bundle();
2594        }
2595        f.performSaveInstanceState(mStateBundle);
2596        dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
2597        if (!mStateBundle.isEmpty()) {
2598            result = mStateBundle;
2599            mStateBundle = null;
2600        }
2601
2602        if (f.mView != null) {
2603            saveFragmentViewState(f);
2604        }
2605        if (f.mSavedViewState != null) {
2606            if (result == null) {
2607                result = new Bundle();
2608            }
2609            result.putSparseParcelableArray(
2610                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
2611        }
2612        if (!f.mUserVisibleHint) {
2613            if (result == null) {
2614                result = new Bundle();
2615            }
2616            // Only add this if it's not the default value
2617            result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
2618        }
2619
2620        return result;
2621    }
2622
2623    Parcelable saveAllState() {
2624        // Make sure all pending operations have now been executed to get
2625        // our state update-to-date.
2626        forcePostponedTransactions();
2627        endAnimatingAwayFragments();
2628        execPendingActions();
2629
2630        if (HONEYCOMB) {
2631            // As of Honeycomb, we save state after pausing.  Prior to that
2632            // it is before pausing.  With fragments this is an issue, since
2633            // there are many things you may do after pausing but before
2634            // stopping that change the fragment state.  For those older
2635            // devices, we will not at this point say that we have saved
2636            // the state, so we will allow them to continue doing fragment
2637            // transactions.  This retains the same semantics as Honeycomb,
2638            // though you do have the risk of losing the very most recent state
2639            // if the process is killed...  we'll live with that.
2640            mStateSaved = true;
2641        }
2642
2643        if (mActive == null || mActive.size() <= 0) {
2644            return null;
2645        }
2646
2647        // First collect all active fragments.
2648        int N = mActive.size();
2649        FragmentState[] active = new FragmentState[N];
2650        boolean haveFragments = false;
2651        for (int i=0; i<N; i++) {
2652            Fragment f = mActive.get(i);
2653            if (f != null) {
2654                if (f.mIndex < 0) {
2655                    throwException(new IllegalStateException(
2656                            "Failure saving state: active " + f
2657                            + " has cleared index: " + f.mIndex));
2658                }
2659
2660                haveFragments = true;
2661
2662                FragmentState fs = new FragmentState(f);
2663                active[i] = fs;
2664
2665                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
2666                    fs.mSavedFragmentState = saveFragmentBasicState(f);
2667
2668                    if (f.mTarget != null) {
2669                        if (f.mTarget.mIndex < 0) {
2670                            throwException(new IllegalStateException(
2671                                    "Failure saving state: " + f
2672                                    + " has target not in fragment manager: " + f.mTarget));
2673                        }
2674                        if (fs.mSavedFragmentState == null) {
2675                            fs.mSavedFragmentState = new Bundle();
2676                        }
2677                        putFragment(fs.mSavedFragmentState,
2678                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
2679                        if (f.mTargetRequestCode != 0) {
2680                            fs.mSavedFragmentState.putInt(
2681                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
2682                                    f.mTargetRequestCode);
2683                        }
2684                    }
2685
2686                } else {
2687                    fs.mSavedFragmentState = f.mSavedFragmentState;
2688                }
2689
2690                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
2691                        + fs.mSavedFragmentState);
2692            }
2693        }
2694
2695        if (!haveFragments) {
2696            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
2697            return null;
2698        }
2699
2700        int[] added = null;
2701        BackStackState[] backStack = null;
2702
2703        // Build list of currently added fragments.
2704        if (mAdded != null) {
2705            N = mAdded.size();
2706            if (N > 0) {
2707                added = new int[N];
2708                for (int i=0; i<N; i++) {
2709                    added[i] = mAdded.get(i).mIndex;
2710                    if (added[i] < 0) {
2711                        throwException(new IllegalStateException(
2712                                "Failure saving state: active " + mAdded.get(i)
2713                                + " has cleared index: " + added[i]));
2714                    }
2715                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
2716                            + ": " + mAdded.get(i));
2717                }
2718            }
2719        }
2720
2721        // Now save back stack.
2722        if (mBackStack != null) {
2723            N = mBackStack.size();
2724            if (N > 0) {
2725                backStack = new BackStackState[N];
2726                for (int i=0; i<N; i++) {
2727                    backStack[i] = new BackStackState(mBackStack.get(i));
2728                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
2729                            + ": " + mBackStack.get(i));
2730                }
2731            }
2732        }
2733
2734        FragmentManagerState fms = new FragmentManagerState();
2735        fms.mActive = active;
2736        fms.mAdded = added;
2737        fms.mBackStack = backStack;
2738        return fms;
2739    }
2740
2741    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
2742        // If there is no saved state at all, then there can not be
2743        // any nonConfig fragments either, so that is that.
2744        if (state == null) return;
2745        FragmentManagerState fms = (FragmentManagerState)state;
2746        if (fms.mActive == null) return;
2747
2748        List<FragmentManagerNonConfig> childNonConfigs = null;
2749
2750        // First re-attach any non-config instances we are retaining back
2751        // to their saved state, so we don't try to instantiate them again.
2752        if (nonConfig != null) {
2753            List<Fragment> nonConfigFragments = nonConfig.getFragments();
2754            childNonConfigs = nonConfig.getChildNonConfigs();
2755            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
2756            for (int i = 0; i < count; i++) {
2757                Fragment f = nonConfigFragments.get(i);
2758                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
2759                FragmentState fs = fms.mActive[f.mIndex];
2760                fs.mInstance = f;
2761                f.mSavedViewState = null;
2762                f.mBackStackNesting = 0;
2763                f.mInLayout = false;
2764                f.mAdded = false;
2765                f.mTarget = null;
2766                if (fs.mSavedFragmentState != null) {
2767                    fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
2768                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
2769                            FragmentManagerImpl.VIEW_STATE_TAG);
2770                    f.mSavedFragmentState = fs.mSavedFragmentState;
2771                }
2772            }
2773        }
2774
2775        // Build the full list of active fragments, instantiating them from
2776        // their saved state.
2777        mActive = new ArrayList<>(fms.mActive.length);
2778        if (mAvailIndices != null) {
2779            mAvailIndices.clear();
2780        }
2781        for (int i=0; i<fms.mActive.length; i++) {
2782            FragmentState fs = fms.mActive[i];
2783            if (fs != null) {
2784                FragmentManagerNonConfig childNonConfig = null;
2785                if (childNonConfigs != null && i < childNonConfigs.size()) {
2786                    childNonConfig = childNonConfigs.get(i);
2787                }
2788                Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
2789                if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
2790                mActive.add(f);
2791                // Now that the fragment is instantiated (or came from being
2792                // retained above), clear mInstance in case we end up re-restoring
2793                // from this FragmentState again.
2794                fs.mInstance = null;
2795            } else {
2796                mActive.add(null);
2797                if (mAvailIndices == null) {
2798                    mAvailIndices = new ArrayList<Integer>();
2799                }
2800                if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
2801                mAvailIndices.add(i);
2802            }
2803        }
2804
2805        // Update the target of all retained fragments.
2806        if (nonConfig != null) {
2807            List<Fragment> nonConfigFragments = nonConfig.getFragments();
2808            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
2809            for (int i = 0; i < count; i++) {
2810                Fragment f = nonConfigFragments.get(i);
2811                if (f.mTargetIndex >= 0) {
2812                    if (f.mTargetIndex < mActive.size()) {
2813                        f.mTarget = mActive.get(f.mTargetIndex);
2814                    } else {
2815                        Log.w(TAG, "Re-attaching retained fragment " + f
2816                                + " target no longer exists: " + f.mTargetIndex);
2817                        f.mTarget = null;
2818                    }
2819                }
2820            }
2821        }
2822
2823        // Build the list of currently added fragments.
2824        if (fms.mAdded != null) {
2825            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
2826            for (int i=0; i<fms.mAdded.length; i++) {
2827                Fragment f = mActive.get(fms.mAdded[i]);
2828                if (f == null) {
2829                    throwException(new IllegalStateException(
2830                            "No instantiated fragment for index #" + fms.mAdded[i]));
2831                }
2832                f.mAdded = true;
2833                if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
2834                if (mAdded.contains(f)) {
2835                    throw new IllegalStateException("Already added!");
2836                }
2837                mAdded.add(f);
2838            }
2839        } else {
2840            mAdded = null;
2841        }
2842
2843        // Build the back stack.
2844        if (fms.mBackStack != null) {
2845            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
2846            for (int i=0; i<fms.mBackStack.length; i++) {
2847                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
2848                if (DEBUG) {
2849                    Log.v(TAG, "restoreAllState: back stack #" + i
2850                        + " (index " + bse.mIndex + "): " + bse);
2851                    LogWriter logw = new LogWriter(TAG);
2852                    PrintWriter pw = new PrintWriter(logw);
2853                    bse.dump("  ", pw, false);
2854                }
2855                mBackStack.add(bse);
2856                if (bse.mIndex >= 0) {
2857                    setBackStackIndex(bse.mIndex, bse);
2858                }
2859            }
2860        } else {
2861            mBackStack = null;
2862        }
2863    }
2864
2865    public void attachController(FragmentHostCallback host,
2866            FragmentContainer container, Fragment parent) {
2867        if (mHost != null) throw new IllegalStateException("Already attached");
2868        mHost = host;
2869        mContainer = container;
2870        mParent = parent;
2871    }
2872
2873    public void noteStateNotSaved() {
2874        mStateSaved = false;
2875    }
2876
2877    public void dispatchCreate() {
2878        mStateSaved = false;
2879        moveToState(Fragment.CREATED, false);
2880    }
2881
2882    public void dispatchActivityCreated() {
2883        mStateSaved = false;
2884        moveToState(Fragment.ACTIVITY_CREATED, false);
2885    }
2886
2887    public void dispatchStart() {
2888        mStateSaved = false;
2889        moveToState(Fragment.STARTED, false);
2890    }
2891
2892    public void dispatchResume() {
2893        mStateSaved = false;
2894        moveToState(Fragment.RESUMED, false);
2895    }
2896
2897    public void dispatchPause() {
2898        moveToState(Fragment.STARTED, false);
2899    }
2900
2901    public void dispatchStop() {
2902        // See saveAllState() for the explanation of this.  We do this for
2903        // all platform versions, to keep our behavior more consistent between
2904        // them.
2905        mStateSaved = true;
2906
2907        moveToState(Fragment.STOPPED, false);
2908    }
2909
2910    public void dispatchReallyStop() {
2911        moveToState(Fragment.ACTIVITY_CREATED, false);
2912    }
2913
2914    public void dispatchDestroyView() {
2915        moveToState(Fragment.CREATED, false);
2916    }
2917
2918    public void dispatchDestroy() {
2919        mDestroyed = true;
2920        execPendingActions();
2921        moveToState(Fragment.INITIALIZING, false);
2922        mHost = null;
2923        mContainer = null;
2924        mParent = null;
2925    }
2926
2927    public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
2928        if (mAdded == null) {
2929            return;
2930        }
2931        for (int i = mAdded.size() - 1; i >= 0; --i) {
2932            final android.support.v4.app.Fragment f = mAdded.get(i);
2933            if (f != null) {
2934                f.performMultiWindowModeChanged(isInMultiWindowMode);
2935            }
2936        }
2937    }
2938
2939    public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
2940        if (mAdded == null) {
2941            return;
2942        }
2943        for (int i = mAdded.size() - 1; i >= 0; --i) {
2944            final android.support.v4.app.Fragment f = mAdded.get(i);
2945            if (f != null) {
2946                f.performPictureInPictureModeChanged(isInPictureInPictureMode);
2947            }
2948        }
2949    }
2950
2951    public void dispatchConfigurationChanged(Configuration newConfig) {
2952        if (mAdded != null) {
2953            for (int i=0; i<mAdded.size(); i++) {
2954                Fragment f = mAdded.get(i);
2955                if (f != null) {
2956                    f.performConfigurationChanged(newConfig);
2957                }
2958            }
2959        }
2960    }
2961
2962    public void dispatchLowMemory() {
2963        if (mAdded != null) {
2964            for (int i=0; i<mAdded.size(); i++) {
2965                Fragment f = mAdded.get(i);
2966                if (f != null) {
2967                    f.performLowMemory();
2968                }
2969            }
2970        }
2971    }
2972
2973    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
2974        boolean show = false;
2975        ArrayList<Fragment> newMenus = null;
2976        if (mAdded != null) {
2977            for (int i=0; i<mAdded.size(); i++) {
2978                Fragment f = mAdded.get(i);
2979                if (f != null) {
2980                    if (f.performCreateOptionsMenu(menu, inflater)) {
2981                        show = true;
2982                        if (newMenus == null) {
2983                            newMenus = new ArrayList<Fragment>();
2984                        }
2985                        newMenus.add(f);
2986                    }
2987                }
2988            }
2989        }
2990
2991        if (mCreatedMenus != null) {
2992            for (int i=0; i<mCreatedMenus.size(); i++) {
2993                Fragment f = mCreatedMenus.get(i);
2994                if (newMenus == null || !newMenus.contains(f)) {
2995                    f.onDestroyOptionsMenu();
2996                }
2997            }
2998        }
2999
3000        mCreatedMenus = newMenus;
3001
3002        return show;
3003    }
3004
3005    public boolean dispatchPrepareOptionsMenu(Menu menu) {
3006        boolean show = false;
3007        if (mAdded != null) {
3008            for (int i=0; i<mAdded.size(); i++) {
3009                Fragment f = mAdded.get(i);
3010                if (f != null) {
3011                    if (f.performPrepareOptionsMenu(menu)) {
3012                        show = true;
3013                    }
3014                }
3015            }
3016        }
3017        return show;
3018    }
3019
3020    public boolean dispatchOptionsItemSelected(MenuItem item) {
3021        if (mAdded != null) {
3022            for (int i=0; i<mAdded.size(); i++) {
3023                Fragment f = mAdded.get(i);
3024                if (f != null) {
3025                    if (f.performOptionsItemSelected(item)) {
3026                        return true;
3027                    }
3028                }
3029            }
3030        }
3031        return false;
3032    }
3033
3034    public boolean dispatchContextItemSelected(MenuItem item) {
3035        if (mAdded != null) {
3036            for (int i=0; i<mAdded.size(); i++) {
3037                Fragment f = mAdded.get(i);
3038                if (f != null) {
3039                    if (f.performContextItemSelected(item)) {
3040                        return true;
3041                    }
3042                }
3043            }
3044        }
3045        return false;
3046    }
3047
3048    public void dispatchOptionsMenuClosed(Menu menu) {
3049        if (mAdded != null) {
3050            for (int i=0; i<mAdded.size(); i++) {
3051                Fragment f = mAdded.get(i);
3052                if (f != null) {
3053                    f.performOptionsMenuClosed(menu);
3054                }
3055            }
3056        }
3057    }
3058
3059    public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
3060            boolean recursive) {
3061        if (mLifecycleCallbacks == null) {
3062            mLifecycleCallbacks = new CopyOnWriteArrayList<>();
3063        }
3064        mLifecycleCallbacks.add(new Pair(cb, recursive));
3065    }
3066
3067    public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) {
3068        if (mLifecycleCallbacks == null) {
3069            return;
3070        }
3071
3072        synchronized (mLifecycleCallbacks) {
3073            for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) {
3074                if (mLifecycleCallbacks.get(i).first == cb) {
3075                    mLifecycleCallbacks.remove(i);
3076                    break;
3077                }
3078            }
3079        }
3080    }
3081
3082    void dispatchOnFragmentPreAttached(Fragment f, Context context, boolean onlyRecursive) {
3083        if (mParent != null) {
3084            FragmentManager parentManager = mParent.getFragmentManager();
3085            if (parentManager instanceof FragmentManagerImpl) {
3086                ((FragmentManagerImpl) parentManager)
3087                        .dispatchOnFragmentPreAttached(f, context, true);
3088            }
3089        }
3090        if (mLifecycleCallbacks == null) {
3091            return;
3092        }
3093        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3094            if (!onlyRecursive || p.second) {
3095                p.first.onFragmentPreAttached(this, f, context);
3096            }
3097        }
3098    }
3099
3100    void dispatchOnFragmentAttached(Fragment f, Context context, boolean onlyRecursive) {
3101        if (mParent != null) {
3102            FragmentManager parentManager = mParent.getFragmentManager();
3103            if (parentManager instanceof FragmentManagerImpl) {
3104                ((FragmentManagerImpl) parentManager)
3105                        .dispatchOnFragmentAttached(f, context, true);
3106            }
3107        }
3108        if (mLifecycleCallbacks == null) {
3109            return;
3110        }
3111        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3112            if (!onlyRecursive || p.second) {
3113                p.first.onFragmentAttached(this, f, context);
3114            }
3115        }
3116    }
3117
3118    void dispatchOnFragmentCreated(Fragment f, Bundle savedInstanceState, boolean onlyRecursive) {
3119        if (mParent != null) {
3120            FragmentManager parentManager = mParent.getFragmentManager();
3121            if (parentManager instanceof FragmentManagerImpl) {
3122                ((FragmentManagerImpl) parentManager)
3123                        .dispatchOnFragmentCreated(f, savedInstanceState, true);
3124            }
3125        }
3126        if (mLifecycleCallbacks == null) {
3127            return;
3128        }
3129        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3130            if (!onlyRecursive || p.second) {
3131                p.first.onFragmentCreated(this, f, savedInstanceState);
3132            }
3133        }
3134    }
3135
3136    void dispatchOnFragmentActivityCreated(Fragment f, Bundle savedInstanceState,
3137            boolean onlyRecursive) {
3138        if (mParent != null) {
3139            FragmentManager parentManager = mParent.getFragmentManager();
3140            if (parentManager instanceof FragmentManagerImpl) {
3141                ((FragmentManagerImpl) parentManager)
3142                        .dispatchOnFragmentActivityCreated(f, savedInstanceState, true);
3143            }
3144        }
3145        if (mLifecycleCallbacks == null) {
3146            return;
3147        }
3148        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3149            if (!onlyRecursive || p.second) {
3150                p.first.onFragmentActivityCreated(this, f, savedInstanceState);
3151            }
3152        }
3153    }
3154
3155    void dispatchOnFragmentViewCreated(Fragment f, View v, Bundle savedInstanceState,
3156            boolean onlyRecursive) {
3157        if (mParent != null) {
3158            FragmentManager parentManager = mParent.getFragmentManager();
3159            if (parentManager instanceof FragmentManagerImpl) {
3160                ((FragmentManagerImpl) parentManager)
3161                        .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true);
3162            }
3163        }
3164        if (mLifecycleCallbacks == null) {
3165            return;
3166        }
3167        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3168            if (!onlyRecursive || p.second) {
3169                p.first.onFragmentViewCreated(this, f, v, savedInstanceState);
3170            }
3171        }
3172    }
3173
3174    void dispatchOnFragmentStarted(Fragment f, boolean onlyRecursive) {
3175        if (mParent != null) {
3176            FragmentManager parentManager = mParent.getFragmentManager();
3177            if (parentManager instanceof FragmentManagerImpl) {
3178                ((FragmentManagerImpl) parentManager)
3179                        .dispatchOnFragmentStarted(f, true);
3180            }
3181        }
3182        if (mLifecycleCallbacks == null) {
3183            return;
3184        }
3185        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3186            if (!onlyRecursive || p.second) {
3187                p.first.onFragmentStarted(this, f);
3188            }
3189        }
3190    }
3191
3192    void dispatchOnFragmentResumed(Fragment f, boolean onlyRecursive) {
3193        if (mParent != null) {
3194            FragmentManager parentManager = mParent.getFragmentManager();
3195            if (parentManager instanceof FragmentManagerImpl) {
3196                ((FragmentManagerImpl) parentManager)
3197                        .dispatchOnFragmentResumed(f, true);
3198            }
3199        }
3200        if (mLifecycleCallbacks == null) {
3201            return;
3202        }
3203        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3204            if (!onlyRecursive || p.second) {
3205                p.first.onFragmentResumed(this, f);
3206            }
3207        }
3208    }
3209
3210    void dispatchOnFragmentPaused(Fragment f, boolean onlyRecursive) {
3211        if (mParent != null) {
3212            FragmentManager parentManager = mParent.getFragmentManager();
3213            if (parentManager instanceof FragmentManagerImpl) {
3214                ((FragmentManagerImpl) parentManager)
3215                        .dispatchOnFragmentPaused(f, true);
3216            }
3217        }
3218        if (mLifecycleCallbacks == null) {
3219            return;
3220        }
3221        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3222            if (!onlyRecursive || p.second) {
3223                p.first.onFragmentPaused(this, f);
3224            }
3225        }
3226    }
3227
3228    void dispatchOnFragmentStopped(Fragment f, boolean onlyRecursive) {
3229        if (mParent != null) {
3230            FragmentManager parentManager = mParent.getFragmentManager();
3231            if (parentManager instanceof FragmentManagerImpl) {
3232                ((FragmentManagerImpl) parentManager)
3233                        .dispatchOnFragmentStopped(f, true);
3234            }
3235        }
3236        if (mLifecycleCallbacks == null) {
3237            return;
3238        }
3239        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3240            if (!onlyRecursive || p.second) {
3241                p.first.onFragmentStopped(this, f);
3242            }
3243        }
3244    }
3245
3246    void dispatchOnFragmentSaveInstanceState(Fragment f, Bundle outState, boolean onlyRecursive) {
3247        if (mParent != null) {
3248            FragmentManager parentManager = mParent.getFragmentManager();
3249            if (parentManager instanceof FragmentManagerImpl) {
3250                ((FragmentManagerImpl) parentManager)
3251                        .dispatchOnFragmentSaveInstanceState(f, outState, true);
3252            }
3253        }
3254        if (mLifecycleCallbacks == null) {
3255            return;
3256        }
3257        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3258            if (!onlyRecursive || p.second) {
3259                p.first.onFragmentSaveInstanceState(this, f, outState);
3260            }
3261        }
3262    }
3263
3264    void dispatchOnFragmentViewDestroyed(Fragment f, boolean onlyRecursive) {
3265        if (mParent != null) {
3266            FragmentManager parentManager = mParent.getFragmentManager();
3267            if (parentManager instanceof FragmentManagerImpl) {
3268                ((FragmentManagerImpl) parentManager)
3269                        .dispatchOnFragmentViewDestroyed(f, true);
3270            }
3271        }
3272        if (mLifecycleCallbacks == null) {
3273            return;
3274        }
3275        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3276            if (!onlyRecursive || p.second) {
3277                p.first.onFragmentViewDestroyed(this, f);
3278            }
3279        }
3280    }
3281
3282    void dispatchOnFragmentDestroyed(Fragment f, boolean onlyRecursive) {
3283        if (mParent != null) {
3284            FragmentManager parentManager = mParent.getFragmentManager();
3285            if (parentManager instanceof FragmentManagerImpl) {
3286                ((FragmentManagerImpl) parentManager)
3287                        .dispatchOnFragmentDestroyed(f, true);
3288            }
3289        }
3290        if (mLifecycleCallbacks == null) {
3291            return;
3292        }
3293        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3294            if (!onlyRecursive || p.second) {
3295                p.first.onFragmentDestroyed(this, f);
3296            }
3297        }
3298    }
3299
3300    void dispatchOnFragmentDetached(Fragment f, boolean onlyRecursive) {
3301        if (mParent != null) {
3302            FragmentManager parentManager = mParent.getFragmentManager();
3303            if (parentManager instanceof FragmentManagerImpl) {
3304                ((FragmentManagerImpl) parentManager)
3305                        .dispatchOnFragmentDetached(f, true);
3306            }
3307        }
3308        if (mLifecycleCallbacks == null) {
3309            return;
3310        }
3311        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
3312            if (!onlyRecursive || p.second) {
3313                p.first.onFragmentDetached(this, f);
3314            }
3315        }
3316    }
3317
3318    public static int reverseTransit(int transit) {
3319        int rev = 0;
3320        switch (transit) {
3321            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
3322                rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
3323                break;
3324            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
3325                rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
3326                break;
3327            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
3328                rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
3329                break;
3330        }
3331        return rev;
3332
3333    }
3334
3335    public static final int ANIM_STYLE_OPEN_ENTER = 1;
3336    public static final int ANIM_STYLE_OPEN_EXIT = 2;
3337    public static final int ANIM_STYLE_CLOSE_ENTER = 3;
3338    public static final int ANIM_STYLE_CLOSE_EXIT = 4;
3339    public static final int ANIM_STYLE_FADE_ENTER = 5;
3340    public static final int ANIM_STYLE_FADE_EXIT = 6;
3341
3342    public static int transitToStyleIndex(int transit, boolean enter) {
3343        int animAttr = -1;
3344        switch (transit) {
3345            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
3346                animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT;
3347                break;
3348            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
3349                animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT;
3350                break;
3351            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
3352                animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT;
3353                break;
3354        }
3355        return animAttr;
3356    }
3357
3358    @Override
3359    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
3360        if (!"fragment".equals(name)) {
3361            return null;
3362        }
3363
3364        String fname = attrs.getAttributeValue(null, "class");
3365        TypedArray a =  context.obtainStyledAttributes(attrs, FragmentTag.Fragment);
3366        if (fname == null) {
3367            fname = a.getString(FragmentTag.Fragment_name);
3368        }
3369        int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID);
3370        String tag = a.getString(FragmentTag.Fragment_tag);
3371        a.recycle();
3372
3373        if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) {
3374            // Invalid support lib fragment; let the device's framework handle it.
3375            // This will allow android.app.Fragments to do the right thing.
3376            return null;
3377        }
3378
3379        int containerId = parent != null ? parent.getId() : 0;
3380        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
3381            throw new IllegalArgumentException(attrs.getPositionDescription()
3382                    + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
3383        }
3384
3385        // If we restored from a previous state, we may already have
3386        // instantiated this fragment from the state and should use
3387        // that instance instead of making a new one.
3388        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
3389        if (fragment == null && tag != null) {
3390            fragment = findFragmentByTag(tag);
3391        }
3392        if (fragment == null && containerId != View.NO_ID) {
3393            fragment = findFragmentById(containerId);
3394        }
3395
3396        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
3397                + Integer.toHexString(id) + " fname=" + fname
3398                + " existing=" + fragment);
3399        if (fragment == null) {
3400            fragment = Fragment.instantiate(context, fname);
3401            fragment.mFromLayout = true;
3402            fragment.mFragmentId = id != 0 ? id : containerId;
3403            fragment.mContainerId = containerId;
3404            fragment.mTag = tag;
3405            fragment.mInLayout = true;
3406            fragment.mFragmentManager = this;
3407            fragment.mHost = mHost;
3408            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
3409            addFragment(fragment, true);
3410
3411        } else if (fragment.mInLayout) {
3412            // A fragment already exists and it is not one we restored from
3413            // previous state.
3414            throw new IllegalArgumentException(attrs.getPositionDescription()
3415                    + ": Duplicate id 0x" + Integer.toHexString(id)
3416                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
3417                    + " with another fragment for " + fname);
3418        } else {
3419            // This fragment was retained from a previous instance; get it
3420            // going now.
3421            fragment.mInLayout = true;
3422            fragment.mHost = mHost;
3423            // If this fragment is newly instantiated (either right now, or
3424            // from last saved state), then give it the attributes to
3425            // initialize itself.
3426            if (!fragment.mRetaining) {
3427                fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
3428            }
3429        }
3430
3431        // If we haven't finished entering the CREATED state ourselves yet,
3432        // push the inflated child fragment along.
3433        if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
3434            moveToState(fragment, Fragment.CREATED, 0, 0, false);
3435        } else {
3436            moveToState(fragment);
3437        }
3438
3439        if (fragment.mView == null) {
3440            throw new IllegalStateException("Fragment " + fname
3441                    + " did not create a view.");
3442        }
3443        if (id != 0) {
3444            fragment.mView.setId(id);
3445        }
3446        if (fragment.mView.getTag() == null) {
3447            fragment.mView.setTag(tag);
3448        }
3449        return fragment.mView;
3450    }
3451
3452    LayoutInflaterFactory getLayoutInflaterFactory() {
3453        return this;
3454    }
3455
3456    static class FragmentTag {
3457        public static final int[] Fragment = {
3458                0x01010003, 0x010100d0, 0x010100d1
3459        };
3460        public static final int Fragment_id = 1;
3461        public static final int Fragment_name = 0;
3462        public static final int Fragment_tag = 2;
3463    }
3464
3465    /**
3466     * An add or pop transaction to be scheduled for the UI thread.
3467     */
3468    interface OpGenerator {
3469        /**
3470         * Generate transactions to add to {@code records} and whether or not the transaction is
3471         * an add or pop to {@code isRecordPop}.
3472         *
3473         * records and isRecordPop must be added equally so that each transaction in records
3474         * matches the boolean for whether or not it is a pop in isRecordPop.
3475         *
3476         * @param records A list to add transactions to.
3477         * @param isRecordPop A list to add whether or not the transactions added to records is
3478         *                    a pop transaction.
3479         * @return true if something was added or false otherwise.
3480         */
3481        boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
3482    }
3483
3484    /**
3485     * A pop operation OpGenerator. This will be run on the UI thread and will generate the
3486     * transactions that will be popped if anything can be popped.
3487     */
3488    private class PopBackStackState implements OpGenerator {
3489        final String mName;
3490        final int mId;
3491        final int mFlags;
3492
3493        PopBackStackState(String name, int id, int flags) {
3494            mName = name;
3495            mId = id;
3496            mFlags = flags;
3497        }
3498
3499        @Override
3500        public boolean generateOps(ArrayList<BackStackRecord> records,
3501                ArrayList<Boolean> isRecordPop) {
3502            return popBackStackState(records, isRecordPop, mName, mId, mFlags);
3503        }
3504    }
3505
3506    /**
3507     * A listener for a postponed transaction. This waits until
3508     * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started
3509     * that interacts with this one, based on interactions with the fragment container.
3510     */
3511    static class StartEnterTransitionListener
3512            implements Fragment.OnStartEnterTransitionListener {
3513        private final boolean mIsBack;
3514        private final BackStackRecord mRecord;
3515        private int mNumPostponed;
3516
3517        StartEnterTransitionListener(BackStackRecord record, boolean isBack) {
3518            mIsBack = isBack;
3519            mRecord = record;
3520        }
3521
3522        /**
3523         * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the
3524         * number of Fragments that are postponed. This may cause the transaction to schedule
3525         * to finish running and run transitions and animations.
3526         */
3527        @Override
3528        public void onStartEnterTransition() {
3529            mNumPostponed--;
3530            if (mNumPostponed != 0) {
3531                return;
3532            }
3533            mRecord.mManager.scheduleCommit();
3534        }
3535
3536        /**
3537         * Called from {@link Fragment#
3538         * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this
3539         * increases the number of fragments that are postponed as part of this transaction.
3540         */
3541        @Override
3542        public void startListening() {
3543            mNumPostponed++;
3544        }
3545
3546        /**
3547         * @return true if there are no more postponed fragments as part of the transaction.
3548         */
3549        public boolean isReady() {
3550            return mNumPostponed == 0;
3551        }
3552
3553        /**
3554         * Completes the transaction and start the animations and transitions. This may skip
3555         * the transitions if this is called before all fragments have called
3556         * {@link Fragment#startPostponedEnterTransition()}.
3557         */
3558        public void completeTransaction() {
3559            final boolean canceled;
3560            canceled = mNumPostponed > 0;
3561            FragmentManagerImpl manager = mRecord.mManager;
3562            final int numAdded = manager.mAdded.size();
3563            for (int i = 0; i < numAdded; i++) {
3564                final Fragment fragment = manager.mAdded.get(i);
3565                fragment.setOnStartEnterTransitionListener(null);
3566                if (canceled && fragment.isPostponed()) {
3567                    fragment.startPostponedEnterTransition();
3568                }
3569            }
3570            mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
3571        }
3572
3573        /**
3574         * Cancels this transaction instead of completing it. That means that the state isn't
3575         * changed, so the pop results in no change to the state.
3576         */
3577        public void cancelTransaction() {
3578            mRecord.mManager.completeExecute(mRecord, mIsBack, false, false);
3579        }
3580    }
3581}
3582