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