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.res.Configuration;
27 import android.content.res.Resources.NotFoundException;
28 import android.content.res.TypedArray;
29 import android.os.Bundle;
30 import android.os.Debug;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.Parcel;
34 import android.os.Parcelable;
35 import android.util.AttributeSet;
36 import android.util.DebugUtils;
37 import android.util.Log;
38 import android.util.LogWriter;
39 import android.util.SparseArray;
40 import android.util.SuperNotCalledException;
41 import android.view.LayoutInflater;
42 import android.view.Menu;
43 import android.view.MenuInflater;
44 import android.view.MenuItem;
45 import android.view.View;
46 import android.view.ViewGroup;
47 import com.android.internal.util.FastPrintWriter;
48 
49 import java.io.FileDescriptor;
50 import java.io.PrintWriter;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.List;
54 
55 /**
56  * Interface for interacting with {@link Fragment} objects inside of an
57  * {@link Activity}
58  *
59  * <div class="special reference">
60  * <h3>Developer Guides</h3>
61  * <p>For more information about using fragments, read the
62  * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p>
63  * </div>
64  *
65  * While the FragmentManager API was introduced in
66  * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API
67  * at is also available for use on older platforms through
68  * {@link android.support.v4.app.FragmentActivity}.  See the blog post
69  * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
70  * Fragments For All</a> for more details.
71  */
72 public abstract class FragmentManager {
73     /**
74      * Representation of an entry on the fragment back stack, as created
75      * with {@link FragmentTransaction#addToBackStack(String)
76      * FragmentTransaction.addToBackStack()}.  Entries can later be
77      * retrieved with {@link FragmentManager#getBackStackEntryAt(int)
78      * FragmentManager.getBackStackEntryAt()}.
79      *
80      * <p>Note that you should never hold on to a BackStackEntry object;
81      * the identifier as returned by {@link #getId} is the only thing that
82      * will be persisted across activity instances.
83      */
84     public interface BackStackEntry {
85         /**
86          * Return the unique identifier for the entry.  This is the only
87          * representation of the entry that will persist across activity
88          * instances.
89          */
getId()90         public int getId();
91 
92         /**
93          * Get the name that was supplied to
94          * {@link FragmentTransaction#addToBackStack(String)
95          * FragmentTransaction.addToBackStack(String)} when creating this entry.
96          */
getName()97         public String getName();
98 
99         /**
100          * Return the full bread crumb title resource identifier for the entry,
101          * or 0 if it does not have one.
102          */
getBreadCrumbTitleRes()103         public int getBreadCrumbTitleRes();
104 
105         /**
106          * Return the short bread crumb title resource identifier for the entry,
107          * or 0 if it does not have one.
108          */
getBreadCrumbShortTitleRes()109         public int getBreadCrumbShortTitleRes();
110 
111         /**
112          * Return the full bread crumb title for the entry, or null if it
113          * does not have one.
114          */
getBreadCrumbTitle()115         public CharSequence getBreadCrumbTitle();
116 
117         /**
118          * Return the short bread crumb title for the entry, or null if it
119          * does not have one.
120          */
getBreadCrumbShortTitle()121         public CharSequence getBreadCrumbShortTitle();
122     }
123 
124     /**
125      * Interface to watch for changes to the back stack.
126      */
127     public interface OnBackStackChangedListener {
128         /**
129          * Called whenever the contents of the back stack change.
130          */
onBackStackChanged()131         public void onBackStackChanged();
132     }
133 
134     /**
135      * Start a series of edit operations on the Fragments associated with
136      * this FragmentManager.
137      *
138      * <p>Note: A fragment transaction can only be created/committed prior
139      * to an activity saving its state.  If you try to commit a transaction
140      * after {@link Activity#onSaveInstanceState Activity.onSaveInstanceState()}
141      * (and prior to a following {@link Activity#onStart Activity.onStart}
142      * or {@link Activity#onResume Activity.onResume()}, you will get an error.
143      * This is because the framework takes care of saving your current fragments
144      * in the state, and if changes are made after the state is saved then they
145      * will be lost.</p>
146      */
beginTransaction()147     public abstract FragmentTransaction beginTransaction();
148 
149     /** @hide -- remove once prebuilts are in. */
150     @Deprecated
openTransaction()151     public FragmentTransaction openTransaction() {
152         return beginTransaction();
153     }
154 
155     /**
156      * After a {@link FragmentTransaction} is committed with
157      * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it
158      * is scheduled to be executed asynchronously on the process's main thread.
159      * If you want to immediately executing any such pending operations, you
160      * can call this function (only from the main thread) to do so.  Note that
161      * all callbacks and other related behavior will be done from within this
162      * call, so be careful about where this is called from.
163      *
164      * @return Returns true if there were any pending transactions to be
165      * executed.
166      */
executePendingTransactions()167     public abstract boolean executePendingTransactions();
168 
169     /**
170      * Finds a fragment that was identified by the given id either when inflated
171      * from XML or as the container ID when added in a transaction.  This first
172      * searches through fragments that are currently added to the manager's
173      * activity; if no such fragment is found, then all fragments currently
174      * on the back stack associated with this ID are searched.
175      * @return The fragment if found or null otherwise.
176      */
findFragmentById(int id)177     public abstract Fragment findFragmentById(int id);
178 
179     /**
180      * Finds a fragment that was identified by the given tag either when inflated
181      * from XML or as supplied when added in a transaction.  This first
182      * searches through fragments that are currently added to the manager's
183      * activity; if no such fragment is found, then all fragments currently
184      * on the back stack are searched.
185      * @return The fragment if found or null otherwise.
186      */
findFragmentByTag(String tag)187     public abstract Fragment findFragmentByTag(String tag);
188 
189     /**
190      * Flag for {@link #popBackStack(String, int)}
191      * and {@link #popBackStack(int, int)}: If set, and the name or ID of
192      * a back stack entry has been supplied, then all matching entries will
193      * be consumed until one that doesn't match is found or the bottom of
194      * the stack is reached.  Otherwise, all entries up to but not including that entry
195      * will be removed.
196      */
197     public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
198 
199     /**
200      * Pop the top state off the back stack.  This function is asynchronous -- it
201      * enqueues the request to pop, but the action will not be performed until the
202      * application returns to its event loop.
203      */
popBackStack()204     public abstract void popBackStack();
205 
206     /**
207      * Like {@link #popBackStack()}, but performs the operation immediately
208      * inside of the call.  This is like calling {@link #executePendingTransactions()}
209      * afterwards.
210      * @return Returns true if there was something popped, else false.
211      */
popBackStackImmediate()212     public abstract boolean popBackStackImmediate();
213 
214     /**
215      * Pop the last fragment transition from the manager's fragment
216      * back stack.  If there is nothing to pop, false is returned.
217      * This function is asynchronous -- it enqueues the
218      * request to pop, but the action will not be performed until the application
219      * returns to its event loop.
220      *
221      * @param name If non-null, this is the name of a previous back state
222      * to look for; if found, all states up to that state will be popped.  The
223      * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
224      * the named state itself is popped. If null, only the top state is popped.
225      * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
226      */
popBackStack(String name, int flags)227     public abstract void popBackStack(String name, int flags);
228 
229     /**
230      * Like {@link #popBackStack(String, int)}, but performs the operation immediately
231      * inside of the call.  This is like calling {@link #executePendingTransactions()}
232      * afterwards.
233      * @return Returns true if there was something popped, else false.
234      */
popBackStackImmediate(String name, int flags)235     public abstract boolean popBackStackImmediate(String name, int flags);
236 
237     /**
238      * Pop all back stack states up to the one with the given identifier.
239      * This function is asynchronous -- it enqueues the
240      * request to pop, but the action will not be performed until the application
241      * returns to its event loop.
242      *
243      * @param id Identifier of the stated to be popped. If no identifier exists,
244      * false is returned.
245      * The identifier is the number returned by
246      * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
247      * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
248      * the named state itself is popped.
249      * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
250      */
popBackStack(int id, int flags)251     public abstract void popBackStack(int id, int flags);
252 
253     /**
254      * Like {@link #popBackStack(int, int)}, but performs the operation immediately
255      * inside of the call.  This is like calling {@link #executePendingTransactions()}
256      * afterwards.
257      * @return Returns true if there was something popped, else false.
258      */
popBackStackImmediate(int id, int flags)259     public abstract boolean popBackStackImmediate(int id, int flags);
260 
261     /**
262      * Return the number of entries currently in the back stack.
263      */
getBackStackEntryCount()264     public abstract int getBackStackEntryCount();
265 
266     /**
267      * Return the BackStackEntry at index <var>index</var> in the back stack;
268      * where the item on the bottom of the stack has index 0.
269      */
getBackStackEntryAt(int index)270     public abstract BackStackEntry getBackStackEntryAt(int index);
271 
272     /**
273      * Add a new listener for changes to the fragment back stack.
274      */
addOnBackStackChangedListener(OnBackStackChangedListener listener)275     public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
276 
277     /**
278      * Remove a listener that was previously added with
279      * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
280      */
removeOnBackStackChangedListener(OnBackStackChangedListener listener)281     public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
282 
283     /**
284      * Put a reference to a fragment in a Bundle.  This Bundle can be
285      * persisted as saved state, and when later restoring
286      * {@link #getFragment(Bundle, String)} will return the current
287      * instance of the same fragment.
288      *
289      * @param bundle The bundle in which to put the fragment reference.
290      * @param key The name of the entry in the bundle.
291      * @param fragment The Fragment whose reference is to be stored.
292      */
putFragment(Bundle bundle, String key, Fragment fragment)293     public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
294 
295     /**
296      * Retrieve the current Fragment instance for a reference previously
297      * placed with {@link #putFragment(Bundle, String, Fragment)}.
298      *
299      * @param bundle The bundle from which to retrieve the fragment reference.
300      * @param key The name of the entry in the bundle.
301      * @return Returns the current Fragment instance that is associated with
302      * the given reference.
303      */
getFragment(Bundle bundle, String key)304     public abstract Fragment getFragment(Bundle bundle, String key);
305 
306     /**
307      * Save the current instance state of the given Fragment.  This can be
308      * used later when creating a new instance of the Fragment and adding
309      * it to the fragment manager, to have it create itself to match the
310      * current state returned here.  Note that there are limits on how
311      * this can be used:
312      *
313      * <ul>
314      * <li>The Fragment must currently be attached to the FragmentManager.
315      * <li>A new Fragment created using this saved state must be the same class
316      * type as the Fragment it was created from.
317      * <li>The saved state can not contain dependencies on other fragments --
318      * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to
319      * store a fragment reference because that reference may not be valid when
320      * this saved state is later used.  Likewise the Fragment's target and
321      * result code are not included in this state.
322      * </ul>
323      *
324      * @param f The Fragment whose state is to be saved.
325      * @return The generated state.  This will be null if there was no
326      * interesting state created by the fragment.
327      */
saveFragmentInstanceState(Fragment f)328     public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
329 
330     /**
331      * Returns true if the final {@link Activity#onDestroy() Activity.onDestroy()}
332      * call has been made on the FragmentManager's Activity, so this instance is now dead.
333      */
isDestroyed()334     public abstract boolean isDestroyed();
335 
336     /**
337      * Print the FragmentManager's state into the given stream.
338      *
339      * @param prefix Text to print at the front of each line.
340      * @param fd The raw file descriptor that the dump is being sent to.
341      * @param writer A PrintWriter to which the dump is to be set.
342      * @param args Additional arguments to the dump request.
343      */
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)344     public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
345 
346     /**
347      * Control whether the framework's internal fragment manager debugging
348      * logs are turned on.  If enabled, you will see output in logcat as
349      * the framework performs fragment operations.
350      */
enableDebugLogging(boolean enabled)351     public static void enableDebugLogging(boolean enabled) {
352         FragmentManagerImpl.DEBUG = enabled;
353     }
354 
355     /**
356      * Invalidate the attached activity's options menu as necessary.
357      * This may end up being deferred until we move to the resumed state.
358      */
invalidateOptionsMenu()359     public void invalidateOptionsMenu() { }
360 }
361 
362 final class FragmentManagerState implements Parcelable {
363     FragmentState[] mActive;
364     int[] mAdded;
365     BackStackState[] mBackStack;
366 
FragmentManagerState()367     public FragmentManagerState() {
368     }
369 
FragmentManagerState(Parcel in)370     public FragmentManagerState(Parcel in) {
371         mActive = in.createTypedArray(FragmentState.CREATOR);
372         mAdded = in.createIntArray();
373         mBackStack = in.createTypedArray(BackStackState.CREATOR);
374     }
375 
describeContents()376     public int describeContents() {
377         return 0;
378     }
379 
writeToParcel(Parcel dest, int flags)380     public void writeToParcel(Parcel dest, int flags) {
381         dest.writeTypedArray(mActive, flags);
382         dest.writeIntArray(mAdded);
383         dest.writeTypedArray(mBackStack, flags);
384     }
385 
386     public static final Parcelable.Creator<FragmentManagerState> CREATOR
387             = new Parcelable.Creator<FragmentManagerState>() {
388         public FragmentManagerState createFromParcel(Parcel in) {
389             return new FragmentManagerState(in);
390         }
391 
392         public FragmentManagerState[] newArray(int size) {
393             return new FragmentManagerState[size];
394         }
395     };
396 }
397 
398 /**
399  * Container for fragments associated with an activity.
400  */
401 final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
402     static boolean DEBUG = false;
403     static final String TAG = "FragmentManager";
404 
405     static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
406     static final String TARGET_STATE_TAG = "android:target_state";
407     static final String VIEW_STATE_TAG = "android:view_state";
408     static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
409 
410     static class AnimateOnHWLayerIfNeededListener implements Animator.AnimatorListener {
411         private boolean mShouldRunOnHWLayer = false;
412         private View mView;
AnimateOnHWLayerIfNeededListener(final View v)413         public AnimateOnHWLayerIfNeededListener(final View v) {
414             if (v == null) {
415                 return;
416             }
417             mView = v;
418         }
419 
420         @Override
onAnimationStart(Animator animation)421         public void onAnimationStart(Animator animation) {
422             mShouldRunOnHWLayer = shouldRunOnHWLayer(mView, animation);
423             if (mShouldRunOnHWLayer) {
424                 mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
425             }
426         }
427 
428         @Override
onAnimationEnd(Animator animation)429         public void onAnimationEnd(Animator animation) {
430             if (mShouldRunOnHWLayer) {
431                 mView.setLayerType(View.LAYER_TYPE_NONE, null);
432             }
433             mView = null;
434             animation.removeListener(this);
435         }
436 
437         @Override
onAnimationCancel(Animator animation)438         public void onAnimationCancel(Animator animation) {
439 
440         }
441 
442         @Override
onAnimationRepeat(Animator animation)443         public void onAnimationRepeat(Animator animation) {
444 
445         }
446     }
447 
448     ArrayList<Runnable> mPendingActions;
449     Runnable[] mTmpActions;
450     boolean mExecutingActions;
451 
452     ArrayList<Fragment> mActive;
453     ArrayList<Fragment> mAdded;
454     ArrayList<Integer> mAvailIndices;
455     ArrayList<BackStackRecord> mBackStack;
456     ArrayList<Fragment> mCreatedMenus;
457 
458     // Must be accessed while locked.
459     ArrayList<BackStackRecord> mBackStackIndices;
460     ArrayList<Integer> mAvailBackStackIndices;
461 
462     ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
463 
464     int mCurState = Fragment.INITIALIZING;
465     FragmentHostCallback<?> mHost;
466     FragmentController mController;
467     FragmentContainer mContainer;
468     Fragment mParent;
469 
470     boolean mNeedMenuInvalidate;
471     boolean mStateSaved;
472     boolean mDestroyed;
473     String mNoTransactionsBecause;
474     boolean mHavePendingDeferredStart;
475 
476     // Temporary vars for state save and restore.
477     Bundle mStateBundle = null;
478     SparseArray<Parcelable> mStateArray = null;
479 
480     Runnable mExecCommit = new Runnable() {
481         @Override
482         public void run() {
483             execPendingActions();
484         }
485     };
486 
throwException(RuntimeException ex)487     private void throwException(RuntimeException ex) {
488         Log.e(TAG, ex.getMessage());
489         LogWriter logw = new LogWriter(Log.ERROR, TAG);
490         PrintWriter pw = new FastPrintWriter(logw, false, 1024);
491         if (mHost != null) {
492             Log.e(TAG, "Activity state:");
493             try {
494                 mHost.onDump("  ", null, pw, new String[] { });
495             } catch (Exception e) {
496                 pw.flush();
497                 Log.e(TAG, "Failed dumping state", e);
498             }
499         } else {
500             Log.e(TAG, "Fragment manager state:");
501             try {
502                 dump("  ", null, pw, new String[] { });
503             } catch (Exception e) {
504                 pw.flush();
505                 Log.e(TAG, "Failed dumping state", e);
506             }
507         }
508         pw.flush();
509         throw ex;
510     }
511 
modifiesAlpha(Animator anim)512     static boolean modifiesAlpha(Animator anim) {
513         if (anim == null) {
514             return false;
515         }
516         if (anim instanceof ValueAnimator) {
517             ValueAnimator valueAnim = (ValueAnimator) anim;
518             PropertyValuesHolder[] values = valueAnim.getValues();
519             for (int i = 0; i < values.length; i++) {
520                 if (("alpha").equals(values[i].getPropertyName())) {
521                     return true;
522                 }
523             }
524         } else if (anim instanceof AnimatorSet) {
525             List<Animator> animList = ((AnimatorSet) anim).getChildAnimations();
526             for (int i = 0; i < animList.size(); i++) {
527                 if (modifiesAlpha(animList.get(i))) {
528                     return true;
529                 }
530             }
531         }
532         return false;
533     }
534 
shouldRunOnHWLayer(View v, Animator anim)535     static boolean shouldRunOnHWLayer(View v, Animator anim) {
536         if (v == null || anim == null) {
537             return false;
538         }
539         return v.getLayerType() == View.LAYER_TYPE_NONE
540                 && v.hasOverlappingRendering()
541                 && modifiesAlpha(anim);
542     }
543 
544     /**
545      * Sets the to be animated view on hardware layer during the animation.
546      */
setHWLayerAnimListenerIfAlpha(final View v, Animator anim)547     private void setHWLayerAnimListenerIfAlpha(final View v, Animator anim) {
548         if (v == null || anim == null) {
549             return;
550         }
551         if (shouldRunOnHWLayer(v, anim)) {
552             anim.addListener(new AnimateOnHWLayerIfNeededListener(v));
553         }
554     }
555 
556     @Override
beginTransaction()557     public FragmentTransaction beginTransaction() {
558         return new BackStackRecord(this);
559     }
560 
561     @Override
executePendingTransactions()562     public boolean executePendingTransactions() {
563         return execPendingActions();
564     }
565 
566     @Override
popBackStack()567     public void popBackStack() {
568         enqueueAction(new Runnable() {
569             @Override public void run() {
570                 popBackStackState(mHost.getHandler(), null, -1, 0);
571             }
572         }, false);
573     }
574 
575     @Override
popBackStackImmediate()576     public boolean popBackStackImmediate() {
577         checkStateLoss();
578         executePendingTransactions();
579         return popBackStackState(mHost.getHandler(), null, -1, 0);
580     }
581 
582     @Override
popBackStack(final String name, final int flags)583     public void popBackStack(final String name, final int flags) {
584         enqueueAction(new Runnable() {
585             @Override public void run() {
586                 popBackStackState(mHost.getHandler(), name, -1, flags);
587             }
588         }, false);
589     }
590 
591     @Override
popBackStackImmediate(String name, int flags)592     public boolean popBackStackImmediate(String name, int flags) {
593         checkStateLoss();
594         executePendingTransactions();
595         return popBackStackState(mHost.getHandler(), name, -1, flags);
596     }
597 
598     @Override
popBackStack(final int id, final int flags)599     public void popBackStack(final int id, final int flags) {
600         if (id < 0) {
601             throw new IllegalArgumentException("Bad id: " + id);
602         }
603         enqueueAction(new Runnable() {
604             @Override public void run() {
605                 popBackStackState(mHost.getHandler(), null, id, flags);
606             }
607         }, false);
608     }
609 
610     @Override
popBackStackImmediate(int id, int flags)611     public boolean popBackStackImmediate(int id, int flags) {
612         checkStateLoss();
613         executePendingTransactions();
614         if (id < 0) {
615             throw new IllegalArgumentException("Bad id: " + id);
616         }
617         return popBackStackState(mHost.getHandler(), null, id, flags);
618     }
619 
620     @Override
getBackStackEntryCount()621     public int getBackStackEntryCount() {
622         return mBackStack != null ? mBackStack.size() : 0;
623     }
624 
625     @Override
getBackStackEntryAt(int index)626     public BackStackEntry getBackStackEntryAt(int index) {
627         return mBackStack.get(index);
628     }
629 
630     @Override
addOnBackStackChangedListener(OnBackStackChangedListener listener)631     public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
632         if (mBackStackChangeListeners == null) {
633             mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
634         }
635         mBackStackChangeListeners.add(listener);
636     }
637 
638     @Override
removeOnBackStackChangedListener(OnBackStackChangedListener listener)639     public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
640         if (mBackStackChangeListeners != null) {
641             mBackStackChangeListeners.remove(listener);
642         }
643     }
644 
645     @Override
putFragment(Bundle bundle, String key, Fragment fragment)646     public void putFragment(Bundle bundle, String key, Fragment fragment) {
647         if (fragment.mIndex < 0) {
648             throwException(new IllegalStateException("Fragment " + fragment
649                     + " is not currently in the FragmentManager"));
650         }
651         bundle.putInt(key, fragment.mIndex);
652     }
653 
654     @Override
getFragment(Bundle bundle, String key)655     public Fragment getFragment(Bundle bundle, String key) {
656         int index = bundle.getInt(key, -1);
657         if (index == -1) {
658             return null;
659         }
660         if (index >= mActive.size()) {
661             throwException(new IllegalStateException("Fragment no longer exists for key "
662                     + key + ": index " + index));
663         }
664         Fragment f = mActive.get(index);
665         if (f == null) {
666             throwException(new IllegalStateException("Fragment no longer exists for key "
667                     + key + ": index " + index));
668         }
669         return f;
670     }
671 
672     @Override
saveFragmentInstanceState(Fragment fragment)673     public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) {
674         if (fragment.mIndex < 0) {
675             throwException(new IllegalStateException("Fragment " + fragment
676                     + " is not currently in the FragmentManager"));
677         }
678         if (fragment.mState > Fragment.INITIALIZING) {
679             Bundle result = saveFragmentBasicState(fragment);
680             return result != null ? new Fragment.SavedState(result) : null;
681         }
682         return null;
683     }
684 
685     @Override
isDestroyed()686     public boolean isDestroyed() {
687         return mDestroyed;
688     }
689 
690     @Override
toString()691     public String toString() {
692         StringBuilder sb = new StringBuilder(128);
693         sb.append("FragmentManager{");
694         sb.append(Integer.toHexString(System.identityHashCode(this)));
695         sb.append(" in ");
696         if (mParent != null) {
697             DebugUtils.buildShortClassTag(mParent, sb);
698         } else {
699             DebugUtils.buildShortClassTag(mHost, sb);
700         }
701         sb.append("}}");
702         return sb.toString();
703     }
704 
705     @Override
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)706     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
707         String innerPrefix = prefix + "    ";
708 
709         int N;
710         if (mActive != null) {
711             N = mActive.size();
712             if (N > 0) {
713                 writer.print(prefix); writer.print("Active Fragments in ");
714                         writer.print(Integer.toHexString(System.identityHashCode(this)));
715                         writer.println(":");
716                 for (int i=0; i<N; i++) {
717                     Fragment f = mActive.get(i);
718                     writer.print(prefix); writer.print("  #"); writer.print(i);
719                             writer.print(": "); writer.println(f);
720                     if (f != null) {
721                         f.dump(innerPrefix, fd, writer, args);
722                     }
723                 }
724             }
725         }
726 
727         if (mAdded != null) {
728             N = mAdded.size();
729             if (N > 0) {
730                 writer.print(prefix); writer.println("Added Fragments:");
731                 for (int i=0; i<N; i++) {
732                     Fragment f = mAdded.get(i);
733                     writer.print(prefix); writer.print("  #"); writer.print(i);
734                             writer.print(": "); writer.println(f.toString());
735                 }
736             }
737         }
738 
739         if (mCreatedMenus != null) {
740             N = mCreatedMenus.size();
741             if (N > 0) {
742                 writer.print(prefix); writer.println("Fragments Created Menus:");
743                 for (int i=0; i<N; i++) {
744                     Fragment f = mCreatedMenus.get(i);
745                     writer.print(prefix); writer.print("  #"); writer.print(i);
746                             writer.print(": "); writer.println(f.toString());
747                 }
748             }
749         }
750 
751         if (mBackStack != null) {
752             N = mBackStack.size();
753             if (N > 0) {
754                 writer.print(prefix); writer.println("Back Stack:");
755                 for (int i=0; i<N; i++) {
756                     BackStackRecord bs = mBackStack.get(i);
757                     writer.print(prefix); writer.print("  #"); writer.print(i);
758                             writer.print(": "); writer.println(bs.toString());
759                     bs.dump(innerPrefix, fd, writer, args);
760                 }
761             }
762         }
763 
764         synchronized (this) {
765             if (mBackStackIndices != null) {
766                 N = mBackStackIndices.size();
767                 if (N > 0) {
768                     writer.print(prefix); writer.println("Back Stack Indices:");
769                     for (int i=0; i<N; i++) {
770                         BackStackRecord bs = mBackStackIndices.get(i);
771                         writer.print(prefix); writer.print("  #"); writer.print(i);
772                                 writer.print(": "); writer.println(bs);
773                     }
774                 }
775             }
776 
777             if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) {
778                 writer.print(prefix); writer.print("mAvailBackStackIndices: ");
779                         writer.println(Arrays.toString(mAvailBackStackIndices.toArray()));
780             }
781         }
782 
783         if (mPendingActions != null) {
784             N = mPendingActions.size();
785             if (N > 0) {
786                 writer.print(prefix); writer.println("Pending Actions:");
787                 for (int i=0; i<N; i++) {
788                     Runnable r = mPendingActions.get(i);
789                     writer.print(prefix); writer.print("  #"); writer.print(i);
790                             writer.print(": "); writer.println(r);
791                 }
792             }
793         }
794 
795         writer.print(prefix); writer.println("FragmentManager misc state:");
796         writer.print(prefix); writer.print("  mHost="); writer.println(mHost);
797         writer.print(prefix); writer.print("  mContainer="); writer.println(mContainer);
798         if (mParent != null) {
799             writer.print(prefix); writer.print("  mParent="); writer.println(mParent);
800         }
801         writer.print(prefix); writer.print("  mCurState="); writer.print(mCurState);
802                 writer.print(" mStateSaved="); writer.print(mStateSaved);
803                 writer.print(" mDestroyed="); writer.println(mDestroyed);
804         if (mNeedMenuInvalidate) {
805             writer.print(prefix); writer.print("  mNeedMenuInvalidate=");
806                     writer.println(mNeedMenuInvalidate);
807         }
808         if (mNoTransactionsBecause != null) {
809             writer.print(prefix); writer.print("  mNoTransactionsBecause=");
810                     writer.println(mNoTransactionsBecause);
811         }
812         if (mAvailIndices != null && mAvailIndices.size() > 0) {
813             writer.print(prefix); writer.print("  mAvailIndices: ");
814                     writer.println(Arrays.toString(mAvailIndices.toArray()));
815         }
816     }
817 
loadAnimator(Fragment fragment, int transit, boolean enter, int transitionStyle)818     Animator loadAnimator(Fragment fragment, int transit, boolean enter,
819             int transitionStyle) {
820         Animator animObj = fragment.onCreateAnimator(transit, enter,
821                 fragment.mNextAnim);
822         if (animObj != null) {
823             return animObj;
824         }
825 
826         if (fragment.mNextAnim != 0) {
827             Animator anim = AnimatorInflater.loadAnimator(mHost.getContext(), fragment.mNextAnim);
828             if (anim != null) {
829                 return anim;
830             }
831         }
832 
833         if (transit == 0) {
834             return null;
835         }
836 
837         int styleIndex = transitToStyleIndex(transit, enter);
838         if (styleIndex < 0) {
839             return null;
840         }
841 
842         if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
843             transitionStyle = mHost.onGetWindowAnimations();
844         }
845         if (transitionStyle == 0) {
846             return null;
847         }
848 
849         TypedArray attrs = mHost.getContext().obtainStyledAttributes(transitionStyle,
850                 com.android.internal.R.styleable.FragmentAnimation);
851         int anim = attrs.getResourceId(styleIndex, 0);
852         attrs.recycle();
853 
854         if (anim == 0) {
855             return null;
856         }
857 
858         return AnimatorInflater.loadAnimator(mHost.getContext(), anim);
859     }
860 
performPendingDeferredStart(Fragment f)861     public void performPendingDeferredStart(Fragment f) {
862         if (f.mDeferStart) {
863             if (mExecutingActions) {
864                 // Wait until we're done executing our pending transactions
865                 mHavePendingDeferredStart = true;
866                 return;
867             }
868             f.mDeferStart = false;
869             moveToState(f, mCurState, 0, 0, false);
870         }
871     }
872 
isStateAtLeast(int state)873     boolean isStateAtLeast(int state) {
874         return mCurState >= state;
875     }
876 
moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive)877     void moveToState(Fragment f, int newState, int transit, int transitionStyle,
878             boolean keepActive) {
879         if (DEBUG && false) Log.v(TAG, "moveToState: " + f
880             + " oldState=" + f.mState + " newState=" + newState
881             + " mRemoving=" + f.mRemoving + " Callers=" + Debug.getCallers(5));
882 
883         // Fragments that are not currently added will sit in the onCreate() state.
884         if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
885             newState = Fragment.CREATED;
886         }
887         if (f.mRemoving && newState > f.mState) {
888             // While removing a fragment, we can't change it to a higher state.
889             newState = f.mState;
890         }
891         // Defer start if requested; don't allow it to move to STARTED or higher
892         // if it's not already started.
893         if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
894             newState = Fragment.STOPPED;
895         }
896         if (f.mState < newState) {
897             // For fragments that are created from a layout, when restoring from
898             // state we don't want to allow them to be created until they are
899             // being reloaded from the layout.
900             if (f.mFromLayout && !f.mInLayout) {
901                 return;
902             }
903             if (f.mAnimatingAway != null) {
904                 // The fragment is currently being animated...  but!  Now we
905                 // want to move our state back up.  Give up on waiting for the
906                 // animation, move to whatever the final state should be once
907                 // the animation is done, and then we can proceed from there.
908                 f.mAnimatingAway = null;
909                 moveToState(f, f.mStateAfterAnimating, 0, 0, true);
910             }
911             switch (f.mState) {
912                 case Fragment.INITIALIZING:
913                     if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
914                     if (f.mSavedFragmentState != null) {
915                         f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
916                                 FragmentManagerImpl.VIEW_STATE_TAG);
917                         f.mTarget = getFragment(f.mSavedFragmentState,
918                                 FragmentManagerImpl.TARGET_STATE_TAG);
919                         if (f.mTarget != null) {
920                             f.mTargetRequestCode = f.mSavedFragmentState.getInt(
921                                     FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
922                         }
923                         f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
924                                 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
925                         if (!f.mUserVisibleHint) {
926                             f.mDeferStart = true;
927                             if (newState > Fragment.STOPPED) {
928                                 newState = Fragment.STOPPED;
929                             }
930                         }
931                     }
932                     f.mHost = mHost;
933                     f.mParentFragment = mParent;
934                     f.mFragmentManager = mParent != null
935                             ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
936                     f.mCalled = false;
937                     f.onAttach(mHost.getContext());
938                     if (!f.mCalled) {
939                         throw new SuperNotCalledException("Fragment " + f
940                                 + " did not call through to super.onAttach()");
941                     }
942                     if (f.mParentFragment == null) {
943                         mHost.onAttachFragment(f);
944                     } else {
945                         f.mParentFragment.onAttachFragment(f);
946                     }
947 
948                     if (!f.mRetaining) {
949                         f.performCreate(f.mSavedFragmentState);
950                     } else {
951                         f.restoreChildFragmentState(f.mSavedFragmentState, true);
952                         f.mState = Fragment.CREATED;
953                     }
954                     f.mRetaining = false;
955                     if (f.mFromLayout) {
956                         // For fragments that are part of the content view
957                         // layout, we need to instantiate the view immediately
958                         // and the inflater will take care of adding it.
959                         f.mView = f.performCreateView(f.getLayoutInflater(
960                                 f.mSavedFragmentState), null, f.mSavedFragmentState);
961                         if (f.mView != null) {
962                             f.mView.setSaveFromParentEnabled(false);
963                             if (f.mHidden) f.mView.setVisibility(View.GONE);
964                             f.onViewCreated(f.mView, f.mSavedFragmentState);
965                         }
966                     }
967                 case Fragment.CREATED:
968                     if (newState > Fragment.CREATED) {
969                         if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
970                         if (!f.mFromLayout) {
971                             ViewGroup container = null;
972                             if (f.mContainerId != 0) {
973                                 if (f.mContainerId == View.NO_ID) {
974                                     throwException(new IllegalArgumentException(
975                                             "Cannot create fragment "
976                                                     + f
977                                                     + " for a container view with no id"));
978                                 }
979                                 container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
980                                 if (container == null && !f.mRestored) {
981                                     String resName;
982                                     try {
983                                         resName = f.getResources().getResourceName(f.mContainerId);
984                                     } catch (NotFoundException e) {
985                                         resName = "unknown";
986                                     }
987                                     throwException(new IllegalArgumentException(
988                                             "No view found for id 0x"
989                                             + Integer.toHexString(f.mContainerId) + " ("
990                                             + resName
991                                             + ") for fragment " + f));
992                                 }
993                             }
994                             f.mContainer = container;
995                             f.mView = f.performCreateView(f.getLayoutInflater(
996                                     f.mSavedFragmentState), container, f.mSavedFragmentState);
997                             if (f.mView != null) {
998                                 f.mView.setSaveFromParentEnabled(false);
999                                 if (container != null) {
1000                                     Animator anim = loadAnimator(f, transit, true,
1001                                             transitionStyle);
1002                                     if (anim != null) {
1003                                         anim.setTarget(f.mView);
1004                                         setHWLayerAnimListenerIfAlpha(f.mView, anim);
1005                                         anim.start();
1006                                     }
1007                                     container.addView(f.mView);
1008                                 }
1009                                 if (f.mHidden) f.mView.setVisibility(View.GONE);
1010                                 f.onViewCreated(f.mView, f.mSavedFragmentState);
1011                             }
1012                         }
1013 
1014                         f.performActivityCreated(f.mSavedFragmentState);
1015                         if (f.mView != null) {
1016                             f.restoreViewState(f.mSavedFragmentState);
1017                         }
1018                         f.mSavedFragmentState = null;
1019                     }
1020                 case Fragment.ACTIVITY_CREATED:
1021                     if (newState > Fragment.ACTIVITY_CREATED) {
1022                         f.mState = Fragment.STOPPED;
1023                     }
1024                 case Fragment.STOPPED:
1025                     if (newState > Fragment.STOPPED) {
1026                         if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
1027                         f.performStart();
1028                     }
1029                 case Fragment.STARTED:
1030                     if (newState > Fragment.STARTED) {
1031                         if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
1032                         f.performResume();
1033                         // Get rid of this in case we saved it and never needed it.
1034                         f.mSavedFragmentState = null;
1035                         f.mSavedViewState = null;
1036                     }
1037             }
1038         } else if (f.mState > newState) {
1039             switch (f.mState) {
1040                 case Fragment.RESUMED:
1041                     if (newState < Fragment.RESUMED) {
1042                         if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
1043                         f.performPause();
1044                     }
1045                 case Fragment.STARTED:
1046                     if (newState < Fragment.STARTED) {
1047                         if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
1048                         f.performStop();
1049                     }
1050                 case Fragment.STOPPED:
1051                 case Fragment.ACTIVITY_CREATED:
1052                     if (newState < Fragment.ACTIVITY_CREATED) {
1053                         if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
1054                         if (f.mView != null) {
1055                             // Need to save the current view state if not
1056                             // done already.
1057                             if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
1058                                 saveFragmentViewState(f);
1059                             }
1060                         }
1061                         f.performDestroyView();
1062                         if (f.mView != null && f.mContainer != null) {
1063                             Animator anim = null;
1064                             if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
1065                                 anim = loadAnimator(f, transit, false,
1066                                         transitionStyle);
1067                             }
1068                             if (anim != null) {
1069                                 final ViewGroup container = f.mContainer;
1070                                 final View view = f.mView;
1071                                 final Fragment fragment = f;
1072                                 container.startViewTransition(view);
1073                                 f.mAnimatingAway = anim;
1074                                 f.mStateAfterAnimating = newState;
1075                                 anim.addListener(new AnimatorListenerAdapter() {
1076                                     @Override
1077                                     public void onAnimationEnd(Animator anim) {
1078                                         container.endViewTransition(view);
1079                                         if (fragment.mAnimatingAway != null) {
1080                                             fragment.mAnimatingAway = null;
1081                                             moveToState(fragment, fragment.mStateAfterAnimating,
1082                                                     0, 0, false);
1083                                         }
1084                                     }
1085                                 });
1086                                 anim.setTarget(f.mView);
1087                                 setHWLayerAnimListenerIfAlpha(f.mView, anim);
1088                                 anim.start();
1089 
1090                             }
1091                             f.mContainer.removeView(f.mView);
1092                         }
1093                         f.mContainer = null;
1094                         f.mView = null;
1095                     }
1096                 case Fragment.CREATED:
1097                     if (newState < Fragment.CREATED) {
1098                         if (mDestroyed) {
1099                             if (f.mAnimatingAway != null) {
1100                                 // The fragment's containing activity is
1101                                 // being destroyed, but this fragment is
1102                                 // currently animating away.  Stop the
1103                                 // animation right now -- it is not needed,
1104                                 // and we can't wait any more on destroying
1105                                 // the fragment.
1106                                 Animator anim = f.mAnimatingAway;
1107                                 f.mAnimatingAway = null;
1108                                 anim.cancel();
1109                             }
1110                         }
1111                         if (f.mAnimatingAway != null) {
1112                             // We are waiting for the fragment's view to finish
1113                             // animating away.  Just make a note of the state
1114                             // the fragment now should move to once the animation
1115                             // is done.
1116                             f.mStateAfterAnimating = newState;
1117                             newState = Fragment.CREATED;
1118                         } else {
1119                             if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
1120                             if (!f.mRetaining) {
1121                                 f.performDestroy();
1122                             } else {
1123                                 f.mState = Fragment.INITIALIZING;
1124                             }
1125 
1126                             f.performDetach();
1127                             if (!keepActive) {
1128                                 if (!f.mRetaining) {
1129                                     makeInactive(f);
1130                                 } else {
1131                                     f.mHost = null;
1132                                     f.mParentFragment = null;
1133                                     f.mFragmentManager = null;
1134                                 }
1135                             }
1136                         }
1137                     }
1138             }
1139         }
1140 
1141         if (f.mState != newState) {
1142             Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
1143                     + "expected state " + newState + " found " + f.mState);
1144             f.mState = newState;
1145         }
1146     }
1147 
moveToState(Fragment f)1148     void moveToState(Fragment f) {
1149         moveToState(f, mCurState, 0, 0, false);
1150     }
1151 
moveToState(int newState, boolean always)1152     void moveToState(int newState, boolean always) {
1153         moveToState(newState, 0, 0, always);
1154     }
1155 
moveToState(int newState, int transit, int transitStyle, boolean always)1156     void moveToState(int newState, int transit, int transitStyle, boolean always) {
1157         if (mHost == null && newState != Fragment.INITIALIZING) {
1158             throw new IllegalStateException("No activity");
1159         }
1160 
1161         if (!always && mCurState == newState) {
1162             return;
1163         }
1164 
1165         mCurState = newState;
1166         if (mActive != null) {
1167             boolean loadersRunning = false;
1168             for (int i=0; i<mActive.size(); i++) {
1169                 Fragment f = mActive.get(i);
1170                 if (f != null) {
1171                     moveToState(f, newState, transit, transitStyle, false);
1172                     if (f.mLoaderManager != null) {
1173                         loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1174                     }
1175                 }
1176             }
1177 
1178             if (!loadersRunning) {
1179                 startPendingDeferredFragments();
1180             }
1181 
1182             if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
1183                 mHost.onInvalidateOptionsMenu();
1184                 mNeedMenuInvalidate = false;
1185             }
1186         }
1187     }
1188 
startPendingDeferredFragments()1189     void startPendingDeferredFragments() {
1190         if (mActive == null) return;
1191 
1192         for (int i=0; i<mActive.size(); i++) {
1193             Fragment f = mActive.get(i);
1194             if (f != null) {
1195                 performPendingDeferredStart(f);
1196             }
1197         }
1198     }
1199 
makeActive(Fragment f)1200     void makeActive(Fragment f) {
1201         if (f.mIndex >= 0) {
1202             return;
1203         }
1204 
1205         if (mAvailIndices == null || mAvailIndices.size() <= 0) {
1206             if (mActive == null) {
1207                 mActive = new ArrayList<Fragment>();
1208             }
1209             f.setIndex(mActive.size(), mParent);
1210             mActive.add(f);
1211 
1212         } else {
1213             f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
1214             mActive.set(f.mIndex, f);
1215         }
1216         if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
1217     }
1218 
makeInactive(Fragment f)1219     void makeInactive(Fragment f) {
1220         if (f.mIndex < 0) {
1221             return;
1222         }
1223 
1224         if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
1225         mActive.set(f.mIndex, null);
1226         if (mAvailIndices == null) {
1227             mAvailIndices = new ArrayList<Integer>();
1228         }
1229         mAvailIndices.add(f.mIndex);
1230         mHost.inactivateFragment(f.mWho);
1231         f.initState();
1232     }
1233 
addFragment(Fragment fragment, boolean moveToStateNow)1234     public void addFragment(Fragment fragment, boolean moveToStateNow) {
1235         if (mAdded == null) {
1236             mAdded = new ArrayList<Fragment>();
1237         }
1238         if (DEBUG) Log.v(TAG, "add: " + fragment);
1239         makeActive(fragment);
1240         if (!fragment.mDetached) {
1241             if (mAdded.contains(fragment)) {
1242                 throw new IllegalStateException("Fragment already added: " + fragment);
1243             }
1244             mAdded.add(fragment);
1245             fragment.mAdded = true;
1246             fragment.mRemoving = false;
1247             if (fragment.mHasMenu && fragment.mMenuVisible) {
1248                 mNeedMenuInvalidate = true;
1249             }
1250             if (moveToStateNow) {
1251                 moveToState(fragment);
1252             }
1253         }
1254     }
1255 
removeFragment(Fragment fragment, int transition, int transitionStyle)1256     public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
1257         if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
1258         final boolean inactive = !fragment.isInBackStack();
1259         if (!fragment.mDetached || inactive) {
1260             if (false) {
1261                 // Would be nice to catch a bad remove here, but we need
1262                 // time to test this to make sure we aren't crashes cases
1263                 // where it is not a problem.
1264                 if (!mAdded.contains(fragment)) {
1265                     throw new IllegalStateException("Fragment not added: " + fragment);
1266                 }
1267             }
1268             if (mAdded != null) {
1269                 mAdded.remove(fragment);
1270             }
1271             if (fragment.mHasMenu && fragment.mMenuVisible) {
1272                 mNeedMenuInvalidate = true;
1273             }
1274             fragment.mAdded = false;
1275             fragment.mRemoving = true;
1276             moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
1277                     transition, transitionStyle, false);
1278         }
1279     }
1280 
hideFragment(Fragment fragment, int transition, int transitionStyle)1281     public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
1282         if (DEBUG) Log.v(TAG, "hide: " + fragment);
1283         if (!fragment.mHidden) {
1284             fragment.mHidden = true;
1285             if (fragment.mView != null) {
1286                 Animator anim = loadAnimator(fragment, transition, false,
1287                         transitionStyle);
1288                 if (anim != null) {
1289                     anim.setTarget(fragment.mView);
1290                     // Delay the actual hide operation until the animation finishes, otherwise
1291                     // the fragment will just immediately disappear
1292                     final Fragment finalFragment = fragment;
1293                     anim.addListener(new AnimatorListenerAdapter() {
1294                         @Override
1295                         public void onAnimationEnd(Animator animation) {
1296                             if (finalFragment.mView != null) {
1297                                 finalFragment.mView.setVisibility(View.GONE);
1298                             }
1299                         }
1300                     });
1301                     setHWLayerAnimListenerIfAlpha(finalFragment.mView, anim);
1302                     anim.start();
1303                 } else {
1304                     fragment.mView.setVisibility(View.GONE);
1305                 }
1306             }
1307             if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
1308                 mNeedMenuInvalidate = true;
1309             }
1310             fragment.onHiddenChanged(true);
1311         }
1312     }
1313 
showFragment(Fragment fragment, int transition, int transitionStyle)1314     public void showFragment(Fragment fragment, int transition, int transitionStyle) {
1315         if (DEBUG) Log.v(TAG, "show: " + fragment);
1316         if (fragment.mHidden) {
1317             fragment.mHidden = false;
1318             if (fragment.mView != null) {
1319                 Animator anim = loadAnimator(fragment, transition, true,
1320                         transitionStyle);
1321                 if (anim != null) {
1322                     anim.setTarget(fragment.mView);
1323                     setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
1324                     anim.start();
1325                 }
1326                 fragment.mView.setVisibility(View.VISIBLE);
1327             }
1328             if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
1329                 mNeedMenuInvalidate = true;
1330             }
1331             fragment.onHiddenChanged(false);
1332         }
1333     }
1334 
detachFragment(Fragment fragment, int transition, int transitionStyle)1335     public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
1336         if (DEBUG) Log.v(TAG, "detach: " + fragment);
1337         if (!fragment.mDetached) {
1338             fragment.mDetached = true;
1339             if (fragment.mAdded) {
1340                 // We are not already in back stack, so need to remove the fragment.
1341                 if (mAdded != null) {
1342                     if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
1343                     mAdded.remove(fragment);
1344                 }
1345                 if (fragment.mHasMenu && fragment.mMenuVisible) {
1346                     mNeedMenuInvalidate = true;
1347                 }
1348                 fragment.mAdded = false;
1349                 moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
1350             }
1351         }
1352     }
1353 
attachFragment(Fragment fragment, int transition, int transitionStyle)1354     public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
1355         if (DEBUG) Log.v(TAG, "attach: " + fragment);
1356         if (fragment.mDetached) {
1357             fragment.mDetached = false;
1358             if (!fragment.mAdded) {
1359                 if (mAdded == null) {
1360                     mAdded = new ArrayList<Fragment>();
1361                 }
1362                 if (mAdded.contains(fragment)) {
1363                     throw new IllegalStateException("Fragment already added: " + fragment);
1364                 }
1365                 if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
1366                 mAdded.add(fragment);
1367                 fragment.mAdded = true;
1368                 if (fragment.mHasMenu && fragment.mMenuVisible) {
1369                     mNeedMenuInvalidate = true;
1370                 }
1371                 moveToState(fragment, mCurState, transition, transitionStyle, false);
1372             }
1373         }
1374     }
1375 
findFragmentById(int id)1376     public Fragment findFragmentById(int id) {
1377         if (mAdded != null) {
1378             // First look through added fragments.
1379             for (int i=mAdded.size()-1; i>=0; i--) {
1380                 Fragment f = mAdded.get(i);
1381                 if (f != null && f.mFragmentId == id) {
1382                     return f;
1383                 }
1384             }
1385         }
1386         if (mActive != null) {
1387             // Now for any known fragment.
1388             for (int i=mActive.size()-1; i>=0; i--) {
1389                 Fragment f = mActive.get(i);
1390                 if (f != null && f.mFragmentId == id) {
1391                     return f;
1392                 }
1393             }
1394         }
1395         return null;
1396     }
1397 
findFragmentByTag(String tag)1398     public Fragment findFragmentByTag(String tag) {
1399         if (mAdded != null && tag != null) {
1400             // First look through added fragments.
1401             for (int i=mAdded.size()-1; i>=0; i--) {
1402                 Fragment f = mAdded.get(i);
1403                 if (f != null && tag.equals(f.mTag)) {
1404                     return f;
1405                 }
1406             }
1407         }
1408         if (mActive != null && tag != null) {
1409             // Now for any known fragment.
1410             for (int i=mActive.size()-1; i>=0; i--) {
1411                 Fragment f = mActive.get(i);
1412                 if (f != null && tag.equals(f.mTag)) {
1413                     return f;
1414                 }
1415             }
1416         }
1417         return null;
1418     }
1419 
findFragmentByWho(String who)1420     public Fragment findFragmentByWho(String who) {
1421         if (mActive != null && who != null) {
1422             for (int i=mActive.size()-1; i>=0; i--) {
1423                 Fragment f = mActive.get(i);
1424                 if (f != null && (f=f.findFragmentByWho(who)) != null) {
1425                     return f;
1426                 }
1427             }
1428         }
1429         return null;
1430     }
1431 
checkStateLoss()1432     private void checkStateLoss() {
1433         if (mStateSaved) {
1434             throw new IllegalStateException(
1435                     "Can not perform this action after onSaveInstanceState");
1436         }
1437         if (mNoTransactionsBecause != null) {
1438             throw new IllegalStateException(
1439                     "Can not perform this action inside of " + mNoTransactionsBecause);
1440         }
1441     }
1442 
1443     /**
1444      * Adds an action to the queue of pending actions.
1445      *
1446      * @param action the action to add
1447      * @param allowStateLoss whether to allow loss of state information
1448      * @throws IllegalStateException if the activity has been destroyed
1449      */
enqueueAction(Runnable action, boolean allowStateLoss)1450     public void enqueueAction(Runnable action, boolean allowStateLoss) {
1451         if (!allowStateLoss) {
1452             checkStateLoss();
1453         }
1454         synchronized (this) {
1455             if (mDestroyed || mHost == null) {
1456                 throw new IllegalStateException("Activity has been destroyed");
1457             }
1458             if (mPendingActions == null) {
1459                 mPendingActions = new ArrayList<Runnable>();
1460             }
1461             mPendingActions.add(action);
1462             if (mPendingActions.size() == 1) {
1463                 mHost.getHandler().removeCallbacks(mExecCommit);
1464                 mHost.getHandler().post(mExecCommit);
1465             }
1466         }
1467     }
1468 
allocBackStackIndex(BackStackRecord bse)1469     public int allocBackStackIndex(BackStackRecord bse) {
1470         synchronized (this) {
1471             if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
1472                 if (mBackStackIndices == null) {
1473                     mBackStackIndices = new ArrayList<BackStackRecord>();
1474                 }
1475                 int index = mBackStackIndices.size();
1476                 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
1477                 mBackStackIndices.add(bse);
1478                 return index;
1479 
1480             } else {
1481                 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
1482                 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
1483                 mBackStackIndices.set(index, bse);
1484                 return index;
1485             }
1486         }
1487     }
1488 
setBackStackIndex(int index, BackStackRecord bse)1489     public void setBackStackIndex(int index, BackStackRecord bse) {
1490         synchronized (this) {
1491             if (mBackStackIndices == null) {
1492                 mBackStackIndices = new ArrayList<BackStackRecord>();
1493             }
1494             int N = mBackStackIndices.size();
1495             if (index < N) {
1496                 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
1497                 mBackStackIndices.set(index, bse);
1498             } else {
1499                 while (N < index) {
1500                     mBackStackIndices.add(null);
1501                     if (mAvailBackStackIndices == null) {
1502                         mAvailBackStackIndices = new ArrayList<Integer>();
1503                     }
1504                     if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
1505                     mAvailBackStackIndices.add(N);
1506                     N++;
1507                 }
1508                 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
1509                 mBackStackIndices.add(bse);
1510             }
1511         }
1512     }
1513 
freeBackStackIndex(int index)1514     public void freeBackStackIndex(int index) {
1515         synchronized (this) {
1516             mBackStackIndices.set(index, null);
1517             if (mAvailBackStackIndices == null) {
1518                 mAvailBackStackIndices = new ArrayList<Integer>();
1519             }
1520             if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
1521             mAvailBackStackIndices.add(index);
1522         }
1523     }
1524 
execSingleAction(Runnable action, boolean allowStateLoss)1525     public void execSingleAction(Runnable action, boolean allowStateLoss) {
1526         if (mExecutingActions) {
1527             throw new IllegalStateException("FragmentManager is already executing transactions");
1528         }
1529 
1530         if (Looper.myLooper() != mHost.getHandler().getLooper()) {
1531             throw new IllegalStateException("Must be called from main thread of fragment host");
1532         }
1533 
1534         if (!allowStateLoss) {
1535             checkStateLoss();
1536         }
1537 
1538         mExecutingActions = true;
1539         action.run();
1540         mExecutingActions = false;
1541 
1542         doPendingDeferredStart();
1543     }
1544 
1545     /**
1546      * Only call from main thread!
1547      */
execPendingActions()1548     public boolean execPendingActions() {
1549         if (mExecutingActions) {
1550             throw new IllegalStateException("Recursive entry to executePendingTransactions");
1551         }
1552 
1553         if (Looper.myLooper() != mHost.getHandler().getLooper()) {
1554             throw new IllegalStateException("Must be called from main thread of process");
1555         }
1556 
1557         boolean didSomething = false;
1558 
1559         while (true) {
1560             int numActions;
1561 
1562             synchronized (this) {
1563                 if (mPendingActions == null || mPendingActions.size() == 0) {
1564                     break;
1565                 }
1566 
1567                 numActions = mPendingActions.size();
1568                 if (mTmpActions == null || mTmpActions.length < numActions) {
1569                     mTmpActions = new Runnable[numActions];
1570                 }
1571                 mPendingActions.toArray(mTmpActions);
1572                 mPendingActions.clear();
1573                 mHost.getHandler().removeCallbacks(mExecCommit);
1574             }
1575 
1576             mExecutingActions = true;
1577             for (int i=0; i<numActions; i++) {
1578                 mTmpActions[i].run();
1579                 mTmpActions[i] = null;
1580             }
1581             mExecutingActions = false;
1582             didSomething = true;
1583         }
1584 
1585         doPendingDeferredStart();
1586 
1587         return didSomething;
1588     }
1589 
doPendingDeferredStart()1590     void doPendingDeferredStart() {
1591         if (mHavePendingDeferredStart) {
1592             boolean loadersRunning = false;
1593             for (int i=0; i<mActive.size(); i++) {
1594                 Fragment f = mActive.get(i);
1595                 if (f != null && f.mLoaderManager != null) {
1596                     loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1597                 }
1598             }
1599             if (!loadersRunning) {
1600                 mHavePendingDeferredStart = false;
1601                 startPendingDeferredFragments();
1602             }
1603         }
1604     }
1605 
reportBackStackChanged()1606     void reportBackStackChanged() {
1607         if (mBackStackChangeListeners != null) {
1608             for (int i=0; i<mBackStackChangeListeners.size(); i++) {
1609                 mBackStackChangeListeners.get(i).onBackStackChanged();
1610             }
1611         }
1612     }
1613 
addBackStackState(BackStackRecord state)1614     void addBackStackState(BackStackRecord state) {
1615         if (mBackStack == null) {
1616             mBackStack = new ArrayList<BackStackRecord>();
1617         }
1618         mBackStack.add(state);
1619         reportBackStackChanged();
1620     }
1621 
popBackStackState(Handler handler, String name, int id, int flags)1622     boolean popBackStackState(Handler handler, String name, int id, int flags) {
1623         if (mBackStack == null) {
1624             return false;
1625         }
1626         if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
1627             int last = mBackStack.size()-1;
1628             if (last < 0) {
1629                 return false;
1630             }
1631             final BackStackRecord bss = mBackStack.remove(last);
1632             SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
1633             SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
1634             if (mCurState >= Fragment.CREATED) {
1635                 bss.calculateBackFragments(firstOutFragments, lastInFragments);
1636             }
1637             bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
1638             reportBackStackChanged();
1639         } else {
1640             int index = -1;
1641             if (name != null || id >= 0) {
1642                 // If a name or ID is specified, look for that place in
1643                 // the stack.
1644                 index = mBackStack.size()-1;
1645                 while (index >= 0) {
1646                     BackStackRecord bss = mBackStack.get(index);
1647                     if (name != null && name.equals(bss.getName())) {
1648                         break;
1649                     }
1650                     if (id >= 0 && id == bss.mIndex) {
1651                         break;
1652                     }
1653                     index--;
1654                 }
1655                 if (index < 0) {
1656                     return false;
1657                 }
1658                 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
1659                     index--;
1660                     // Consume all following entries that match.
1661                     while (index >= 0) {
1662                         BackStackRecord bss = mBackStack.get(index);
1663                         if ((name != null && name.equals(bss.getName()))
1664                                 || (id >= 0 && id == bss.mIndex)) {
1665                             index--;
1666                             continue;
1667                         }
1668                         break;
1669                     }
1670                 }
1671             }
1672             if (index == mBackStack.size()-1) {
1673                 return false;
1674             }
1675             final ArrayList<BackStackRecord> states
1676                     = new ArrayList<BackStackRecord>();
1677             for (int i=mBackStack.size()-1; i>index; i--) {
1678                 states.add(mBackStack.remove(i));
1679             }
1680             final int LAST = states.size()-1;
1681             SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
1682             SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
1683             if (mCurState >= Fragment.CREATED) {
1684                 for (int i = 0; i <= LAST; i++) {
1685                     states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
1686                 }
1687             }
1688             BackStackRecord.TransitionState state = null;
1689             for (int i=0; i<=LAST; i++) {
1690                 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
1691                 state = states.get(i).popFromBackStack(i == LAST, state,
1692                         firstOutFragments, lastInFragments);
1693             }
1694             reportBackStackChanged();
1695         }
1696         return true;
1697     }
1698 
retainNonConfig()1699     FragmentManagerNonConfig retainNonConfig() {
1700         ArrayList<Fragment> fragments = null;
1701         ArrayList<FragmentManagerNonConfig> childFragments = null;
1702         if (mActive != null) {
1703             for (int i=0; i<mActive.size(); i++) {
1704                 Fragment f = mActive.get(i);
1705                 if (f != null) {
1706                     if (f.mRetainInstance) {
1707                         if (fragments == null) {
1708                             fragments = new ArrayList<>();
1709                         }
1710                         fragments.add(f);
1711                         f.mRetaining = true;
1712                         f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
1713                         if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
1714                     }
1715                     boolean addedChild = false;
1716                     if (f.mChildFragmentManager != null) {
1717                         FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
1718                         if (child != null) {
1719                             if (childFragments == null) {
1720                                 childFragments = new ArrayList<>();
1721                                 for (int j = 0; j < i; j++) {
1722                                     childFragments.add(null);
1723                                 }
1724                             }
1725                             childFragments.add(child);
1726                             addedChild = true;
1727                         }
1728                     }
1729                     if (childFragments != null && !addedChild) {
1730                         childFragments.add(null);
1731                     }
1732                 }
1733             }
1734         }
1735         if (fragments == null && childFragments == null) {
1736             return null;
1737         }
1738         return new FragmentManagerNonConfig(fragments, childFragments);
1739     }
1740 
saveFragmentViewState(Fragment f)1741     void saveFragmentViewState(Fragment f) {
1742         if (f.mView == null) {
1743             return;
1744         }
1745         if (mStateArray == null) {
1746             mStateArray = new SparseArray<Parcelable>();
1747         } else {
1748             mStateArray.clear();
1749         }
1750         f.mView.saveHierarchyState(mStateArray);
1751         if (mStateArray.size() > 0) {
1752             f.mSavedViewState = mStateArray;
1753             mStateArray = null;
1754         }
1755     }
1756 
saveFragmentBasicState(Fragment f)1757     Bundle saveFragmentBasicState(Fragment f) {
1758         Bundle result = null;
1759 
1760         if (mStateBundle == null) {
1761             mStateBundle = new Bundle();
1762         }
1763         f.performSaveInstanceState(mStateBundle);
1764         if (!mStateBundle.isEmpty()) {
1765             result = mStateBundle;
1766             mStateBundle = null;
1767         }
1768 
1769         if (f.mView != null) {
1770             saveFragmentViewState(f);
1771         }
1772         if (f.mSavedViewState != null) {
1773             if (result == null) {
1774                 result = new Bundle();
1775             }
1776             result.putSparseParcelableArray(
1777                     FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
1778         }
1779         if (!f.mUserVisibleHint) {
1780             if (result == null) {
1781                 result = new Bundle();
1782             }
1783             // Only add this if it's not the default value
1784             result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
1785         }
1786 
1787         return result;
1788     }
1789 
saveAllState()1790     Parcelable saveAllState() {
1791         // Make sure all pending operations have now been executed to get
1792         // our state update-to-date.
1793         execPendingActions();
1794 
1795         mStateSaved = true;
1796 
1797         if (mActive == null || mActive.size() <= 0) {
1798             return null;
1799         }
1800 
1801         // First collect all active fragments.
1802         int N = mActive.size();
1803         FragmentState[] active = new FragmentState[N];
1804         boolean haveFragments = false;
1805         for (int i=0; i<N; i++) {
1806             Fragment f = mActive.get(i);
1807             if (f != null) {
1808                 if (f.mIndex < 0) {
1809                     throwException(new IllegalStateException(
1810                             "Failure saving state: active " + f
1811                             + " has cleared index: " + f.mIndex));
1812                 }
1813 
1814                 haveFragments = true;
1815 
1816                 FragmentState fs = new FragmentState(f);
1817                 active[i] = fs;
1818 
1819                 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
1820                     fs.mSavedFragmentState = saveFragmentBasicState(f);
1821 
1822                     if (f.mTarget != null) {
1823                         if (f.mTarget.mIndex < 0) {
1824                             throwException(new IllegalStateException(
1825                                     "Failure saving state: " + f
1826                                     + " has target not in fragment manager: " + f.mTarget));
1827                         }
1828                         if (fs.mSavedFragmentState == null) {
1829                             fs.mSavedFragmentState = new Bundle();
1830                         }
1831                         putFragment(fs.mSavedFragmentState,
1832                                 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
1833                         if (f.mTargetRequestCode != 0) {
1834                             fs.mSavedFragmentState.putInt(
1835                                     FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
1836                                     f.mTargetRequestCode);
1837                         }
1838                     }
1839 
1840                 } else {
1841                     fs.mSavedFragmentState = f.mSavedFragmentState;
1842                 }
1843 
1844                 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
1845                         + fs.mSavedFragmentState);
1846             }
1847         }
1848 
1849         if (!haveFragments) {
1850             if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
1851             return null;
1852         }
1853 
1854         int[] added = null;
1855         BackStackState[] backStack = null;
1856 
1857         // Build list of currently added fragments.
1858         if (mAdded != null) {
1859             N = mAdded.size();
1860             if (N > 0) {
1861                 added = new int[N];
1862                 for (int i=0; i<N; i++) {
1863                     added[i] = mAdded.get(i).mIndex;
1864                     if (added[i] < 0) {
1865                         throwException(new IllegalStateException(
1866                                 "Failure saving state: active " + mAdded.get(i)
1867                                 + " has cleared index: " + added[i]));
1868                     }
1869                     if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
1870                             + ": " + mAdded.get(i));
1871                 }
1872             }
1873         }
1874 
1875         // Now save back stack.
1876         if (mBackStack != null) {
1877             N = mBackStack.size();
1878             if (N > 0) {
1879                 backStack = new BackStackState[N];
1880                 for (int i=0; i<N; i++) {
1881                     backStack[i] = new BackStackState(this, mBackStack.get(i));
1882                     if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
1883                             + ": " + mBackStack.get(i));
1884                 }
1885             }
1886         }
1887 
1888         FragmentManagerState fms = new FragmentManagerState();
1889         fms.mActive = active;
1890         fms.mAdded = added;
1891         fms.mBackStack = backStack;
1892         return fms;
1893     }
1894 
restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig)1895     void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
1896         // If there is no saved state at all, then there can not be
1897         // any nonConfig fragments either, so that is that.
1898         if (state == null) return;
1899         FragmentManagerState fms = (FragmentManagerState)state;
1900         if (fms.mActive == null) return;
1901 
1902         List<FragmentManagerNonConfig> childNonConfigs = null;
1903 
1904         // First re-attach any non-config instances we are retaining back
1905         // to their saved state, so we don't try to instantiate them again.
1906         if (nonConfig != null) {
1907             List<Fragment> nonConfigFragments = nonConfig.getFragments();
1908             childNonConfigs = nonConfig.getChildNonConfigs();
1909             final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
1910             for (int i = 0; i < count; i++) {
1911                 Fragment f = nonConfigFragments.get(i);
1912                 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
1913                 FragmentState fs = fms.mActive[f.mIndex];
1914                 fs.mInstance = f;
1915                 f.mSavedViewState = null;
1916                 f.mBackStackNesting = 0;
1917                 f.mInLayout = false;
1918                 f.mAdded = false;
1919                 f.mTarget = null;
1920                 if (fs.mSavedFragmentState != null) {
1921                     fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
1922                     f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
1923                             FragmentManagerImpl.VIEW_STATE_TAG);
1924                     f.mSavedFragmentState = fs.mSavedFragmentState;
1925                 }
1926             }
1927         }
1928 
1929         // Build the full list of active fragments, instantiating them from
1930         // their saved state.
1931         mActive = new ArrayList<>(fms.mActive.length);
1932         if (mAvailIndices != null) {
1933             mAvailIndices.clear();
1934         }
1935         for (int i=0; i<fms.mActive.length; i++) {
1936             FragmentState fs = fms.mActive[i];
1937             if (fs != null) {
1938                 FragmentManagerNonConfig childNonConfig = null;
1939                 if (childNonConfigs != null && i < childNonConfigs.size()) {
1940                     childNonConfig = childNonConfigs.get(i);
1941                 }
1942                 Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
1943                 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
1944                 mActive.add(f);
1945                 // Now that the fragment is instantiated (or came from being
1946                 // retained above), clear mInstance in case we end up re-restoring
1947                 // from this FragmentState again.
1948                 fs.mInstance = null;
1949             } else {
1950                 mActive.add(null);
1951                 if (mAvailIndices == null) {
1952                     mAvailIndices = new ArrayList<>();
1953                 }
1954                 if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
1955                 mAvailIndices.add(i);
1956             }
1957         }
1958 
1959         // Update the target of all retained fragments.
1960         if (nonConfig != null) {
1961             List<Fragment> nonConfigFragments = nonConfig.getFragments();
1962             final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
1963             for (int i = 0; i < count; i++) {
1964                 Fragment f = nonConfigFragments.get(i);
1965                 if (f.mTargetIndex >= 0) {
1966                     if (f.mTargetIndex < mActive.size()) {
1967                         f.mTarget = mActive.get(f.mTargetIndex);
1968                     } else {
1969                         Log.w(TAG, "Re-attaching retained fragment " + f
1970                                 + " target no longer exists: " + f.mTargetIndex);
1971                         f.mTarget = null;
1972                     }
1973                 }
1974             }
1975         }
1976 
1977         // Build the list of currently added fragments.
1978         if (fms.mAdded != null) {
1979             mAdded = new ArrayList<Fragment>(fms.mAdded.length);
1980             for (int i=0; i<fms.mAdded.length; i++) {
1981                 Fragment f = mActive.get(fms.mAdded[i]);
1982                 if (f == null) {
1983                     throwException(new IllegalStateException(
1984                             "No instantiated fragment for index #" + fms.mAdded[i]));
1985                 }
1986                 f.mAdded = true;
1987                 if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
1988                 if (mAdded.contains(f)) {
1989                     throw new IllegalStateException("Already added!");
1990                 }
1991                 mAdded.add(f);
1992             }
1993         } else {
1994             mAdded = null;
1995         }
1996 
1997         // Build the back stack.
1998         if (fms.mBackStack != null) {
1999             mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
2000             for (int i=0; i<fms.mBackStack.length; i++) {
2001                 BackStackRecord bse = fms.mBackStack[i].instantiate(this);
2002                 if (DEBUG) {
2003                     Log.v(TAG, "restoreAllState: back stack #" + i
2004                         + " (index " + bse.mIndex + "): " + bse);
2005                     LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
2006                     PrintWriter pw = new FastPrintWriter(logw, false, 1024);
2007                     bse.dump("  ", pw, false);
2008                     pw.flush();
2009                 }
2010                 mBackStack.add(bse);
2011                 if (bse.mIndex >= 0) {
2012                     setBackStackIndex(bse.mIndex, bse);
2013                 }
2014             }
2015         } else {
2016             mBackStack = null;
2017         }
2018     }
2019 
attachController(FragmentHostCallback<?> host, FragmentContainer container, Fragment parent)2020     public void attachController(FragmentHostCallback<?> host, FragmentContainer container,
2021             Fragment parent) {
2022         if (mHost != null) throw new IllegalStateException("Already attached");
2023         mHost = host;
2024         mContainer = container;
2025         mParent = parent;
2026     }
2027 
noteStateNotSaved()2028     public void noteStateNotSaved() {
2029         mStateSaved = false;
2030     }
2031 
dispatchCreate()2032     public void dispatchCreate() {
2033         mStateSaved = false;
2034         moveToState(Fragment.CREATED, false);
2035     }
2036 
dispatchActivityCreated()2037     public void dispatchActivityCreated() {
2038         mStateSaved = false;
2039         moveToState(Fragment.ACTIVITY_CREATED, false);
2040     }
2041 
dispatchStart()2042     public void dispatchStart() {
2043         mStateSaved = false;
2044         moveToState(Fragment.STARTED, false);
2045     }
2046 
dispatchResume()2047     public void dispatchResume() {
2048         mStateSaved = false;
2049         moveToState(Fragment.RESUMED, false);
2050     }
2051 
dispatchPause()2052     public void dispatchPause() {
2053         moveToState(Fragment.STARTED, false);
2054     }
2055 
dispatchStop()2056     public void dispatchStop() {
2057         moveToState(Fragment.STOPPED, false);
2058     }
2059 
dispatchDestroyView()2060     public void dispatchDestroyView() {
2061         moveToState(Fragment.CREATED, false);
2062     }
2063 
dispatchDestroy()2064     public void dispatchDestroy() {
2065         mDestroyed = true;
2066         execPendingActions();
2067         moveToState(Fragment.INITIALIZING, false);
2068         mHost = null;
2069         mContainer = null;
2070         mParent = null;
2071     }
2072 
dispatchMultiWindowModeChanged(boolean isInMultiWindowMode)2073     public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
2074         if (mAdded == null) {
2075             return;
2076         }
2077         for (int i = mAdded.size() - 1; i >= 0; --i) {
2078             final Fragment f = mAdded.get(i);
2079             if (f != null) {
2080                 f.performMultiWindowModeChanged(isInMultiWindowMode);
2081             }
2082         }
2083     }
2084 
dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode)2085     public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
2086         if (mAdded == null) {
2087             return;
2088         }
2089         for (int i = mAdded.size() - 1; i >= 0; --i) {
2090             final Fragment f = mAdded.get(i);
2091             if (f != null) {
2092                 f.performPictureInPictureModeChanged(isInPictureInPictureMode);
2093             }
2094         }
2095     }
2096 
dispatchConfigurationChanged(Configuration newConfig)2097     public void dispatchConfigurationChanged(Configuration newConfig) {
2098         if (mAdded != null) {
2099             for (int i=0; i<mAdded.size(); i++) {
2100                 Fragment f = mAdded.get(i);
2101                 if (f != null) {
2102                     f.performConfigurationChanged(newConfig);
2103                 }
2104             }
2105         }
2106     }
2107 
dispatchLowMemory()2108     public void dispatchLowMemory() {
2109         if (mAdded != null) {
2110             for (int i=0; i<mAdded.size(); i++) {
2111                 Fragment f = mAdded.get(i);
2112                 if (f != null) {
2113                     f.performLowMemory();
2114                 }
2115             }
2116         }
2117     }
2118 
dispatchTrimMemory(int level)2119     public void dispatchTrimMemory(int level) {
2120         if (mAdded != null) {
2121             for (int i=0; i<mAdded.size(); i++) {
2122                 Fragment f = mAdded.get(i);
2123                 if (f != null) {
2124                     f.performTrimMemory(level);
2125                 }
2126             }
2127         }
2128     }
2129 
dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater)2130     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
2131         boolean show = false;
2132         ArrayList<Fragment> newMenus = null;
2133         if (mAdded != null) {
2134             for (int i=0; i<mAdded.size(); i++) {
2135                 Fragment f = mAdded.get(i);
2136                 if (f != null) {
2137                     if (f.performCreateOptionsMenu(menu, inflater)) {
2138                         show = true;
2139                         if (newMenus == null) {
2140                             newMenus = new ArrayList<Fragment>();
2141                         }
2142                         newMenus.add(f);
2143                     }
2144                 }
2145             }
2146         }
2147 
2148         if (mCreatedMenus != null) {
2149             for (int i=0; i<mCreatedMenus.size(); i++) {
2150                 Fragment f = mCreatedMenus.get(i);
2151                 if (newMenus == null || !newMenus.contains(f)) {
2152                     f.onDestroyOptionsMenu();
2153                 }
2154             }
2155         }
2156 
2157         mCreatedMenus = newMenus;
2158 
2159         return show;
2160     }
2161 
dispatchPrepareOptionsMenu(Menu menu)2162     public boolean dispatchPrepareOptionsMenu(Menu menu) {
2163         boolean show = false;
2164         if (mAdded != null) {
2165             for (int i=0; i<mAdded.size(); i++) {
2166                 Fragment f = mAdded.get(i);
2167                 if (f != null) {
2168                     if (f.performPrepareOptionsMenu(menu)) {
2169                         show = true;
2170                     }
2171                 }
2172             }
2173         }
2174         return show;
2175     }
2176 
dispatchOptionsItemSelected(MenuItem item)2177     public boolean dispatchOptionsItemSelected(MenuItem item) {
2178         if (mAdded != null) {
2179             for (int i=0; i<mAdded.size(); i++) {
2180                 Fragment f = mAdded.get(i);
2181                 if (f != null) {
2182                     if (f.performOptionsItemSelected(item)) {
2183                         return true;
2184                     }
2185                 }
2186             }
2187         }
2188         return false;
2189     }
2190 
dispatchContextItemSelected(MenuItem item)2191     public boolean dispatchContextItemSelected(MenuItem item) {
2192         if (mAdded != null) {
2193             for (int i=0; i<mAdded.size(); i++) {
2194                 Fragment f = mAdded.get(i);
2195                 if (f != null) {
2196                     if (f.performContextItemSelected(item)) {
2197                         return true;
2198                     }
2199                 }
2200             }
2201         }
2202         return false;
2203     }
2204 
dispatchOptionsMenuClosed(Menu menu)2205     public void dispatchOptionsMenuClosed(Menu menu) {
2206         if (mAdded != null) {
2207             for (int i=0; i<mAdded.size(); i++) {
2208                 Fragment f = mAdded.get(i);
2209                 if (f != null) {
2210                     f.performOptionsMenuClosed(menu);
2211                 }
2212             }
2213         }
2214     }
2215 
2216     @Override
invalidateOptionsMenu()2217     public void invalidateOptionsMenu() {
2218         if (mHost != null && mCurState == Fragment.RESUMED) {
2219             mHost.onInvalidateOptionsMenu();
2220         } else {
2221             mNeedMenuInvalidate = true;
2222         }
2223     }
2224 
reverseTransit(int transit)2225     public static int reverseTransit(int transit) {
2226         int rev = 0;
2227         switch (transit) {
2228             case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
2229                 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
2230                 break;
2231             case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
2232                 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
2233                 break;
2234             case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
2235                 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
2236                 break;
2237         }
2238         return rev;
2239 
2240     }
2241 
transitToStyleIndex(int transit, boolean enter)2242     public static int transitToStyleIndex(int transit, boolean enter) {
2243         int animAttr = -1;
2244         switch (transit) {
2245             case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
2246                 animAttr = enter
2247                     ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation
2248                     : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation;
2249                 break;
2250             case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
2251                 animAttr = enter
2252                     ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation
2253                     : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation;
2254                 break;
2255             case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
2256                 animAttr = enter
2257                     ? com.android.internal.R.styleable.FragmentAnimation_fragmentFadeEnterAnimation
2258                     : com.android.internal.R.styleable.FragmentAnimation_fragmentFadeExitAnimation;
2259                 break;
2260         }
2261         return animAttr;
2262     }
2263 
2264     @Override
onCreateView(View parent, String name, Context context, AttributeSet attrs)2265     public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
2266         if (!"fragment".equals(name)) {
2267             return null;
2268         }
2269 
2270         String fname = attrs.getAttributeValue(null, "class");
2271         TypedArray a =
2272                 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
2273         if (fname == null) {
2274             fname = a.getString(com.android.internal.R.styleable.Fragment_name);
2275         }
2276         int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);
2277         String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
2278         a.recycle();
2279 
2280         int containerId = parent != null ? parent.getId() : 0;
2281         if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
2282             throw new IllegalArgumentException(attrs.getPositionDescription()
2283                     + ": Must specify unique android:id, android:tag, or have a parent with"
2284                     + " an id for " + fname);
2285         }
2286 
2287         // If we restored from a previous state, we may already have
2288         // instantiated this fragment from the state and should use
2289         // that instance instead of making a new one.
2290         Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
2291         if (fragment == null && tag != null) {
2292             fragment = findFragmentByTag(tag);
2293         }
2294         if (fragment == null && containerId != View.NO_ID) {
2295             fragment = findFragmentById(containerId);
2296         }
2297 
2298         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
2299                 + Integer.toHexString(id) + " fname=" + fname
2300                 + " existing=" + fragment);
2301         if (fragment == null) {
2302             fragment = Fragment.instantiate(context, fname);
2303             fragment.mFromLayout = true;
2304             fragment.mFragmentId = id != 0 ? id : containerId;
2305             fragment.mContainerId = containerId;
2306             fragment.mTag = tag;
2307             fragment.mInLayout = true;
2308             fragment.mFragmentManager = this;
2309             fragment.mHost = mHost;
2310             fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
2311             addFragment(fragment, true);
2312         } else if (fragment.mInLayout) {
2313             // A fragment already exists and it is not one we restored from
2314             // previous state.
2315             throw new IllegalArgumentException(attrs.getPositionDescription()
2316                     + ": Duplicate id 0x" + Integer.toHexString(id)
2317                     + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
2318                     + " with another fragment for " + fname);
2319         } else {
2320             // This fragment was retained from a previous instance; get it
2321             // going now.
2322             fragment.mInLayout = true;
2323             fragment.mHost = mHost;
2324             // If this fragment is newly instantiated (either right now, or
2325             // from last saved state), then give it the attributes to
2326             // initialize itself.
2327             if (!fragment.mRetaining) {
2328                 fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
2329             }
2330         }
2331 
2332         // If we haven't finished entering the CREATED state ourselves yet,
2333         // push the inflated child fragment along.
2334         if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
2335             moveToState(fragment, Fragment.CREATED, 0, 0, false);
2336         } else {
2337             moveToState(fragment);
2338         }
2339 
2340         if (fragment.mView == null) {
2341             throw new IllegalStateException("Fragment " + fname
2342                     + " did not create a view.");
2343         }
2344         if (id != 0) {
2345             fragment.mView.setId(id);
2346         }
2347         if (fragment.mView.getTag() == null) {
2348             fragment.mView.setTag(tag);
2349         }
2350         return fragment.mView;
2351     }
2352 
2353     @Override
onCreateView(String name, Context context, AttributeSet attrs)2354     public View onCreateView(String name, Context context, AttributeSet attrs) {
2355         return null;
2356     }
2357 
getLayoutInflaterFactory()2358     LayoutInflater.Factory2 getLayoutInflaterFactory() {
2359         return this;
2360     }
2361 }
2362