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