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