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