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