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