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