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