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