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