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