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