FragmentManager.java revision 805fb8e2508edfc45a1b80a3bf63501aa3507bf2
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.STOPPED) {
1336            newState = Fragment.STOPPED;
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                            f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
1370                                    FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
1371                            if (!f.mUserVisibleHint) {
1372                                f.mDeferStart = true;
1373                                if (newState > Fragment.STOPPED) {
1374                                    newState = Fragment.STOPPED;
1375                                }
1376                            }
1377                        }
1378
1379                        f.mHost = mHost;
1380                        f.mParentFragment = mParent;
1381                        f.mFragmentManager = mParent != null
1382                                ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
1383
1384                        // If we have a target fragment, push it along to at least CREATED
1385                        // so that this one can rely on it as an initialized dependency.
1386                        if (f.mTarget != null) {
1387                            if (mActive.get(f.mTarget.mIndex) != f.mTarget) {
1388                                throw new IllegalStateException("Fragment " + f
1389                                        + " declared target fragment " + f.mTarget
1390                                        + " that does not belong to this FragmentManager!");
1391                            }
1392                            if (f.mTarget.mState < Fragment.CREATED) {
1393                                moveToState(f.mTarget, Fragment.CREATED, 0, 0, true);
1394                            }
1395                        }
1396
1397                        dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
1398                        f.mCalled = false;
1399                        f.onAttach(mHost.getContext());
1400                        if (!f.mCalled) {
1401                            throw new SuperNotCalledException("Fragment " + f
1402                                    + " did not call through to super.onAttach()");
1403                        }
1404                        if (f.mParentFragment == null) {
1405                            mHost.onAttachFragment(f);
1406                        } else {
1407                            f.mParentFragment.onAttachFragment(f);
1408                        }
1409                        dispatchOnFragmentAttached(f, mHost.getContext(), false);
1410
1411                        if (!f.mIsCreated) {
1412                            dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
1413                            f.performCreate(f.mSavedFragmentState);
1414                            dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
1415                        } else {
1416                            f.restoreChildFragmentState(f.mSavedFragmentState);
1417                            f.mState = Fragment.CREATED;
1418                        }
1419                        f.mRetaining = false;
1420                    }
1421                    // fall through
1422                case Fragment.CREATED:
1423                    // This is outside the if statement below on purpose; we want this to run
1424                    // even if we do a moveToState from CREATED => *, CREATED => CREATED, and
1425                    // * => CREATED as part of the case fallthrough above.
1426                    ensureInflatedFragmentView(f);
1427
1428                    if (newState > Fragment.CREATED) {
1429                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
1430                        if (!f.mFromLayout) {
1431                            ViewGroup container = null;
1432                            if (f.mContainerId != 0) {
1433                                if (f.mContainerId == View.NO_ID) {
1434                                    throwException(new IllegalArgumentException(
1435                                            "Cannot create fragment "
1436                                                    + f
1437                                                    + " for a container view with no id"));
1438                                }
1439                                container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
1440                                if (container == null && !f.mRestored) {
1441                                    String resName;
1442                                    try {
1443                                        resName = f.getResources().getResourceName(f.mContainerId);
1444                                    } catch (NotFoundException e) {
1445                                        resName = "unknown";
1446                                    }
1447                                    throwException(new IllegalArgumentException(
1448                                            "No view found for id 0x"
1449                                            + Integer.toHexString(f.mContainerId) + " ("
1450                                            + resName
1451                                            + ") for fragment " + f));
1452                                }
1453                            }
1454                            f.mContainer = container;
1455                            f.mView = f.performCreateView(f.performGetLayoutInflater(
1456                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
1457                            if (f.mView != null) {
1458                                f.mInnerView = f.mView;
1459                                f.mView.setSaveFromParentEnabled(false);
1460                                if (container != null) {
1461                                    container.addView(f.mView);
1462                                }
1463                                if (f.mHidden) {
1464                                    f.mView.setVisibility(View.GONE);
1465                                }
1466                                f.onViewCreated(f.mView, f.mSavedFragmentState);
1467                                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
1468                                        false);
1469                                // Only animate the view if it is visible. This is done after
1470                                // dispatchOnFragmentViewCreated in case visibility is changed
1471                                f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
1472                                        && f.mContainer != null;
1473                            } else {
1474                                f.mInnerView = null;
1475                            }
1476                        }
1477
1478                        f.performActivityCreated(f.mSavedFragmentState);
1479                        dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
1480                        if (f.mView != null) {
1481                            f.restoreViewState(f.mSavedFragmentState);
1482                        }
1483                        f.mSavedFragmentState = null;
1484                    }
1485                    // fall through
1486                case Fragment.ACTIVITY_CREATED:
1487                    if (newState > Fragment.ACTIVITY_CREATED) {
1488                        f.mState = Fragment.STOPPED;
1489                    }
1490                    // fall through
1491                case Fragment.STOPPED:
1492                    if (newState > Fragment.STOPPED) {
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.STOPPED:
1524                    if (newState < Fragment.STOPPED) {
1525                        if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
1526                        f.performReallyStop();
1527                    }
1528                    // fall through
1529                case Fragment.ACTIVITY_CREATED:
1530                    if (newState < Fragment.ACTIVITY_CREATED) {
1531                        if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
1532                        if (f.mView != null) {
1533                            // Need to save the current view state if not
1534                            // done already.
1535                            if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
1536                                saveFragmentViewState(f);
1537                            }
1538                        }
1539                        f.performDestroyView();
1540                        dispatchOnFragmentViewDestroyed(f, false);
1541                        if (f.mView != null && f.mContainer != null) {
1542                            // Stop any current animations:
1543                            f.mContainer.endViewTransition(f.mView);
1544                            f.mView.clearAnimation();
1545                            AnimationOrAnimator anim = null;
1546                            if (mCurState > Fragment.INITIALIZING && !mDestroyed
1547                                    && f.mView.getVisibility() == View.VISIBLE
1548                                    && f.mPostponedAlpha >= 0) {
1549                                anim = loadAnimation(f, transit, false,
1550                                        transitionStyle);
1551                            }
1552                            f.mPostponedAlpha = 0;
1553                            if (anim != null) {
1554                                animateRemoveFragment(f, anim, newState);
1555                            }
1556                            f.mContainer.removeView(f.mView);
1557                        }
1558                        f.mContainer = null;
1559                        f.mView = null;
1560                        f.mInnerView = null;
1561                        f.mInLayout = false;
1562                    }
1563                    // fall through
1564                case Fragment.CREATED:
1565                    if (newState < Fragment.CREATED) {
1566                        if (mDestroyed) {
1567                            // The fragment's containing activity is
1568                            // being destroyed, but this fragment is
1569                            // currently animating away.  Stop the
1570                            // animation right now -- it is not needed,
1571                            // and we can't wait any more on destroying
1572                            // the fragment.
1573                            if (f.getAnimatingAway() != null) {
1574                                View v = f.getAnimatingAway();
1575                                f.setAnimatingAway(null);
1576                                v.clearAnimation();
1577                            } else if (f.getAnimator() != null) {
1578                                Animator animator = f.getAnimator();
1579                                f.setAnimator(null);
1580                                animator.cancel();
1581                            }
1582                        }
1583                        if (f.getAnimatingAway() != null || f.getAnimator() != null) {
1584                            // We are waiting for the fragment's view to finish
1585                            // animating away.  Just make a note of the state
1586                            // the fragment now should move to once the animation
1587                            // is done.
1588                            f.setStateAfterAnimating(newState);
1589                            newState = Fragment.CREATED;
1590                        } else {
1591                            if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
1592                            if (!f.mRetaining) {
1593                                f.performDestroy();
1594                                dispatchOnFragmentDestroyed(f, false);
1595                            } else {
1596                                f.mState = Fragment.INITIALIZING;
1597                            }
1598
1599                            f.performDetach();
1600                            dispatchOnFragmentDetached(f, false);
1601                            if (!keepActive) {
1602                                if (!f.mRetaining) {
1603                                    makeInactive(f);
1604                                } else {
1605                                    f.mHost = null;
1606                                    f.mParentFragment = null;
1607                                    f.mFragmentManager = null;
1608                                }
1609                            }
1610                        }
1611                    }
1612            }
1613        }
1614
1615        if (f.mState != newState) {
1616            Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
1617                    + "expected state " + newState + " found " + f.mState);
1618            f.mState = newState;
1619        }
1620    }
1621
1622    /**
1623     * Animates the removal of a fragment with the given animator or animation. After animating,
1624     * the fragment's view will be removed from the hierarchy.
1625     *
1626     * @param fragment The fragment to animate out
1627     * @param anim The animator or animation to run on the fragment's view
1628     * @param newState The final state after animating.
1629     */
1630    private void animateRemoveFragment(@NonNull final Fragment fragment,
1631            @NonNull AnimationOrAnimator anim, final int newState) {
1632        final View viewToAnimate = fragment.mView;
1633        final ViewGroup container = fragment.mContainer;
1634        container.startViewTransition(viewToAnimate);
1635        fragment.setStateAfterAnimating(newState);
1636        if (anim.animation != null) {
1637            Animation animation =
1638                    new EndViewTransitionAnimator(anim.animation, container, viewToAnimate);
1639            fragment.setAnimatingAway(fragment.mView);
1640            AnimationListener listener = getAnimationListener(animation);
1641            animation.setAnimationListener(new AnimationListenerWrapper(listener) {
1642                @Override
1643                public void onAnimationEnd(Animation animation) {
1644                    super.onAnimationEnd(animation);
1645
1646                    // onAnimationEnd() comes during draw(), so there can still be some
1647                    // draw events happening after this call. We don't want to detach
1648                    // the view until after the onAnimationEnd()
1649                    container.post(new Runnable() {
1650                        @Override
1651                        public void run() {
1652                            if (fragment.getAnimatingAway() != null) {
1653                                fragment.setAnimatingAway(null);
1654                                moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0,
1655                                        false);
1656                            }
1657                        }
1658                    });
1659                }
1660            });
1661            setHWLayerAnimListenerIfAlpha(viewToAnimate, anim);
1662            fragment.mView.startAnimation(animation);
1663        } else {
1664            Animator animator = anim.animator;
1665            fragment.setAnimator(anim.animator);
1666            animator.addListener(new AnimatorListenerAdapter() {
1667                @Override
1668                public void onAnimationEnd(Animator anim) {
1669                    container.endViewTransition(viewToAnimate);
1670                    // If an animator ends immediately, we can just pretend there is no animation.
1671                    // When that happens the the fragment's view won't have been removed yet.
1672                    Animator animator = fragment.getAnimator();
1673                    fragment.setAnimator(null);
1674                    if (animator != null && container.indexOfChild(viewToAnimate) < 0) {
1675                        moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false);
1676                    }
1677                }
1678            });
1679            animator.setTarget(fragment.mView);
1680            setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1681            animator.start();
1682        }
1683    }
1684
1685    void moveToState(Fragment f) {
1686        moveToState(f, mCurState, 0, 0, false);
1687    }
1688
1689    void ensureInflatedFragmentView(Fragment f) {
1690        if (f.mFromLayout && !f.mPerformedCreateView) {
1691            f.mView = f.performCreateView(f.performGetLayoutInflater(
1692                    f.mSavedFragmentState), null, f.mSavedFragmentState);
1693            if (f.mView != null) {
1694                f.mInnerView = f.mView;
1695                f.mView.setSaveFromParentEnabled(false);
1696                if (f.mHidden) f.mView.setVisibility(View.GONE);
1697                f.onViewCreated(f.mView, f.mSavedFragmentState);
1698                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
1699            } else {
1700                f.mInnerView = null;
1701            }
1702        }
1703    }
1704
1705    /**
1706     * Fragments that have been shown or hidden don't have their visibility changed or
1707     * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
1708     * calls. After fragments are brought to their final state in
1709     * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or
1710     * hidden must have their visibility changed and their animations started here.
1711     *
1712     * @param fragment The fragment with mHiddenChanged = true that should change its View's
1713     *                 visibility and start the show or hide animation.
1714     */
1715    void completeShowHideFragment(final Fragment fragment) {
1716        if (fragment.mView != null) {
1717            AnimationOrAnimator anim = loadAnimation(fragment, fragment.getNextTransition(),
1718                    !fragment.mHidden, fragment.getNextTransitionStyle());
1719            if (anim != null && anim.animator != null) {
1720                anim.animator.setTarget(fragment.mView);
1721                if (fragment.mHidden) {
1722                    if (fragment.isHideReplaced()) {
1723                        fragment.setHideReplaced(false);
1724                    } else {
1725                        final ViewGroup container = fragment.mContainer;
1726                        final View animatingView = fragment.mView;
1727                        container.startViewTransition(animatingView);
1728                        // Delay the actual hide operation until the animation finishes,
1729                        // otherwise the fragment will just immediately disappear
1730                        anim.animator.addListener(new AnimatorListenerAdapter() {
1731                            @Override
1732                            public void onAnimationEnd(Animator animation) {
1733                                container.endViewTransition(animatingView);
1734                                animation.removeListener(this);
1735                                if (fragment.mView != null) {
1736                                    fragment.mView.setVisibility(View.GONE);
1737                                }
1738                            }
1739                        });
1740                    }
1741                } else {
1742                    fragment.mView.setVisibility(View.VISIBLE);
1743                }
1744                setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1745                anim.animator.start();
1746            } else {
1747                if (anim != null) {
1748                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1749                    fragment.mView.startAnimation(anim.animation);
1750                    anim.animation.start();
1751                }
1752                final int visibility = fragment.mHidden && !fragment.isHideReplaced()
1753                        ? View.GONE
1754                        : View.VISIBLE;
1755                fragment.mView.setVisibility(visibility);
1756                if (fragment.isHideReplaced()) {
1757                    fragment.setHideReplaced(false);
1758                }
1759            }
1760        }
1761        if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
1762            mNeedMenuInvalidate = true;
1763        }
1764        fragment.mHiddenChanged = false;
1765        fragment.onHiddenChanged(fragment.mHidden);
1766    }
1767
1768    /**
1769     * Moves a fragment to its expected final state or the fragment manager's state, depending
1770     * on whether the fragment manager's state is raised properly.
1771     *
1772     * @param f The fragment to change.
1773     */
1774    void moveFragmentToExpectedState(Fragment f) {
1775        if (f == null) {
1776            return;
1777        }
1778        int nextState = mCurState;
1779        if (f.mRemoving) {
1780            if (f.isInBackStack()) {
1781                nextState = Math.min(nextState, Fragment.CREATED);
1782            } else {
1783                nextState = Math.min(nextState, Fragment.INITIALIZING);
1784            }
1785        }
1786        moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
1787
1788        if (f.mView != null) {
1789            // Move the view if it is out of order
1790            Fragment underFragment = findFragmentUnder(f);
1791            if (underFragment != null) {
1792                final View underView = underFragment.mView;
1793                // make sure this fragment is in the right order.
1794                final ViewGroup container = f.mContainer;
1795                int underIndex = container.indexOfChild(underView);
1796                int viewIndex = container.indexOfChild(f.mView);
1797                if (viewIndex < underIndex) {
1798                    container.removeViewAt(viewIndex);
1799                    container.addView(f.mView, underIndex);
1800                }
1801            }
1802            if (f.mIsNewlyAdded && f.mContainer != null) {
1803                // Make it visible and run the animations
1804                if (f.mPostponedAlpha > 0f) {
1805                    f.mView.setAlpha(f.mPostponedAlpha);
1806                }
1807                f.mPostponedAlpha = 0f;
1808                f.mIsNewlyAdded = false;
1809                // run animations:
1810                AnimationOrAnimator anim = loadAnimation(f, f.getNextTransition(), true,
1811                        f.getNextTransitionStyle());
1812                if (anim != null) {
1813                    setHWLayerAnimListenerIfAlpha(f.mView, anim);
1814                    if (anim.animation != null) {
1815                        f.mView.startAnimation(anim.animation);
1816                    } else {
1817                        anim.animator.setTarget(f.mView);
1818                        anim.animator.start();
1819                    }
1820                }
1821            }
1822        }
1823        if (f.mHiddenChanged) {
1824            completeShowHideFragment(f);
1825        }
1826    }
1827
1828    /**
1829     * Changes the state of the fragment manager to {@code newState}. If the fragment manager
1830     * changes state or {@code always} is {@code true}, any fragments within it have their
1831     * states updated as well.
1832     *
1833     * @param newState The new state for the fragment manager
1834     * @param always If {@code true}, all fragments update their state, even
1835     *               if {@code newState} matches the current fragment manager's state.
1836     */
1837    void moveToState(int newState, boolean always) {
1838        if (mHost == null && newState != Fragment.INITIALIZING) {
1839            throw new IllegalStateException("No activity");
1840        }
1841
1842        if (!always && newState == mCurState) {
1843            return;
1844        }
1845
1846        mCurState = newState;
1847
1848        if (mActive != null) {
1849
1850            // Must add them in the proper order. mActive fragments may be out of order
1851            final int numAdded = mAdded.size();
1852            for (int i = 0; i < numAdded; i++) {
1853                Fragment f = mAdded.get(i);
1854                moveFragmentToExpectedState(f);
1855            }
1856
1857            // Now iterate through all active fragments. These will include those that are removed
1858            // and detached.
1859            final int numActive = mActive.size();
1860            for (int i = 0; i < numActive; i++) {
1861                Fragment f = mActive.valueAt(i);
1862                if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
1863                    moveFragmentToExpectedState(f);
1864                }
1865            }
1866
1867            startPendingDeferredFragments();
1868
1869            if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
1870                mHost.onSupportInvalidateOptionsMenu();
1871                mNeedMenuInvalidate = false;
1872            }
1873        }
1874    }
1875
1876    void startPendingDeferredFragments() {
1877        if (mActive == null) return;
1878
1879        for (int i=0; i<mActive.size(); i++) {
1880            Fragment f = mActive.valueAt(i);
1881            if (f != null) {
1882                performPendingDeferredStart(f);
1883            }
1884        }
1885    }
1886
1887    void makeActive(Fragment f) {
1888        if (f.mIndex >= 0) {
1889            return;
1890        }
1891
1892        f.setIndex(mNextFragmentIndex++, mParent);
1893        if (mActive == null) {
1894            mActive = new SparseArray<>();
1895        }
1896        mActive.put(f.mIndex, f);
1897        if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
1898    }
1899
1900    void makeInactive(Fragment f) {
1901        if (f.mIndex < 0) {
1902            return;
1903        }
1904
1905        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
1906        // Don't remove yet. That happens in burpActive(). This prevents
1907        // concurrent modification while iterating over mActive
1908        mActive.put(f.mIndex, null);
1909
1910        f.initState();
1911    }
1912
1913    public void addFragment(Fragment fragment, boolean moveToStateNow) {
1914        if (DEBUG) Log.v(TAG, "add: " + fragment);
1915        makeActive(fragment);
1916        if (!fragment.mDetached) {
1917            if (mAdded.contains(fragment)) {
1918                throw new IllegalStateException("Fragment already added: " + fragment);
1919            }
1920            synchronized (mAdded) {
1921                mAdded.add(fragment);
1922            }
1923            fragment.mAdded = true;
1924            fragment.mRemoving = false;
1925            if (fragment.mView == null) {
1926                fragment.mHiddenChanged = false;
1927            }
1928            if (fragment.mHasMenu && fragment.mMenuVisible) {
1929                mNeedMenuInvalidate = true;
1930            }
1931            if (moveToStateNow) {
1932                moveToState(fragment);
1933            }
1934        }
1935    }
1936
1937    public void removeFragment(Fragment fragment) {
1938        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
1939        final boolean inactive = !fragment.isInBackStack();
1940        if (!fragment.mDetached || inactive) {
1941            synchronized (mAdded) {
1942                mAdded.remove(fragment);
1943            }
1944            if (fragment.mHasMenu && fragment.mMenuVisible) {
1945                mNeedMenuInvalidate = true;
1946            }
1947            fragment.mAdded = false;
1948            fragment.mRemoving = true;
1949        }
1950    }
1951
1952    /**
1953     * Marks a fragment as hidden to be later animated in with
1954     * {@link #completeShowHideFragment(Fragment)}.
1955     *
1956     * @param fragment The fragment to be shown.
1957     */
1958    public void hideFragment(Fragment fragment) {
1959        if (DEBUG) Log.v(TAG, "hide: " + fragment);
1960        if (!fragment.mHidden) {
1961            fragment.mHidden = true;
1962            // Toggle hidden changed so that if a fragment goes through show/hide/show
1963            // it doesn't go through the animation.
1964            fragment.mHiddenChanged = !fragment.mHiddenChanged;
1965        }
1966    }
1967
1968    /**
1969     * Marks a fragment as shown to be later animated in with
1970     * {@link #completeShowHideFragment(Fragment)}.
1971     *
1972     * @param fragment The fragment to be shown.
1973     */
1974    public void showFragment(Fragment fragment) {
1975        if (DEBUG) Log.v(TAG, "show: " + fragment);
1976        if (fragment.mHidden) {
1977            fragment.mHidden = false;
1978            // Toggle hidden changed so that if a fragment goes through show/hide/show
1979            // it doesn't go through the animation.
1980            fragment.mHiddenChanged = !fragment.mHiddenChanged;
1981        }
1982    }
1983
1984    public void detachFragment(Fragment fragment) {
1985        if (DEBUG) Log.v(TAG, "detach: " + fragment);
1986        if (!fragment.mDetached) {
1987            fragment.mDetached = true;
1988            if (fragment.mAdded) {
1989                // We are not already in back stack, so need to remove the fragment.
1990                if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
1991                synchronized (mAdded) {
1992                    mAdded.remove(fragment);
1993                }
1994                if (fragment.mHasMenu && fragment.mMenuVisible) {
1995                    mNeedMenuInvalidate = true;
1996                }
1997                fragment.mAdded = false;
1998            }
1999        }
2000    }
2001
2002    public void attachFragment(Fragment fragment) {
2003        if (DEBUG) Log.v(TAG, "attach: " + fragment);
2004        if (fragment.mDetached) {
2005            fragment.mDetached = false;
2006            if (!fragment.mAdded) {
2007                if (mAdded.contains(fragment)) {
2008                    throw new IllegalStateException("Fragment already added: " + fragment);
2009                }
2010                if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
2011                synchronized (mAdded) {
2012                    mAdded.add(fragment);
2013                }
2014                fragment.mAdded = true;
2015                if (fragment.mHasMenu && fragment.mMenuVisible) {
2016                    mNeedMenuInvalidate = true;
2017                }
2018            }
2019        }
2020    }
2021
2022    @Override
2023    @Nullable
2024    public Fragment findFragmentById(int id) {
2025        // First look through added fragments.
2026        for (int i = mAdded.size() - 1; i >= 0; i--) {
2027            Fragment f = mAdded.get(i);
2028            if (f != null && f.mFragmentId == id) {
2029                return f;
2030            }
2031        }
2032        if (mActive != null) {
2033            // Now for any known fragment.
2034            for (int i=mActive.size()-1; i>=0; i--) {
2035                Fragment f = mActive.valueAt(i);
2036                if (f != null && f.mFragmentId == id) {
2037                    return f;
2038                }
2039            }
2040        }
2041        return null;
2042    }
2043
2044    @Override
2045    @Nullable
2046    public Fragment findFragmentByTag(@Nullable String tag) {
2047        if (tag != null) {
2048            // First look through added fragments.
2049            for (int i=mAdded.size()-1; i>=0; i--) {
2050                Fragment f = mAdded.get(i);
2051                if (f != null && tag.equals(f.mTag)) {
2052                    return f;
2053                }
2054            }
2055        }
2056        if (mActive != null && tag != null) {
2057            // Now for any known fragment.
2058            for (int i=mActive.size()-1; i>=0; i--) {
2059                Fragment f = mActive.valueAt(i);
2060                if (f != null && tag.equals(f.mTag)) {
2061                    return f;
2062                }
2063            }
2064        }
2065        return null;
2066    }
2067
2068    public Fragment findFragmentByWho(String who) {
2069        if (mActive != null && who != null) {
2070            for (int i=mActive.size()-1; i>=0; i--) {
2071                Fragment f = mActive.valueAt(i);
2072                if (f != null && (f=f.findFragmentByWho(who)) != null) {
2073                    return f;
2074                }
2075            }
2076        }
2077        return null;
2078    }
2079
2080    private void checkStateLoss() {
2081        if (isStateSaved()) {
2082            throw new IllegalStateException(
2083                    "Can not perform this action after onSaveInstanceState");
2084        }
2085        if (mNoTransactionsBecause != null) {
2086            throw new IllegalStateException(
2087                    "Can not perform this action inside of " + mNoTransactionsBecause);
2088        }
2089    }
2090
2091    @Override
2092    public boolean isStateSaved() {
2093        // See saveAllState() for the explanation of this.  We do this for
2094        // all platform versions, to keep our behavior more consistent between
2095        // them.
2096        return mStateSaved || mStopped;
2097    }
2098
2099    /**
2100     * Adds an action to the queue of pending actions.
2101     *
2102     * @param action the action to add
2103     * @param allowStateLoss whether to allow loss of state information
2104     * @throws IllegalStateException if the activity has been destroyed
2105     */
2106    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
2107        if (!allowStateLoss) {
2108            checkStateLoss();
2109        }
2110        synchronized (this) {
2111            if (mDestroyed || mHost == null) {
2112                if (allowStateLoss) {
2113                    // This FragmentManager isn't attached, so drop the entire transaction.
2114                    return;
2115                }
2116                throw new IllegalStateException("Activity has been destroyed");
2117            }
2118            if (mPendingActions == null) {
2119                mPendingActions = new ArrayList<>();
2120            }
2121            mPendingActions.add(action);
2122            scheduleCommit();
2123        }
2124    }
2125
2126    /**
2127     * Schedules the execution when one hasn't been scheduled already. This should happen
2128     * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
2129     * a postponed transaction has been started with
2130     * {@link Fragment#startPostponedEnterTransition()}
2131     */
2132    private void scheduleCommit() {
2133        synchronized (this) {
2134            boolean postponeReady =
2135                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
2136            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
2137            if (postponeReady || pendingReady) {
2138                mHost.getHandler().removeCallbacks(mExecCommit);
2139                mHost.getHandler().post(mExecCommit);
2140            }
2141        }
2142    }
2143
2144    public int allocBackStackIndex(BackStackRecord bse) {
2145        synchronized (this) {
2146            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
2147                if (mBackStackIndices == null) {
2148                    mBackStackIndices = new ArrayList<BackStackRecord>();
2149                }
2150                int index = mBackStackIndices.size();
2151                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
2152                mBackStackIndices.add(bse);
2153                return index;
2154
2155            } else {
2156                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
2157                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
2158                mBackStackIndices.set(index, bse);
2159                return index;
2160            }
2161        }
2162    }
2163
2164    public void setBackStackIndex(int index, BackStackRecord bse) {
2165        synchronized (this) {
2166            if (mBackStackIndices == null) {
2167                mBackStackIndices = new ArrayList<BackStackRecord>();
2168            }
2169            int N = mBackStackIndices.size();
2170            if (index < N) {
2171                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
2172                mBackStackIndices.set(index, bse);
2173            } else {
2174                while (N < index) {
2175                    mBackStackIndices.add(null);
2176                    if (mAvailBackStackIndices == null) {
2177                        mAvailBackStackIndices = new ArrayList<Integer>();
2178                    }
2179                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
2180                    mAvailBackStackIndices.add(N);
2181                    N++;
2182                }
2183                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
2184                mBackStackIndices.add(bse);
2185            }
2186        }
2187    }
2188
2189    public void freeBackStackIndex(int index) {
2190        synchronized (this) {
2191            mBackStackIndices.set(index, null);
2192            if (mAvailBackStackIndices == null) {
2193                mAvailBackStackIndices = new ArrayList<Integer>();
2194            }
2195            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
2196            mAvailBackStackIndices.add(index);
2197        }
2198    }
2199
2200    /**
2201     * Broken out from exec*, this prepares for gathering and executing operations.
2202     *
2203     * @param allowStateLoss true if state loss should be ignored or false if it should be
2204     *                       checked.
2205     */
2206    private void ensureExecReady(boolean allowStateLoss) {
2207        if (mExecutingActions) {
2208            throw new IllegalStateException("FragmentManager is already executing transactions");
2209        }
2210
2211        if (mHost == null) {
2212            throw new IllegalStateException("Fragment host has been destroyed");
2213        }
2214
2215        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
2216            throw new IllegalStateException("Must be called from main thread of fragment host");
2217        }
2218
2219        if (!allowStateLoss) {
2220            checkStateLoss();
2221        }
2222
2223        if (mTmpRecords == null) {
2224            mTmpRecords = new ArrayList<>();
2225            mTmpIsPop = new ArrayList<>();
2226        }
2227        mExecutingActions = true;
2228        try {
2229            executePostponedTransaction(null, null);
2230        } finally {
2231            mExecutingActions = false;
2232        }
2233    }
2234
2235    public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
2236        if (allowStateLoss && (mHost == null || mDestroyed)) {
2237            // This FragmentManager isn't attached, so drop the entire transaction.
2238            return;
2239        }
2240        ensureExecReady(allowStateLoss);
2241        if (action.generateOps(mTmpRecords, mTmpIsPop)) {
2242            mExecutingActions = true;
2243            try {
2244                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
2245            } finally {
2246                cleanupExec();
2247            }
2248        }
2249
2250        doPendingDeferredStart();
2251        burpActive();
2252    }
2253
2254    /**
2255     * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures
2256     * used in executing operations.
2257     */
2258    private void cleanupExec() {
2259        mExecutingActions = false;
2260        mTmpIsPop.clear();
2261        mTmpRecords.clear();
2262    }
2263
2264    /**
2265     * Only call from main thread!
2266     */
2267    public boolean execPendingActions() {
2268        ensureExecReady(true);
2269
2270        boolean didSomething = false;
2271        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
2272            mExecutingActions = true;
2273            try {
2274                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
2275            } finally {
2276                cleanupExec();
2277            }
2278            didSomething = true;
2279        }
2280
2281        doPendingDeferredStart();
2282        burpActive();
2283
2284        return didSomething;
2285    }
2286
2287    /**
2288     * Complete the execution of transactions that have previously been postponed, but are
2289     * now ready.
2290     */
2291    private void executePostponedTransaction(ArrayList<BackStackRecord> records,
2292            ArrayList<Boolean> isRecordPop) {
2293        int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size();
2294        for (int i = 0; i < numPostponed; i++) {
2295            StartEnterTransitionListener listener = mPostponedTransactions.get(i);
2296            if (records != null && !listener.mIsBack) {
2297                int index = records.indexOf(listener.mRecord);
2298                if (index != -1 && isRecordPop.get(index)) {
2299                    listener.cancelTransaction();
2300                    continue;
2301                }
2302            }
2303            if (listener.isReady() || (records != null
2304                    && listener.mRecord.interactsWith(records, 0, records.size()))) {
2305                mPostponedTransactions.remove(i);
2306                i--;
2307                numPostponed--;
2308                int index;
2309                if (records != null && !listener.mIsBack
2310                        && (index = records.indexOf(listener.mRecord)) != -1
2311                        && isRecordPop.get(index)) {
2312                    // This is popping a postponed transaction
2313                    listener.cancelTransaction();
2314                } else {
2315                    listener.completeTransaction();
2316                }
2317            }
2318        }
2319    }
2320
2321    /**
2322     * Remove redundant BackStackRecord operations and executes them. This method merges operations
2323     * of proximate records that allow reordering. See
2324     * {@link FragmentTransaction#setReorderingAllowed(boolean)}.
2325     * <p>
2326     * For example, a transaction that adds to the back stack and then another that pops that
2327     * back stack record will be optimized to remove the unnecessary operation.
2328     * <p>
2329     * Likewise, two transactions committed that are executed at the same time will be optimized
2330     * to remove the redundant operations as well as two pop operations executed together.
2331     *
2332     * @param records The records pending execution
2333     * @param isRecordPop The direction that these records are being run.
2334     */
2335    private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
2336            ArrayList<Boolean> isRecordPop) {
2337        if (records == null || records.isEmpty()) {
2338            return;
2339        }
2340
2341        if (isRecordPop == null || records.size() != isRecordPop.size()) {
2342            throw new IllegalStateException("Internal error with the back stack records");
2343        }
2344
2345        // Force start of any postponed transactions that interact with scheduled transactions:
2346        executePostponedTransaction(records, isRecordPop);
2347
2348        final int numRecords = records.size();
2349        int startIndex = 0;
2350        for (int recordNum = 0; recordNum < numRecords; recordNum++) {
2351            final boolean canReorder = records.get(recordNum).mReorderingAllowed;
2352            if (!canReorder) {
2353                // execute all previous transactions
2354                if (startIndex != recordNum) {
2355                    executeOpsTogether(records, isRecordPop, startIndex, recordNum);
2356                }
2357                // execute all pop operations that don't allow reordering together or
2358                // one add operation
2359                int reorderingEnd = recordNum + 1;
2360                if (isRecordPop.get(recordNum)) {
2361                    while (reorderingEnd < numRecords
2362                            && isRecordPop.get(reorderingEnd)
2363                            && !records.get(reorderingEnd).mReorderingAllowed) {
2364                        reorderingEnd++;
2365                    }
2366                }
2367                executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
2368                startIndex = reorderingEnd;
2369                recordNum = reorderingEnd - 1;
2370            }
2371        }
2372        if (startIndex != numRecords) {
2373            executeOpsTogether(records, isRecordPop, startIndex, numRecords);
2374        }
2375    }
2376
2377    /**
2378     * Executes a subset of a list of BackStackRecords, all of which either allow reordering or
2379     * do not allow ordering.
2380     * @param records A list of BackStackRecords that are to be executed
2381     * @param isRecordPop The direction that these records are being run.
2382     * @param startIndex The index of the first record in <code>records</code> to be executed
2383     * @param endIndex One more than the final record index in <code>records</code> to executed.
2384     */
2385    private void executeOpsTogether(ArrayList<BackStackRecord> records,
2386            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
2387        final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
2388        boolean addToBackStack = false;
2389        if (mTmpAddedFragments == null) {
2390            mTmpAddedFragments = new ArrayList<>();
2391        } else {
2392            mTmpAddedFragments.clear();
2393        }
2394        mTmpAddedFragments.addAll(mAdded);
2395        Fragment oldPrimaryNav = getPrimaryNavigationFragment();
2396        for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
2397            final BackStackRecord record = records.get(recordNum);
2398            final boolean isPop = isRecordPop.get(recordNum);
2399            if (!isPop) {
2400                oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
2401            } else {
2402                oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
2403            }
2404            addToBackStack = addToBackStack || record.mAddToBackStack;
2405        }
2406        mTmpAddedFragments.clear();
2407
2408        if (!allowReordering) {
2409            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
2410                    false);
2411        }
2412        executeOps(records, isRecordPop, startIndex, endIndex);
2413
2414        int postponeIndex = endIndex;
2415        if (allowReordering) {
2416            ArraySet<Fragment> addedFragments = new ArraySet<>();
2417            addAddedFragments(addedFragments);
2418            postponeIndex = postponePostponableTransactions(records, isRecordPop,
2419                    startIndex, endIndex, addedFragments);
2420            makeRemovedFragmentsInvisible(addedFragments);
2421        }
2422
2423        if (postponeIndex != startIndex && allowReordering) {
2424            // need to run something now
2425            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
2426                    postponeIndex, true);
2427            moveToState(mCurState, true);
2428        }
2429
2430        for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
2431            final BackStackRecord record = records.get(recordNum);
2432            final boolean isPop = isRecordPop.get(recordNum);
2433            if (isPop && record.mIndex >= 0) {
2434                freeBackStackIndex(record.mIndex);
2435                record.mIndex = -1;
2436            }
2437            record.runOnCommitRunnables();
2438        }
2439        if (addToBackStack) {
2440            reportBackStackChanged();
2441        }
2442    }
2443
2444    /**
2445     * Any fragments that were removed because they have been postponed should have their views
2446     * made invisible by setting their alpha to 0.
2447     *
2448     * @param fragments The fragments that were added during operation execution. Only the ones
2449     *                  that are no longer added will have their alpha changed.
2450     */
2451    private void makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments) {
2452        final int numAdded = fragments.size();
2453        for (int i = 0; i < numAdded; i++) {
2454            final Fragment fragment = fragments.valueAt(i);
2455            if (!fragment.mAdded) {
2456                final View view = fragment.getView();
2457                fragment.mPostponedAlpha = view.getAlpha();
2458                view.setAlpha(0f);
2459            }
2460        }
2461    }
2462
2463    /**
2464     * Examine all transactions and determine which ones are marked as postponed. Those will
2465     * have their operations rolled back and moved to the end of the record list (up to endIndex).
2466     * It will also add the postponed transaction to the queue.
2467     *
2468     * @param records A list of BackStackRecords that should be checked.
2469     * @param isRecordPop The direction that these records are being run.
2470     * @param startIndex The index of the first record in <code>records</code> to be checked
2471     * @param endIndex One more than the final record index in <code>records</code> to be checked.
2472     * @return The index of the first postponed transaction or endIndex if no transaction was
2473     * postponed.
2474     */
2475    private int postponePostponableTransactions(ArrayList<BackStackRecord> records,
2476            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex,
2477            ArraySet<Fragment> added) {
2478        int postponeIndex = endIndex;
2479        for (int i = endIndex - 1; i >= startIndex; i--) {
2480            final BackStackRecord record = records.get(i);
2481            final boolean isPop = isRecordPop.get(i);
2482            boolean isPostponed = record.isPostponed()
2483                    && !record.interactsWith(records, i + 1, endIndex);
2484            if (isPostponed) {
2485                if (mPostponedTransactions == null) {
2486                    mPostponedTransactions = new ArrayList<>();
2487                }
2488                StartEnterTransitionListener listener =
2489                        new StartEnterTransitionListener(record, isPop);
2490                mPostponedTransactions.add(listener);
2491                record.setOnStartPostponedListener(listener);
2492
2493                // roll back the transaction
2494                if (isPop) {
2495                    record.executeOps();
2496                } else {
2497                    record.executePopOps(false);
2498                }
2499
2500                // move to the end
2501                postponeIndex--;
2502                if (i != postponeIndex) {
2503                    records.remove(i);
2504                    records.add(postponeIndex, record);
2505                }
2506
2507                // different views may be visible now
2508                addAddedFragments(added);
2509            }
2510        }
2511        return postponeIndex;
2512    }
2513
2514    /**
2515     * When a postponed transaction is ready to be started, this completes the transaction,
2516     * removing, hiding, or showing views as well as starting the animations and transitions.
2517     * <p>
2518     * {@code runtransitions} is set to false when the transaction postponement was interrupted
2519     * abnormally -- normally by a new transaction being started that affects the postponed
2520     * transaction.
2521     *
2522     * @param record The transaction to run
2523     * @param isPop true if record is popping or false if it is adding
2524     * @param runTransitions true if the fragment transition should be run or false otherwise.
2525     * @param moveToState true if the state should be changed after executing the operations.
2526     *                    This is false when the transaction is canceled when a postponed
2527     *                    transaction is popped.
2528     */
2529    private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions,
2530            boolean moveToState) {
2531        if (isPop) {
2532            record.executePopOps(moveToState);
2533        } else {
2534            record.executeOps();
2535        }
2536        ArrayList<BackStackRecord> records = new ArrayList<>(1);
2537        ArrayList<Boolean> isRecordPop = new ArrayList<>(1);
2538        records.add(record);
2539        isRecordPop.add(isPop);
2540        if (runTransitions) {
2541            FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
2542        }
2543        if (moveToState) {
2544            moveToState(mCurState, true);
2545        }
2546
2547        if (mActive != null) {
2548            final int numActive = mActive.size();
2549            for (int i = 0; i < numActive; i++) {
2550                // Allow added fragments to be removed during the pop since we aren't going
2551                // to move them to the final state with moveToState(mCurState).
2552                Fragment fragment = mActive.valueAt(i);
2553                if (fragment != null && fragment.mView != null && fragment.mIsNewlyAdded
2554                        && record.interactsWith(fragment.mContainerId)) {
2555                    if (fragment.mPostponedAlpha > 0) {
2556                        fragment.mView.setAlpha(fragment.mPostponedAlpha);
2557                    }
2558                    if (moveToState) {
2559                        fragment.mPostponedAlpha = 0;
2560                    } else {
2561                        fragment.mPostponedAlpha = -1;
2562                        fragment.mIsNewlyAdded = false;
2563                    }
2564                }
2565            }
2566        }
2567    }
2568
2569    /**
2570     * Find a fragment within the fragment's container whose View should be below the passed
2571     * fragment. {@code null} is returned when the fragment has no View or if there should be
2572     * no fragment with a View below the given fragment.
2573     *
2574     * As an example, if mAdded has two Fragments with Views sharing the same container:
2575     * FragmentA
2576     * FragmentB
2577     *
2578     * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA
2579     * had no View, null would be returned.
2580     *
2581     * @param f The fragment that may be on top of another fragment.
2582     * @return The fragment with a View under f, if one exists or null if f has no View or
2583     * there are no fragments with Views in the same container.
2584     */
2585    private Fragment findFragmentUnder(Fragment f) {
2586        final ViewGroup container = f.mContainer;
2587        final View view = f.mView;
2588
2589        if (container == null || view == null) {
2590            return null;
2591        }
2592
2593        final int fragmentIndex = mAdded.indexOf(f);
2594        for (int i = fragmentIndex - 1; i >= 0; i--) {
2595            Fragment underFragment = mAdded.get(i);
2596            if (underFragment.mContainer == container && underFragment.mView != null) {
2597                // Found the fragment under this one
2598                return underFragment;
2599            }
2600        }
2601        return null;
2602    }
2603
2604    /**
2605     * Run the operations in the BackStackRecords, either to push or pop.
2606     *
2607     * @param records The list of records whose operations should be run.
2608     * @param isRecordPop The direction that these records are being run.
2609     * @param startIndex The index of the first entry in records to run.
2610     * @param endIndex One past the index of the final entry in records to run.
2611     */
2612    private static void executeOps(ArrayList<BackStackRecord> records,
2613            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
2614        for (int i = startIndex; i < endIndex; i++) {
2615            final BackStackRecord record = records.get(i);
2616            final boolean isPop = isRecordPop.get(i);
2617            if (isPop) {
2618                record.bumpBackStackNesting(-1);
2619                // Only execute the add operations at the end of
2620                // all transactions.
2621                boolean moveToState = i == (endIndex - 1);
2622                record.executePopOps(moveToState);
2623            } else {
2624                record.bumpBackStackNesting(1);
2625                record.executeOps();
2626            }
2627        }
2628    }
2629
2630    /**
2631     * Ensure that fragments that are added are moved to at least the CREATED state.
2632     * Any newly-added Views are inserted into {@code added} so that the Transaction can be
2633     * postponed with {@link Fragment#postponeEnterTransition()}. They will later be made
2634     * invisible (by setting their alpha to 0) if they have been removed when postponed.
2635     */
2636    private void addAddedFragments(ArraySet<Fragment> added) {
2637        if (mCurState < Fragment.CREATED) {
2638            return;
2639        }
2640        // We want to leave the fragment in the started state
2641        final int state = Math.min(mCurState, Fragment.STARTED);
2642        final int numAdded = mAdded.size();
2643        for (int i = 0; i < numAdded; i++) {
2644            Fragment fragment = mAdded.get(i);
2645            if (fragment.mState < state) {
2646                moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(),
2647                        false);
2648                if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) {
2649                    added.add(fragment);
2650                }
2651            }
2652        }
2653    }
2654
2655    /**
2656     * Starts all postponed transactions regardless of whether they are ready or not.
2657     */
2658    private void forcePostponedTransactions() {
2659        if (mPostponedTransactions != null) {
2660            while (!mPostponedTransactions.isEmpty()) {
2661                mPostponedTransactions.remove(0).completeTransaction();
2662            }
2663        }
2664    }
2665
2666    /**
2667     * Ends the animations of fragments so that they immediately reach the end state.
2668     * This is used prior to saving the state so that the correct state is saved.
2669     */
2670    private void endAnimatingAwayFragments() {
2671        final int numFragments = mActive == null ? 0 : mActive.size();
2672        for (int i = 0; i < numFragments; i++) {
2673            Fragment fragment = mActive.valueAt(i);
2674            if (fragment != null) {
2675                if (fragment.getAnimatingAway() != null) {
2676                    // Give up waiting for the animation and just end it.
2677                    final int stateAfterAnimating = fragment.getStateAfterAnimating();
2678                    final View animatingAway = fragment.getAnimatingAway();
2679                    Animation animation = animatingAway.getAnimation();
2680                    if (animation != null) {
2681                        animation.cancel();
2682                        // force-clear the animation, as Animation#cancel() doesn't work prior to N,
2683                        // and will instead cause the animation to infinitely loop
2684                        animatingAway.clearAnimation();
2685                    }
2686                    fragment.setAnimatingAway(null);
2687                    moveToState(fragment, stateAfterAnimating, 0, 0, false);
2688                } else if (fragment.getAnimator() != null) {
2689                    fragment.getAnimator().end();
2690                }
2691            }
2692        }
2693    }
2694
2695    /**
2696     * Adds all records in the pending actions to records and whether they are add or pop
2697     * operations to isPop. After executing, the pending actions will be empty.
2698     *
2699     * @param records All pending actions will generate BackStackRecords added to this.
2700     *                This contains the transactions, in order, to execute.
2701     * @param isPop All pending actions will generate booleans to add to this. This contains
2702     *              an entry for each entry in records to indicate whether or not it is a
2703     *              pop action.
2704     */
2705    private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
2706            ArrayList<Boolean> isPop) {
2707        boolean didSomething = false;
2708        synchronized (this) {
2709            if (mPendingActions == null || mPendingActions.size() == 0) {
2710                return false;
2711            }
2712
2713            final int numActions = mPendingActions.size();
2714            for (int i = 0; i < numActions; i++) {
2715                didSomething |= mPendingActions.get(i).generateOps(records, isPop);
2716            }
2717            mPendingActions.clear();
2718            mHost.getHandler().removeCallbacks(mExecCommit);
2719        }
2720        return didSomething;
2721    }
2722
2723    void doPendingDeferredStart() {
2724        if (mHavePendingDeferredStart) {
2725            mHavePendingDeferredStart = false;
2726            startPendingDeferredFragments();
2727        }
2728    }
2729
2730    void reportBackStackChanged() {
2731        if (mBackStackChangeListeners != null) {
2732            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
2733                mBackStackChangeListeners.get(i).onBackStackChanged();
2734            }
2735        }
2736    }
2737
2738    void addBackStackState(BackStackRecord state) {
2739        if (mBackStack == null) {
2740            mBackStack = new ArrayList<BackStackRecord>();
2741        }
2742        mBackStack.add(state);
2743    }
2744
2745    @SuppressWarnings("unused")
2746    boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
2747            String name, int id, int flags) {
2748        if (mBackStack == null) {
2749            return false;
2750        }
2751        if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
2752            int last = mBackStack.size() - 1;
2753            if (last < 0) {
2754                return false;
2755            }
2756            records.add(mBackStack.remove(last));
2757            isRecordPop.add(true);
2758        } else {
2759            int index = -1;
2760            if (name != null || id >= 0) {
2761                // If a name or ID is specified, look for that place in
2762                // the stack.
2763                index = mBackStack.size()-1;
2764                while (index >= 0) {
2765                    BackStackRecord bss = mBackStack.get(index);
2766                    if (name != null && name.equals(bss.getName())) {
2767                        break;
2768                    }
2769                    if (id >= 0 && id == bss.mIndex) {
2770                        break;
2771                    }
2772                    index--;
2773                }
2774                if (index < 0) {
2775                    return false;
2776                }
2777                if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
2778                    index--;
2779                    // Consume all following entries that match.
2780                    while (index >= 0) {
2781                        BackStackRecord bss = mBackStack.get(index);
2782                        if ((name != null && name.equals(bss.getName()))
2783                                || (id >= 0 && id == bss.mIndex)) {
2784                            index--;
2785                            continue;
2786                        }
2787                        break;
2788                    }
2789                }
2790            }
2791            if (index == mBackStack.size()-1) {
2792                return false;
2793            }
2794            for (int i = mBackStack.size() - 1; i > index; i--) {
2795                records.add(mBackStack.remove(i));
2796                isRecordPop.add(true);
2797            }
2798        }
2799        return true;
2800    }
2801
2802    FragmentManagerNonConfig retainNonConfig() {
2803        setRetaining(mSavedNonConfig);
2804        return mSavedNonConfig;
2805    }
2806
2807    /**
2808     * Recurse the FragmentManagerNonConfig fragments and set the mRetaining to true. This
2809     * was previously done while saving the non-config state, but that has been moved to
2810     * {@link #saveNonConfig()} called from {@link #saveAllState()}. If mRetaining is set too
2811     * early, the fragment won't be destroyed when the FragmentManager is destroyed.
2812     */
2813    private static void setRetaining(FragmentManagerNonConfig nonConfig) {
2814        if (nonConfig == null) {
2815            return;
2816        }
2817        List<Fragment> fragments = nonConfig.getFragments();
2818        if (fragments != null) {
2819            for (Fragment fragment : fragments) {
2820                fragment.mRetaining = true;
2821            }
2822        }
2823        List<FragmentManagerNonConfig> children = nonConfig.getChildNonConfigs();
2824        if (children != null) {
2825            for (FragmentManagerNonConfig child : children) {
2826                setRetaining(child);
2827            }
2828        }
2829    }
2830
2831    void saveNonConfig() {
2832        ArrayList<Fragment> fragments = null;
2833        ArrayList<FragmentManagerNonConfig> childFragments = null;
2834        ArrayList<ViewModelStore> viewModelStores = null;
2835        if (mActive != null) {
2836            for (int i=0; i<mActive.size(); i++) {
2837                Fragment f = mActive.valueAt(i);
2838                if (f != null) {
2839                    if (f.mRetainInstance) {
2840                        if (fragments == null) {
2841                            fragments = new ArrayList<Fragment>();
2842                        }
2843                        fragments.add(f);
2844                        f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
2845                        if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
2846                    }
2847                    FragmentManagerNonConfig child;
2848                    if (f.mChildFragmentManager != null) {
2849                        f.mChildFragmentManager.saveNonConfig();
2850                        child = f.mChildFragmentManager.mSavedNonConfig;
2851                    } else {
2852                        // f.mChildNonConfig may be not null, when the parent fragment is
2853                        // in the backstack.
2854                        child = f.mChildNonConfig;
2855                    }
2856
2857                    if (childFragments == null && child != null) {
2858                        childFragments = new ArrayList<>(mActive.size());
2859                        for (int j = 0; j < i; j++) {
2860                            childFragments.add(null);
2861                        }
2862                    }
2863
2864                    if (childFragments != null) {
2865                        childFragments.add(child);
2866                    }
2867                    if (viewModelStores == null && f.mViewModelStore != null) {
2868                        viewModelStores = new ArrayList<>(mActive.size());
2869                        for (int j = 0; j < i; j++) {
2870                            viewModelStores.add(null);
2871                        }
2872                    }
2873
2874                    if (viewModelStores != null) {
2875                        viewModelStores.add(f.mViewModelStore);
2876                    }
2877                }
2878            }
2879        }
2880        if (fragments == null && childFragments == null && viewModelStores == null) {
2881            mSavedNonConfig = null;
2882        } else {
2883            mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments,
2884                    viewModelStores);
2885        }
2886    }
2887
2888    void saveFragmentViewState(Fragment f) {
2889        if (f.mInnerView == null) {
2890            return;
2891        }
2892        if (mStateArray == null) {
2893            mStateArray = new SparseArray<Parcelable>();
2894        } else {
2895            mStateArray.clear();
2896        }
2897        f.mInnerView.saveHierarchyState(mStateArray);
2898        if (mStateArray.size() > 0) {
2899            f.mSavedViewState = mStateArray;
2900            mStateArray = null;
2901        }
2902    }
2903
2904    Bundle saveFragmentBasicState(Fragment f) {
2905        Bundle result = null;
2906
2907        if (mStateBundle == null) {
2908            mStateBundle = new Bundle();
2909        }
2910        f.performSaveInstanceState(mStateBundle);
2911        dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
2912        if (!mStateBundle.isEmpty()) {
2913            result = mStateBundle;
2914            mStateBundle = null;
2915        }
2916
2917        if (f.mView != null) {
2918            saveFragmentViewState(f);
2919        }
2920        if (f.mSavedViewState != null) {
2921            if (result == null) {
2922                result = new Bundle();
2923            }
2924            result.putSparseParcelableArray(
2925                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
2926        }
2927        if (!f.mUserVisibleHint) {
2928            if (result == null) {
2929                result = new Bundle();
2930            }
2931            // Only add this if it's not the default value
2932            result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
2933        }
2934
2935        return result;
2936    }
2937
2938    Parcelable saveAllState() {
2939        // Make sure all pending operations have now been executed to get
2940        // our state update-to-date.
2941        forcePostponedTransactions();
2942        endAnimatingAwayFragments();
2943        execPendingActions();
2944
2945        mStateSaved = true;
2946        mSavedNonConfig = null;
2947
2948        if (mActive == null || mActive.size() <= 0) {
2949            return null;
2950        }
2951
2952        // First collect all active fragments.
2953        int N = mActive.size();
2954        FragmentState[] active = new FragmentState[N];
2955        boolean haveFragments = false;
2956        for (int i=0; i<N; i++) {
2957            Fragment f = mActive.valueAt(i);
2958            if (f != null) {
2959                if (f.mIndex < 0) {
2960                    throwException(new IllegalStateException(
2961                            "Failure saving state: active " + f
2962                            + " has cleared index: " + f.mIndex));
2963                }
2964
2965                haveFragments = true;
2966
2967                FragmentState fs = new FragmentState(f);
2968                active[i] = fs;
2969
2970                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
2971                    fs.mSavedFragmentState = saveFragmentBasicState(f);
2972
2973                    if (f.mTarget != null) {
2974                        if (f.mTarget.mIndex < 0) {
2975                            throwException(new IllegalStateException(
2976                                    "Failure saving state: " + f
2977                                    + " has target not in fragment manager: " + f.mTarget));
2978                        }
2979                        if (fs.mSavedFragmentState == null) {
2980                            fs.mSavedFragmentState = new Bundle();
2981                        }
2982                        putFragment(fs.mSavedFragmentState,
2983                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
2984                        if (f.mTargetRequestCode != 0) {
2985                            fs.mSavedFragmentState.putInt(
2986                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
2987                                    f.mTargetRequestCode);
2988                        }
2989                    }
2990
2991                } else {
2992                    fs.mSavedFragmentState = f.mSavedFragmentState;
2993                }
2994
2995                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
2996                        + fs.mSavedFragmentState);
2997            }
2998        }
2999
3000        if (!haveFragments) {
3001            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
3002            return null;
3003        }
3004
3005        int[] added = null;
3006        BackStackState[] backStack = null;
3007
3008        // Build list of currently added fragments.
3009        N = mAdded.size();
3010        if (N > 0) {
3011            added = new int[N];
3012            for (int i = 0; i < N; i++) {
3013                added[i] = mAdded.get(i).mIndex;
3014                if (added[i] < 0) {
3015                    throwException(new IllegalStateException(
3016                            "Failure saving state: active " + mAdded.get(i)
3017                            + " has cleared index: " + added[i]));
3018                }
3019                if (DEBUG) {
3020                    Log.v(TAG, "saveAllState: adding fragment #" + i
3021                            + ": " + mAdded.get(i));
3022                }
3023            }
3024        }
3025
3026        // Now save back stack.
3027        if (mBackStack != null) {
3028            N = mBackStack.size();
3029            if (N > 0) {
3030                backStack = new BackStackState[N];
3031                for (int i=0; i<N; i++) {
3032                    backStack[i] = new BackStackState(mBackStack.get(i));
3033                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
3034                            + ": " + mBackStack.get(i));
3035                }
3036            }
3037        }
3038
3039        FragmentManagerState fms = new FragmentManagerState();
3040        fms.mActive = active;
3041        fms.mAdded = added;
3042        fms.mBackStack = backStack;
3043        if (mPrimaryNav != null) {
3044            fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
3045        }
3046        fms.mNextFragmentIndex = mNextFragmentIndex;
3047        saveNonConfig();
3048        return fms;
3049    }
3050
3051    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
3052        // If there is no saved state at all, then there can not be
3053        // any nonConfig fragments either, so that is that.
3054        if (state == null) return;
3055        FragmentManagerState fms = (FragmentManagerState)state;
3056        if (fms.mActive == null) return;
3057
3058        List<FragmentManagerNonConfig> childNonConfigs = null;
3059        List<ViewModelStore> viewModelStores = null;
3060
3061        // First re-attach any non-config instances we are retaining back
3062        // to their saved state, so we don't try to instantiate them again.
3063        if (nonConfig != null) {
3064            List<Fragment> nonConfigFragments = nonConfig.getFragments();
3065            childNonConfigs = nonConfig.getChildNonConfigs();
3066            viewModelStores = nonConfig.getViewModelStores();
3067            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
3068            for (int i = 0; i < count; i++) {
3069                Fragment f = nonConfigFragments.get(i);
3070                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
3071                int index = 0; // index into fms.mActive
3072                while (index < fms.mActive.length && fms.mActive[index].mIndex != f.mIndex) {
3073                    index++;
3074                }
3075                if (index == fms.mActive.length) {
3076                    throwException(new IllegalStateException("Could not find active fragment "
3077                            + "with index " + f.mIndex));
3078                }
3079                FragmentState fs = fms.mActive[index];
3080                fs.mInstance = f;
3081                f.mSavedViewState = null;
3082                f.mBackStackNesting = 0;
3083                f.mInLayout = false;
3084                f.mAdded = false;
3085                f.mTarget = null;
3086                if (fs.mSavedFragmentState != null) {
3087                    fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
3088                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
3089                            FragmentManagerImpl.VIEW_STATE_TAG);
3090                    f.mSavedFragmentState = fs.mSavedFragmentState;
3091                }
3092            }
3093        }
3094
3095        // Build the full list of active fragments, instantiating them from
3096        // their saved state.
3097        mActive = new SparseArray<>(fms.mActive.length);
3098        for (int i=0; i<fms.mActive.length; i++) {
3099            FragmentState fs = fms.mActive[i];
3100            if (fs != null) {
3101                FragmentManagerNonConfig childNonConfig = null;
3102                if (childNonConfigs != null && i < childNonConfigs.size()) {
3103                    childNonConfig = childNonConfigs.get(i);
3104                }
3105                ViewModelStore viewModelStore = null;
3106                if (viewModelStores != null && i < viewModelStores.size()) {
3107                    viewModelStore = viewModelStores.get(i);
3108                }
3109                Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig,
3110                        viewModelStore);
3111                if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
3112                mActive.put(f.mIndex, f);
3113                // Now that the fragment is instantiated (or came from being
3114                // retained above), clear mInstance in case we end up re-restoring
3115                // from this FragmentState again.
3116                fs.mInstance = null;
3117            }
3118        }
3119
3120        // Update the target of all retained fragments.
3121        if (nonConfig != null) {
3122            List<Fragment> nonConfigFragments = nonConfig.getFragments();
3123            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
3124            for (int i = 0; i < count; i++) {
3125                Fragment f = nonConfigFragments.get(i);
3126                if (f.mTargetIndex >= 0) {
3127                    f.mTarget = mActive.get(f.mTargetIndex);
3128                    if (f.mTarget == null) {
3129                        Log.w(TAG, "Re-attaching retained fragment " + f
3130                                + " target no longer exists: " + f.mTargetIndex);
3131                    }
3132                }
3133            }
3134        }
3135
3136        // Build the list of currently added fragments.
3137        mAdded.clear();
3138        if (fms.mAdded != null) {
3139            for (int i=0; i<fms.mAdded.length; i++) {
3140                Fragment f = mActive.get(fms.mAdded[i]);
3141                if (f == null) {
3142                    throwException(new IllegalStateException(
3143                            "No instantiated fragment for index #" + fms.mAdded[i]));
3144                }
3145                f.mAdded = true;
3146                if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
3147                if (mAdded.contains(f)) {
3148                    throw new IllegalStateException("Already added!");
3149                }
3150                synchronized (mAdded) {
3151                    mAdded.add(f);
3152                }
3153            }
3154        }
3155
3156        // Build the back stack.
3157        if (fms.mBackStack != null) {
3158            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
3159            for (int i=0; i<fms.mBackStack.length; i++) {
3160                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
3161                if (DEBUG) {
3162                    Log.v(TAG, "restoreAllState: back stack #" + i
3163                        + " (index " + bse.mIndex + "): " + bse);
3164                    LogWriter logw = new LogWriter(TAG);
3165                    PrintWriter pw = new PrintWriter(logw);
3166                    bse.dump("  ", pw, false);
3167                    pw.close();
3168                }
3169                mBackStack.add(bse);
3170                if (bse.mIndex >= 0) {
3171                    setBackStackIndex(bse.mIndex, bse);
3172                }
3173            }
3174        } else {
3175            mBackStack = null;
3176        }
3177
3178        if (fms.mPrimaryNavActiveIndex >= 0) {
3179            mPrimaryNav = mActive.get(fms.mPrimaryNavActiveIndex);
3180        }
3181        this.mNextFragmentIndex = fms.mNextFragmentIndex;
3182    }
3183
3184    /**
3185     * To prevent list modification errors, mActive sets values to null instead of
3186     * removing them when the Fragment becomes inactive. This cleans up the list at the
3187     * end of executing the transactions.
3188     */
3189    private void burpActive() {
3190        if (mActive != null) {
3191            for (int i = mActive.size() - 1; i >= 0; i--) {
3192                if (mActive.valueAt(i) == null) {
3193                    mActive.delete(mActive.keyAt(i));
3194                }
3195            }
3196        }
3197    }
3198
3199    public void attachController(FragmentHostCallback host,
3200            FragmentContainer container, Fragment parent) {
3201        if (mHost != null) throw new IllegalStateException("Already attached");
3202        mHost = host;
3203        mContainer = container;
3204        mParent = parent;
3205    }
3206
3207    public void noteStateNotSaved() {
3208        mSavedNonConfig = null;
3209        mStateSaved = false;
3210        mStopped = false;
3211        final int addedCount = mAdded.size();
3212        for (int i = 0; i < addedCount; i++) {
3213            Fragment fragment = mAdded.get(i);
3214            if (fragment != null) {
3215                fragment.noteStateNotSaved();
3216            }
3217        }
3218    }
3219
3220    public void dispatchCreate() {
3221        mStateSaved = false;
3222        mStopped = false;
3223        dispatchStateChange(Fragment.CREATED);
3224    }
3225
3226    public void dispatchActivityCreated() {
3227        mStateSaved = false;
3228        mStopped = false;
3229        dispatchStateChange(Fragment.ACTIVITY_CREATED);
3230    }
3231
3232    public void dispatchStart() {
3233        mStateSaved = false;
3234        mStopped = false;
3235        dispatchStateChange(Fragment.STARTED);
3236    }
3237
3238    public void dispatchResume() {
3239        mStateSaved = false;
3240        mStopped = false;
3241        dispatchStateChange(Fragment.RESUMED);
3242    }
3243
3244    public void dispatchPause() {
3245        dispatchStateChange(Fragment.STARTED);
3246    }
3247
3248    public void dispatchStop() {
3249        mStopped = true;
3250        dispatchStateChange(Fragment.STOPPED);
3251    }
3252
3253    public void dispatchReallyStop() {
3254        dispatchStateChange(Fragment.ACTIVITY_CREATED);
3255    }
3256
3257    public void dispatchDestroyView() {
3258        dispatchStateChange(Fragment.CREATED);
3259    }
3260
3261    public void dispatchDestroy() {
3262        mDestroyed = true;
3263        execPendingActions();
3264        dispatchStateChange(Fragment.INITIALIZING);
3265        mHost = null;
3266        mContainer = null;
3267        mParent = null;
3268    }
3269
3270    private void dispatchStateChange(int nextState) {
3271        try {
3272            mExecutingActions = true;
3273            moveToState(nextState, false);
3274        } finally {
3275            mExecutingActions = false;
3276        }
3277        execPendingActions();
3278    }
3279
3280    public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
3281        for (int i = mAdded.size() - 1; i >= 0; --i) {
3282            final Fragment f = mAdded.get(i);
3283            if (f != null) {
3284                f.performMultiWindowModeChanged(isInMultiWindowMode);
3285            }
3286        }
3287    }
3288
3289    public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
3290        for (int i = mAdded.size() - 1; i >= 0; --i) {
3291            final Fragment f = mAdded.get(i);
3292            if (f != null) {
3293                f.performPictureInPictureModeChanged(isInPictureInPictureMode);
3294            }
3295        }
3296    }
3297
3298    public void dispatchConfigurationChanged(Configuration newConfig) {
3299        for (int i = 0; i < mAdded.size(); i++) {
3300            Fragment f = mAdded.get(i);
3301            if (f != null) {
3302                f.performConfigurationChanged(newConfig);
3303            }
3304        }
3305    }
3306
3307    public void dispatchLowMemory() {
3308        for (int i = 0; i < mAdded.size(); i++) {
3309            Fragment f = mAdded.get(i);
3310            if (f != null) {
3311                f.performLowMemory();
3312            }
3313        }
3314    }
3315
3316    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
3317        if (mCurState < Fragment.CREATED) {
3318            return false;
3319        }
3320        boolean show = false;
3321        ArrayList<Fragment> newMenus = null;
3322        for (int i = 0; i < mAdded.size(); i++) {
3323            Fragment f = mAdded.get(i);
3324            if (f != null) {
3325                if (f.performCreateOptionsMenu(menu, inflater)) {
3326                    show = true;
3327                    if (newMenus == null) {
3328                        newMenus = new ArrayList<Fragment>();
3329                    }
3330                    newMenus.add(f);
3331                }
3332            }
3333        }
3334
3335        if (mCreatedMenus != null) {
3336            for (int i=0; i<mCreatedMenus.size(); i++) {
3337                Fragment f = mCreatedMenus.get(i);
3338                if (newMenus == null || !newMenus.contains(f)) {
3339                    f.onDestroyOptionsMenu();
3340                }
3341            }
3342        }
3343
3344        mCreatedMenus = newMenus;
3345
3346        return show;
3347    }
3348
3349    public boolean dispatchPrepareOptionsMenu(Menu menu) {
3350        if (mCurState < Fragment.CREATED) {
3351            return false;
3352        }
3353        boolean show = false;
3354        for (int i = 0; i < mAdded.size(); i++) {
3355            Fragment f = mAdded.get(i);
3356            if (f != null) {
3357                if (f.performPrepareOptionsMenu(menu)) {
3358                    show = true;
3359                }
3360            }
3361        }
3362        return show;
3363    }
3364
3365    public boolean dispatchOptionsItemSelected(MenuItem item) {
3366        if (mCurState < Fragment.CREATED) {
3367            return false;
3368        }
3369        for (int i = 0; i < mAdded.size(); i++) {
3370            Fragment f = mAdded.get(i);
3371            if (f != null) {
3372                if (f.performOptionsItemSelected(item)) {
3373                    return true;
3374                }
3375            }
3376        }
3377        return false;
3378    }
3379
3380    public boolean dispatchContextItemSelected(MenuItem item) {
3381        if (mCurState < Fragment.CREATED) {
3382            return false;
3383        }
3384        for (int i = 0; i < mAdded.size(); i++) {
3385            Fragment f = mAdded.get(i);
3386            if (f != null) {
3387                if (f.performContextItemSelected(item)) {
3388                    return true;
3389                }
3390            }
3391        }
3392        return false;
3393    }
3394
3395    public void dispatchOptionsMenuClosed(Menu menu) {
3396        if (mCurState < Fragment.CREATED) {
3397            return;
3398        }
3399        for (int i = 0; i < mAdded.size(); i++) {
3400            Fragment f = mAdded.get(i);
3401            if (f != null) {
3402                f.performOptionsMenuClosed(menu);
3403            }
3404        }
3405    }
3406
3407    @SuppressWarnings("ReferenceEquality")
3408    public void setPrimaryNavigationFragment(Fragment f) {
3409        if (f != null && (mActive.get(f.mIndex) != f
3410            || (f.mHost != null && f.getFragmentManager() != this))) {
3411            throw new IllegalArgumentException("Fragment " + f
3412                    + " is not an active fragment of FragmentManager " + this);
3413        }
3414        mPrimaryNav = f;
3415    }
3416
3417    @Override
3418    @Nullable
3419    public Fragment getPrimaryNavigationFragment() {
3420        return mPrimaryNav;
3421    }
3422
3423    @Override
3424    public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
3425            boolean recursive) {
3426        mLifecycleCallbacks.add(new FragmentLifecycleCallbacksHolder(cb, recursive));
3427    }
3428
3429    @Override
3430    public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) {
3431        synchronized (mLifecycleCallbacks) {
3432            for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) {
3433                if (mLifecycleCallbacks.get(i).mCallback == cb) {
3434                    mLifecycleCallbacks.remove(i);
3435                    break;
3436                }
3437            }
3438        }
3439    }
3440
3441    void dispatchOnFragmentPreAttached(@NonNull Fragment f, @NonNull Context context,
3442            boolean onlyRecursive) {
3443        if (mParent != null) {
3444            FragmentManager parentManager = mParent.getFragmentManager();
3445            if (parentManager instanceof FragmentManagerImpl) {
3446                ((FragmentManagerImpl) parentManager)
3447                        .dispatchOnFragmentPreAttached(f, context, true);
3448            }
3449        }
3450        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3451            if (!onlyRecursive || holder.mRecursive) {
3452                holder.mCallback.onFragmentPreAttached(this, f, context);
3453            }
3454        }
3455    }
3456
3457    void dispatchOnFragmentAttached(@NonNull Fragment f, @NonNull Context context,
3458            boolean onlyRecursive) {
3459        if (mParent != null) {
3460            FragmentManager parentManager = mParent.getFragmentManager();
3461            if (parentManager instanceof FragmentManagerImpl) {
3462                ((FragmentManagerImpl) parentManager)
3463                        .dispatchOnFragmentAttached(f, context, true);
3464            }
3465        }
3466        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3467            if (!onlyRecursive || holder.mRecursive) {
3468                holder.mCallback.onFragmentAttached(this, f, context);
3469            }
3470        }
3471    }
3472
3473    void dispatchOnFragmentPreCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState,
3474            boolean onlyRecursive) {
3475        if (mParent != null) {
3476            FragmentManager parentManager = mParent.getFragmentManager();
3477            if (parentManager instanceof FragmentManagerImpl) {
3478                ((FragmentManagerImpl) parentManager)
3479                        .dispatchOnFragmentPreCreated(f, savedInstanceState, true);
3480            }
3481        }
3482        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3483            if (!onlyRecursive || holder.mRecursive) {
3484                holder.mCallback.onFragmentPreCreated(this, f, savedInstanceState);
3485            }
3486        }
3487    }
3488
3489    void dispatchOnFragmentCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState,
3490            boolean onlyRecursive) {
3491        if (mParent != null) {
3492            FragmentManager parentManager = mParent.getFragmentManager();
3493            if (parentManager instanceof FragmentManagerImpl) {
3494                ((FragmentManagerImpl) parentManager)
3495                        .dispatchOnFragmentCreated(f, savedInstanceState, true);
3496            }
3497        }
3498        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3499            if (!onlyRecursive || holder.mRecursive) {
3500                holder.mCallback.onFragmentCreated(this, f, savedInstanceState);
3501            }
3502        }
3503    }
3504
3505    void dispatchOnFragmentActivityCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState,
3506            boolean onlyRecursive) {
3507        if (mParent != null) {
3508            FragmentManager parentManager = mParent.getFragmentManager();
3509            if (parentManager instanceof FragmentManagerImpl) {
3510                ((FragmentManagerImpl) parentManager)
3511                        .dispatchOnFragmentActivityCreated(f, savedInstanceState, true);
3512            }
3513        }
3514        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3515            if (!onlyRecursive || holder.mRecursive) {
3516                holder.mCallback.onFragmentActivityCreated(this, f, savedInstanceState);
3517            }
3518        }
3519    }
3520
3521    void dispatchOnFragmentViewCreated(@NonNull Fragment f, @NonNull View v,
3522            @Nullable Bundle savedInstanceState, boolean onlyRecursive) {
3523        if (mParent != null) {
3524            FragmentManager parentManager = mParent.getFragmentManager();
3525            if (parentManager instanceof FragmentManagerImpl) {
3526                ((FragmentManagerImpl) parentManager)
3527                        .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true);
3528            }
3529        }
3530        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3531            if (!onlyRecursive || holder.mRecursive) {
3532                holder.mCallback.onFragmentViewCreated(this, f, v, savedInstanceState);
3533            }
3534        }
3535    }
3536
3537    void dispatchOnFragmentStarted(@NonNull Fragment f, boolean onlyRecursive) {
3538        if (mParent != null) {
3539            FragmentManager parentManager = mParent.getFragmentManager();
3540            if (parentManager instanceof FragmentManagerImpl) {
3541                ((FragmentManagerImpl) parentManager)
3542                        .dispatchOnFragmentStarted(f, true);
3543            }
3544        }
3545        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3546            if (!onlyRecursive || holder.mRecursive) {
3547                holder.mCallback.onFragmentStarted(this, f);
3548            }
3549        }
3550    }
3551
3552    void dispatchOnFragmentResumed(@NonNull Fragment f, boolean onlyRecursive) {
3553        if (mParent != null) {
3554            FragmentManager parentManager = mParent.getFragmentManager();
3555            if (parentManager instanceof FragmentManagerImpl) {
3556                ((FragmentManagerImpl) parentManager)
3557                        .dispatchOnFragmentResumed(f, true);
3558            }
3559        }
3560        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3561            if (!onlyRecursive || holder.mRecursive) {
3562                holder.mCallback.onFragmentResumed(this, f);
3563            }
3564        }
3565    }
3566
3567    void dispatchOnFragmentPaused(@NonNull Fragment f, boolean onlyRecursive) {
3568        if (mParent != null) {
3569            FragmentManager parentManager = mParent.getFragmentManager();
3570            if (parentManager instanceof FragmentManagerImpl) {
3571                ((FragmentManagerImpl) parentManager)
3572                        .dispatchOnFragmentPaused(f, true);
3573            }
3574        }
3575        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3576            if (!onlyRecursive || holder.mRecursive) {
3577                holder.mCallback.onFragmentPaused(this, f);
3578            }
3579        }
3580    }
3581
3582    void dispatchOnFragmentStopped(@NonNull Fragment f, boolean onlyRecursive) {
3583        if (mParent != null) {
3584            FragmentManager parentManager = mParent.getFragmentManager();
3585            if (parentManager instanceof FragmentManagerImpl) {
3586                ((FragmentManagerImpl) parentManager)
3587                        .dispatchOnFragmentStopped(f, true);
3588            }
3589        }
3590        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3591            if (!onlyRecursive || holder.mRecursive) {
3592                holder.mCallback.onFragmentStopped(this, f);
3593            }
3594        }
3595    }
3596
3597    void dispatchOnFragmentSaveInstanceState(@NonNull Fragment f, @NonNull Bundle outState,
3598            boolean onlyRecursive) {
3599        if (mParent != null) {
3600            FragmentManager parentManager = mParent.getFragmentManager();
3601            if (parentManager instanceof FragmentManagerImpl) {
3602                ((FragmentManagerImpl) parentManager)
3603                        .dispatchOnFragmentSaveInstanceState(f, outState, true);
3604            }
3605        }
3606        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3607            if (!onlyRecursive || holder.mRecursive) {
3608                holder.mCallback.onFragmentSaveInstanceState(this, f, outState);
3609            }
3610        }
3611    }
3612
3613    void dispatchOnFragmentViewDestroyed(@NonNull Fragment f, boolean onlyRecursive) {
3614        if (mParent != null) {
3615            FragmentManager parentManager = mParent.getFragmentManager();
3616            if (parentManager instanceof FragmentManagerImpl) {
3617                ((FragmentManagerImpl) parentManager)
3618                        .dispatchOnFragmentViewDestroyed(f, true);
3619            }
3620        }
3621        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3622            if (!onlyRecursive || holder.mRecursive) {
3623                holder.mCallback.onFragmentViewDestroyed(this, f);
3624            }
3625        }
3626    }
3627
3628    void dispatchOnFragmentDestroyed(@NonNull Fragment f, boolean onlyRecursive) {
3629        if (mParent != null) {
3630            FragmentManager parentManager = mParent.getFragmentManager();
3631            if (parentManager instanceof FragmentManagerImpl) {
3632                ((FragmentManagerImpl) parentManager)
3633                        .dispatchOnFragmentDestroyed(f, true);
3634            }
3635        }
3636        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3637            if (!onlyRecursive || holder.mRecursive) {
3638                holder.mCallback.onFragmentDestroyed(this, f);
3639            }
3640        }
3641    }
3642
3643    void dispatchOnFragmentDetached(@NonNull Fragment f, boolean onlyRecursive) {
3644        if (mParent != null) {
3645            FragmentManager parentManager = mParent.getFragmentManager();
3646            if (parentManager instanceof FragmentManagerImpl) {
3647                ((FragmentManagerImpl) parentManager)
3648                        .dispatchOnFragmentDetached(f, true);
3649            }
3650        }
3651        for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) {
3652            if (!onlyRecursive || holder.mRecursive) {
3653                holder.mCallback.onFragmentDetached(this, f);
3654            }
3655        }
3656    }
3657
3658    public static int reverseTransit(int transit) {
3659        int rev = 0;
3660        switch (transit) {
3661            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
3662                rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
3663                break;
3664            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
3665                rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
3666                break;
3667            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
3668                rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
3669                break;
3670        }
3671        return rev;
3672
3673    }
3674
3675    public static final int ANIM_STYLE_OPEN_ENTER = 1;
3676    public static final int ANIM_STYLE_OPEN_EXIT = 2;
3677    public static final int ANIM_STYLE_CLOSE_ENTER = 3;
3678    public static final int ANIM_STYLE_CLOSE_EXIT = 4;
3679    public static final int ANIM_STYLE_FADE_ENTER = 5;
3680    public static final int ANIM_STYLE_FADE_EXIT = 6;
3681
3682    public static int transitToStyleIndex(int transit, boolean enter) {
3683        int animAttr = -1;
3684        switch (transit) {
3685            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
3686                animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT;
3687                break;
3688            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
3689                animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT;
3690                break;
3691            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
3692                animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT;
3693                break;
3694        }
3695        return animAttr;
3696    }
3697
3698    @Override
3699    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
3700        if (!"fragment".equals(name)) {
3701            return null;
3702        }
3703
3704        String fname = attrs.getAttributeValue(null, "class");
3705        TypedArray a =  context.obtainStyledAttributes(attrs, FragmentTag.Fragment);
3706        if (fname == null) {
3707            fname = a.getString(FragmentTag.Fragment_name);
3708        }
3709        int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID);
3710        String tag = a.getString(FragmentTag.Fragment_tag);
3711        a.recycle();
3712
3713        if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) {
3714            // Invalid support lib fragment; let the device's framework handle it.
3715            // This will allow android.app.Fragments to do the right thing.
3716            return null;
3717        }
3718
3719        int containerId = parent != null ? parent.getId() : 0;
3720        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
3721            throw new IllegalArgumentException(attrs.getPositionDescription()
3722                    + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
3723        }
3724
3725        // If we restored from a previous state, we may already have
3726        // instantiated this fragment from the state and should use
3727        // that instance instead of making a new one.
3728        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
3729        if (fragment == null && tag != null) {
3730            fragment = findFragmentByTag(tag);
3731        }
3732        if (fragment == null && containerId != View.NO_ID) {
3733            fragment = findFragmentById(containerId);
3734        }
3735
3736        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
3737                + Integer.toHexString(id) + " fname=" + fname
3738                + " existing=" + fragment);
3739        if (fragment == null) {
3740            fragment = mContainer.instantiate(context, fname, null);
3741            fragment.mFromLayout = true;
3742            fragment.mFragmentId = id != 0 ? id : containerId;
3743            fragment.mContainerId = containerId;
3744            fragment.mTag = tag;
3745            fragment.mInLayout = true;
3746            fragment.mFragmentManager = this;
3747            fragment.mHost = mHost;
3748            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
3749            addFragment(fragment, true);
3750
3751        } else if (fragment.mInLayout) {
3752            // A fragment already exists and it is not one we restored from
3753            // previous state.
3754            throw new IllegalArgumentException(attrs.getPositionDescription()
3755                    + ": Duplicate id 0x" + Integer.toHexString(id)
3756                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
3757                    + " with another fragment for " + fname);
3758        } else {
3759            // This fragment was retained from a previous instance; get it
3760            // going now.
3761            fragment.mInLayout = true;
3762            fragment.mHost = mHost;
3763            // If this fragment is newly instantiated (either right now, or
3764            // from last saved state), then give it the attributes to
3765            // initialize itself.
3766            if (!fragment.mRetaining) {
3767                fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
3768            }
3769        }
3770
3771        // If we haven't finished entering the CREATED state ourselves yet,
3772        // push the inflated child fragment along. This will ensureInflatedFragmentView
3773        // at the right phase of the lifecycle so that we will have mView populated
3774        // for compliant fragments below.
3775        if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
3776            moveToState(fragment, Fragment.CREATED, 0, 0, false);
3777        } else {
3778            moveToState(fragment);
3779        }
3780
3781        if (fragment.mView == null) {
3782            throw new IllegalStateException("Fragment " + fname
3783                    + " did not create a view.");
3784        }
3785        if (id != 0) {
3786            fragment.mView.setId(id);
3787        }
3788        if (fragment.mView.getTag() == null) {
3789            fragment.mView.setTag(tag);
3790        }
3791        return fragment.mView;
3792    }
3793
3794    @Override
3795    public View onCreateView(String name, Context context, AttributeSet attrs) {
3796        return onCreateView(null, name, context, attrs);
3797    }
3798
3799    LayoutInflater.Factory2 getLayoutInflaterFactory() {
3800        return this;
3801    }
3802
3803    static class FragmentTag {
3804        public static final int[] Fragment = {
3805                0x01010003, 0x010100d0, 0x010100d1
3806        };
3807        public static final int Fragment_id = 1;
3808        public static final int Fragment_name = 0;
3809        public static final int Fragment_tag = 2;
3810    }
3811
3812    /**
3813     * An add or pop transaction to be scheduled for the UI thread.
3814     */
3815    interface OpGenerator {
3816        /**
3817         * Generate transactions to add to {@code records} and whether or not the transaction is
3818         * an add or pop to {@code isRecordPop}.
3819         *
3820         * records and isRecordPop must be added equally so that each transaction in records
3821         * matches the boolean for whether or not it is a pop in isRecordPop.
3822         *
3823         * @param records A list to add transactions to.
3824         * @param isRecordPop A list to add whether or not the transactions added to records is
3825         *                    a pop transaction.
3826         * @return true if something was added or false otherwise.
3827         */
3828        boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
3829    }
3830
3831    /**
3832     * A pop operation OpGenerator. This will be run on the UI thread and will generate the
3833     * transactions that will be popped if anything can be popped.
3834     */
3835    private class PopBackStackState implements OpGenerator {
3836        final String mName;
3837        final int mId;
3838        final int mFlags;
3839
3840        PopBackStackState(String name, int id, int flags) {
3841            mName = name;
3842            mId = id;
3843            mFlags = flags;
3844        }
3845
3846        @Override
3847        public boolean generateOps(ArrayList<BackStackRecord> records,
3848                ArrayList<Boolean> isRecordPop) {
3849            if (mPrimaryNav != null // We have a primary nav fragment
3850                    && mId < 0 // No valid id (since they're local)
3851                    && mName == null) { // no name to pop to (since they're local)
3852                final FragmentManager childManager = mPrimaryNav.peekChildFragmentManager();
3853                if (childManager != null && childManager.popBackStackImmediate()) {
3854                    // We didn't add any operations for this FragmentManager even though
3855                    // a child did do work.
3856                    return false;
3857                }
3858            }
3859            return popBackStackState(records, isRecordPop, mName, mId, mFlags);
3860        }
3861    }
3862
3863    /**
3864     * A listener for a postponed transaction. This waits until
3865     * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started
3866     * that interacts with this one, based on interactions with the fragment container.
3867     */
3868    static class StartEnterTransitionListener
3869            implements Fragment.OnStartEnterTransitionListener {
3870        private final boolean mIsBack;
3871        private final BackStackRecord mRecord;
3872        private int mNumPostponed;
3873
3874        StartEnterTransitionListener(BackStackRecord record, boolean isBack) {
3875            mIsBack = isBack;
3876            mRecord = record;
3877        }
3878
3879        /**
3880         * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the
3881         * number of Fragments that are postponed. This may cause the transaction to schedule
3882         * to finish running and run transitions and animations.
3883         */
3884        @Override
3885        public void onStartEnterTransition() {
3886            mNumPostponed--;
3887            if (mNumPostponed != 0) {
3888                return;
3889            }
3890            mRecord.mManager.scheduleCommit();
3891        }
3892
3893        /**
3894         * Called from {@link Fragment#
3895         * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this
3896         * increases the number of fragments that are postponed as part of this transaction.
3897         */
3898        @Override
3899        public void startListening() {
3900            mNumPostponed++;
3901        }
3902
3903        /**
3904         * @return true if there are no more postponed fragments as part of the transaction.
3905         */
3906        public boolean isReady() {
3907            return mNumPostponed == 0;
3908        }
3909
3910        /**
3911         * Completes the transaction and start the animations and transitions. This may skip
3912         * the transitions if this is called before all fragments have called
3913         * {@link Fragment#startPostponedEnterTransition()}.
3914         */
3915        public void completeTransaction() {
3916            final boolean canceled;
3917            canceled = mNumPostponed > 0;
3918            FragmentManagerImpl manager = mRecord.mManager;
3919            final int numAdded = manager.mAdded.size();
3920            for (int i = 0; i < numAdded; i++) {
3921                final Fragment fragment = manager.mAdded.get(i);
3922                fragment.setOnStartEnterTransitionListener(null);
3923                if (canceled && fragment.isPostponed()) {
3924                    fragment.startPostponedEnterTransition();
3925                }
3926            }
3927            mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
3928        }
3929
3930        /**
3931         * Cancels this transaction instead of completing it. That means that the state isn't
3932         * changed, so the pop results in no change to the state.
3933         */
3934        public void cancelTransaction() {
3935            mRecord.mManager.completeExecute(mRecord, mIsBack, false, false);
3936        }
3937    }
3938
3939    /**
3940     * Contains either an animator or animation. One of these should be null.
3941     */
3942    private static class AnimationOrAnimator {
3943        public final Animation animation;
3944        public final Animator animator;
3945
3946        private AnimationOrAnimator(Animation animation) {
3947            this.animation = animation;
3948            this.animator = null;
3949            if (animation == null) {
3950                throw new IllegalStateException("Animation cannot be null");
3951            }
3952        }
3953
3954        private AnimationOrAnimator(Animator animator) {
3955            this.animation = null;
3956            this.animator = animator;
3957            if (animator == null) {
3958                throw new IllegalStateException("Animator cannot be null");
3959            }
3960        }
3961    }
3962
3963    /**
3964     * Wrap an AnimationListener that can be null. This allows us to chain animation listeners.
3965     */
3966    private static class AnimationListenerWrapper implements AnimationListener {
3967        private final AnimationListener mWrapped;
3968
3969        private AnimationListenerWrapper(AnimationListener wrapped) {
3970            mWrapped = wrapped;
3971        }
3972
3973        @CallSuper
3974        @Override
3975        public void onAnimationStart(Animation animation) {
3976            if (mWrapped != null) {
3977                mWrapped.onAnimationStart(animation);
3978            }
3979        }
3980
3981        @CallSuper
3982        @Override
3983        public void onAnimationEnd(Animation animation) {
3984            if (mWrapped != null) {
3985                mWrapped.onAnimationEnd(animation);
3986            }
3987        }
3988
3989        @CallSuper
3990        @Override
3991        public void onAnimationRepeat(Animation animation) {
3992            if (mWrapped != null) {
3993                mWrapped.onAnimationRepeat(animation);
3994            }
3995        }
3996    }
3997
3998    /**
3999     * Reset the layer type to LAYER_TYPE_NONE at the end of an animation.
4000     */
4001    private static class AnimateOnHWLayerIfNeededListener extends AnimationListenerWrapper  {
4002        View mView;
4003
4004        AnimateOnHWLayerIfNeededListener(final View v, AnimationListener listener) {
4005            super(listener);
4006            mView = v;
4007        }
4008
4009        @Override
4010        @CallSuper
4011        public void onAnimationEnd(Animation animation) {
4012            // If we're attached to a window, assume we're in the normal performTraversals
4013            // drawing path for Animations running. It's not safe to change the layer type
4014            // during drawing, so post it to the View to run later. If we're not attached
4015            // or we're running on N and above, post it to the view. If we're not on N and
4016            // not attached, do it right now since existing platform versions don't run the
4017            // hwui renderer for detached views off the UI thread making changing layer type
4018            // safe, but posting may not be.
4019            // Prior to N posting to a detached view from a non-Looper thread could cause
4020            // leaks, since the thread-local run queue on a non-Looper thread would never
4021            // be flushed.
4022            if (ViewCompat.isAttachedToWindow(mView) || Build.VERSION.SDK_INT >= 24) {
4023                mView.post(new Runnable() {
4024                    @Override
4025                    public void run() {
4026                        mView.setLayerType(View.LAYER_TYPE_NONE, null);
4027                    }
4028                });
4029            } else {
4030                mView.setLayerType(View.LAYER_TYPE_NONE, null);
4031            }
4032            super.onAnimationEnd(animation);
4033        }
4034    }
4035
4036    /**
4037     * Set the layer type to LAYER_TYPE_HARDWARE while an animator is running.
4038     */
4039    private static class AnimatorOnHWLayerIfNeededListener extends AnimatorListenerAdapter  {
4040        View mView;
4041
4042        AnimatorOnHWLayerIfNeededListener(final View v) {
4043            mView = v;
4044        }
4045
4046        @Override
4047        public void onAnimationStart(Animator animation) {
4048            mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
4049        }
4050
4051        @Override
4052        public void onAnimationEnd(Animator animation) {
4053            mView.setLayerType(View.LAYER_TYPE_NONE, null);
4054            animation.removeListener(this);
4055        }
4056    }
4057
4058    /**
4059     * We must call endViewTransition() before the animation ends or else the parent doesn't
4060     * get nulled out. We use both startViewTransition() and startAnimation() to solve a problem
4061     * with Views remaining in the hierarchy as disappearing children after the view has been
4062     * removed in some edge cases.
4063     */
4064    private static class EndViewTransitionAnimator extends AnimationSet implements Runnable {
4065        private final ViewGroup mParent;
4066        private final View mChild;
4067        private boolean mEnded;
4068        private boolean mTransitionEnded;
4069
4070        EndViewTransitionAnimator(@NonNull Animation animation,
4071                @NonNull ViewGroup parent, @NonNull View child) {
4072            super(false);
4073            mParent = parent;
4074            mChild = child;
4075            addAnimation(animation);
4076        }
4077
4078        @Override
4079        public boolean getTransformation(long currentTime, Transformation t) {
4080            if (mEnded) {
4081                return !mTransitionEnded;
4082            }
4083            boolean more = super.getTransformation(currentTime, t);
4084            if (!more) {
4085                mEnded = true;
4086                OneShotPreDrawListener.add(mParent, this);
4087            }
4088            return true;
4089        }
4090
4091        @Override
4092        public boolean getTransformation(long currentTime, Transformation outTransformation,
4093                float scale) {
4094            if (mEnded) {
4095                return !mTransitionEnded;
4096            }
4097            boolean more = super.getTransformation(currentTime, outTransformation, scale);
4098            if (!more) {
4099                mEnded = true;
4100                OneShotPreDrawListener.add(mParent, this);
4101            }
4102            return true;
4103        }
4104
4105        @Override
4106        public void run() {
4107            mParent.endViewTransition(mChild);
4108            mTransitionEnded = true;
4109        }
4110    }
4111}
4112