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