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