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