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