FragmentManager.java revision ab36acb39941ce981dddda9f9cf4d2d23a56fd26
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import android.animation.Animator;
20import android.animation.AnimatorInflater;
21import android.animation.AnimatorListenerAdapter;
22import android.content.res.TypedArray;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.util.Log;
28import android.util.SparseArray;
29import android.view.Menu;
30import android.view.MenuInflater;
31import android.view.MenuItem;
32import android.view.View;
33import android.view.ViewGroup;
34
35import java.io.FileDescriptor;
36import java.io.PrintWriter;
37import java.util.ArrayList;
38
39/**
40 * Interface for interacting with {@link Fragment} objects inside of an
41 * {@link Activity}
42 */
43public abstract class FragmentManager {
44    /**
45     * Representation of an entry on the fragment back stack, as created
46     * with {@link FragmentTransaction#addToBackStack(String)
47     * FragmentTransaction.addToBackStack()}.  Entries can later be
48     * retrieved with {@link FragmentManager#getBackStackEntry(int)
49     * FragmentManager.getBackStackEntry()}.
50     *
51     * <p>Note that you should never hold on to a BackStackEntry object;
52     * the identifier as returned by {@link #getId} is the only thing that
53     * will be persisted across activity instances.
54     */
55    public interface BackStackEntry {
56        /**
57         * Return the unique identifier for the entry.  This is the only
58         * representation of the entry that will persist across activity
59         * instances.
60         */
61        public int getId();
62
63        /**
64         * Return the full bread crumb title for the entry, or null if it
65         * does not have one.
66         */
67        public CharSequence getBreadCrumbTitle();
68
69        /**
70         * Return the short bread crumb title for the entry, or null if it
71         * does not have one.
72         */
73        public CharSequence getBreadCrumbShortTitle();
74    }
75
76    /**
77     * Interface to watch for changes to the back stack.
78     */
79    public interface OnBackStackChangedListener {
80        /**
81         * Called whenever the contents of the back stack change.
82         */
83        public void onBackStackChanged();
84    }
85
86    /**
87     * Start a series of edit operations on the Fragments associated with
88     * this FragmentManager.
89     *
90     * <p>Note: A fragment transaction can only be created/committed prior
91     * to an activity saving its state.  If you try to commit a transaction
92     * after {@link Activity#onSaveInstanceState Activity.onSaveInstanceState()}
93     * (and prior to a following {@link Activity#onStart Activity.onStart}
94     * or {@link Activity#onResume Activity.onResume()}, you will get an error.
95     * This is because the framework takes care of saving your current fragments
96     * in the state, and if changes are made after the state is saved then they
97     * will be lost.</p>
98     */
99    public abstract FragmentTransaction openTransaction();
100
101    /**
102     * Finds a fragment that was identified by the given id either when inflated
103     * from XML or as the container ID when added in a transaction.  This first
104     * searches through fragments that are currently added to the manager's
105     * activity; if no such fragment is found, then all fragments currently
106     * on the back stack associated with this ID are searched.
107     * @return The fragment if found or null otherwise.
108     */
109    public abstract Fragment findFragmentById(int id);
110
111    /**
112     * Finds a fragment that was identified by the given tag either when inflated
113     * from XML or as supplied when added in a transaction.  This first
114     * searches through fragments that are currently added to the manager's
115     * activity; if no such fragment is found, then all fragments currently
116     * on the back stack are searched.
117     * @return The fragment if found or null otherwise.
118     */
119    public abstract Fragment findFragmentByTag(String tag);
120
121    /**
122     * Flag for {@link #popBackStack(String, int)}
123     * and {@link #popBackStack(int, int)}: If set, and the name or ID of
124     * a back stack entry has been supplied, then all matching entries will
125     * be consumed until one that doesn't match is found or the bottom of
126     * the stack is reached.  Otherwise, all entries up to but not including that entry
127     * will be removed.
128     */
129    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
130
131    /**
132     * Pop the top state off the back stack.  Returns true if there was one
133     * to pop, else false.
134     */
135    public abstract boolean popBackStack();
136
137    /**
138     * Pop the last fragment transition from the manager's fragment
139     * back stack.  If there is nothing to pop, false is returned.
140     * @param name If non-null, this is the name of a previous back state
141     * to look for; if found, all states up to that state will be popped.  The
142     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
143     * the named state itself is popped. If null, only the top state is popped.
144     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
145     */
146    public abstract boolean popBackStack(String name, int flags);
147
148    /**
149     * Pop all back stack states up to the one with the given identifier.
150     * @param id Identifier of the stated to be popped. If no identifier exists,
151     * false is returned.
152     * The identifier is the number returned by
153     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
154     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
155     * the named state itself is popped.
156     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
157     */
158    public abstract boolean popBackStack(int id, int flags);
159
160    /**
161     * Return the number of entries currently in the back stack.
162     */
163    public abstract int countBackStackEntries();
164
165    /**
166     * Return the BackStackEntry at index <var>index</var> in the back stack;
167     * entries start index 0 being the bottom of the stack.
168     */
169    public abstract BackStackEntry getBackStackEntry(int index);
170
171    /**
172     * Add a new listener for changes to the fragment back stack.
173     */
174    public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
175
176    /**
177     * Remove a listener that was previously added with
178     * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
179     */
180    public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
181
182    /**
183     * Put a reference to a fragment in a Bundle.  This Bundle can be
184     * persisted as saved state, and when later restoring
185     * {@link #getFragment(Bundle, String)} will return the current
186     * instance of the same fragment.
187     *
188     * @param bundle The bundle in which to put the fragment reference.
189     * @param key The name of the entry in the bundle.
190     * @param fragment The Fragment whose reference is to be stored.
191     */
192    public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
193
194    /**
195     * Retrieve the current Fragment instance for a reference previously
196     * placed with {@link #putFragment(Bundle, String, Fragment)}.
197     *
198     * @param bundle The bundle from which to retrieve the fragment reference.
199     * @param key The name of the entry in the bundle.
200     * @return Returns the current Fragment instance that is associated with
201     * the given reference.
202     */
203    public abstract Fragment getFragment(Bundle bundle, String key);
204
205    /**
206     * Print the FragmentManager's state into the given stream.
207     *
208     * @param prefix Text to print at the front of each line.
209     * @param fd The raw file descriptor that the dump is being sent to.
210     * @param writer A PrintWriter to which the dump is to be set.
211     * @param args additional arguments to the dump request.
212     */
213    public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
214}
215
216final class FragmentManagerState implements Parcelable {
217    FragmentState[] mActive;
218    int[] mAdded;
219    BackStackState[] mBackStack;
220
221    public FragmentManagerState() {
222    }
223
224    public FragmentManagerState(Parcel in) {
225        mActive = in.createTypedArray(FragmentState.CREATOR);
226        mAdded = in.createIntArray();
227        mBackStack = in.createTypedArray(BackStackState.CREATOR);
228    }
229
230    public int describeContents() {
231        return 0;
232    }
233
234    public void writeToParcel(Parcel dest, int flags) {
235        dest.writeTypedArray(mActive, flags);
236        dest.writeIntArray(mAdded);
237        dest.writeTypedArray(mBackStack, flags);
238    }
239
240    public static final Parcelable.Creator<FragmentManagerState> CREATOR
241            = new Parcelable.Creator<FragmentManagerState>() {
242        public FragmentManagerState createFromParcel(Parcel in) {
243            return new FragmentManagerState(in);
244        }
245
246        public FragmentManagerState[] newArray(int size) {
247            return new FragmentManagerState[size];
248        }
249    };
250}
251
252/**
253 * Container for fragments associated with an activity.
254 */
255final class FragmentManagerImpl extends FragmentManager {
256    static final boolean DEBUG = true;
257    static final String TAG = "FragmentManager";
258
259    static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
260    static final String TARGET_STATE_TAG = "android:target_state";
261    static final String VIEW_STATE_TAG = "android:view_state";
262
263    ArrayList<Runnable> mPendingActions;
264    Runnable[] mTmpActions;
265    boolean mExecutingActions;
266
267    ArrayList<Fragment> mActive;
268    ArrayList<Fragment> mAdded;
269    ArrayList<Integer> mAvailIndices;
270    ArrayList<BackStackRecord> mBackStack;
271    ArrayList<Fragment> mCreatedMenus;
272
273    // Must be accessed while locked.
274    ArrayList<BackStackRecord> mBackStackIndices;
275    ArrayList<Integer> mAvailBackStackIndices;
276
277    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
278
279    int mCurState = Fragment.INITIALIZING;
280    Activity mActivity;
281
282    boolean mNeedMenuInvalidate;
283    boolean mStateSaved;
284    String mNoTransactionsBecause;
285
286    // Temporary vars for state save and restore.
287    Bundle mStateBundle = null;
288    SparseArray<Parcelable> mStateArray = null;
289
290    Runnable mExecCommit = new Runnable() {
291        @Override
292        public void run() {
293            execPendingActions();
294        }
295    };
296
297    @Override
298    public FragmentTransaction openTransaction() {
299        return new BackStackRecord(this);
300    }
301
302    @Override
303    public boolean popBackStack() {
304        return popBackStackState(mActivity.mHandler, null, -1, 0);
305    }
306
307    @Override
308    public boolean popBackStack(String name, int flags) {
309        return popBackStackState(mActivity.mHandler, name, -1, flags);
310    }
311
312    @Override
313    public boolean popBackStack(int id, int flags) {
314        if (id < 0) {
315            throw new IllegalArgumentException("Bad id: " + id);
316        }
317        return popBackStackState(mActivity.mHandler, null, id, flags);
318    }
319
320    @Override
321    public int countBackStackEntries() {
322        return mBackStack != null ? mBackStack.size() : 0;
323    }
324
325    @Override
326    public BackStackEntry getBackStackEntry(int index) {
327        return mBackStack.get(index);
328    }
329
330    @Override
331    public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
332        if (mBackStackChangeListeners == null) {
333            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
334        }
335        mBackStackChangeListeners.add(listener);
336    }
337
338    @Override
339    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
340        if (mBackStackChangeListeners != null) {
341            mBackStackChangeListeners.remove(listener);
342        }
343    }
344
345    @Override
346    public void putFragment(Bundle bundle, String key, Fragment fragment) {
347        if (fragment.mIndex < 0) {
348            throw new IllegalStateException("Fragment " + fragment
349                    + " is not currently in the FragmentManager");
350        }
351        bundle.putInt(key, fragment.mIndex);
352    }
353
354    @Override
355    public Fragment getFragment(Bundle bundle, String key) {
356        int index = bundle.getInt(key, -1);
357        if (index == -1) {
358            return null;
359        }
360        if (index >= mActive.size()) {
361            throw new IllegalStateException("Fragement no longer exists for key "
362                    + key + ": index " + index);
363        }
364        Fragment f = mActive.get(index);
365        if (f == null) {
366            throw new IllegalStateException("Fragement no longer exists for key "
367                    + key + ": index " + index);
368        }
369        return f;
370    }
371
372    @Override
373    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
374        if (mActive == null || mActive.size() <= 0) {
375            return;
376        }
377
378        writer.print(prefix); writer.println("Active Fragments:");
379
380        String innerPrefix = prefix + "    ";
381
382        int N = mActive.size();
383        for (int i=0; i<N; i++) {
384            Fragment f = mActive.get(i);
385            if (f != null) {
386                writer.print(prefix); writer.print("  #"); writer.print(i);
387                        writer.print(": "); writer.println(f.toString());
388                f.dump(innerPrefix, fd, writer, args);
389            }
390        }
391
392        if (mAdded != null) {
393            N = mAdded.size();
394            if (N > 0) {
395                writer.print(prefix); writer.println("Added Fragments:");
396                for (int i=0; i<N; i++) {
397                    Fragment f = mAdded.get(i);
398                    writer.print(prefix); writer.print("  #"); writer.print(i);
399                            writer.print(": "); writer.println(f.toString());
400                }
401            }
402        }
403
404        if (mBackStack != null) {
405            N = mBackStack.size();
406            if (N > 0) {
407                writer.print(prefix); writer.println("Back Stack:");
408                for (int i=0; i<N; i++) {
409                    BackStackRecord bs = mBackStack.get(i);
410                    writer.print(prefix); writer.print("  #"); writer.print(i);
411                            writer.print(": "); writer.println(bs.toString());
412                }
413            }
414        }
415    }
416
417    Animator loadAnimator(Fragment fragment, int transit, boolean enter,
418            int transitionStyle) {
419        Animator animObj = fragment.onCreateAnimator(transit, enter,
420                fragment.mNextAnim);
421        if (animObj != null) {
422            return animObj;
423        }
424
425        if (fragment.mNextAnim != 0) {
426            Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim);
427            if (anim != null) {
428                return anim;
429            }
430        }
431
432        if (transit == 0) {
433            return null;
434        }
435
436        int styleIndex = transitToStyleIndex(transit, enter);
437        if (styleIndex < 0) {
438            return null;
439        }
440
441        if (transitionStyle == 0 && mActivity.getWindow() != null) {
442            transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
443        }
444        if (transitionStyle == 0) {
445            return null;
446        }
447
448        TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
449                com.android.internal.R.styleable.FragmentAnimation);
450        int anim = attrs.getResourceId(styleIndex, 0);
451        attrs.recycle();
452
453        if (anim == 0) {
454            return null;
455        }
456
457        return AnimatorInflater.loadAnimator(mActivity, anim);
458    }
459
460    void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
461        // Fragments that are not currently added will sit in the onCreate() state.
462        if (!f.mAdded && newState > Fragment.CREATED) {
463            newState = Fragment.CREATED;
464        }
465
466        if (f.mState < newState) {
467            switch (f.mState) {
468                case Fragment.INITIALIZING:
469                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
470                    if (f.mSavedFragmentState != null) {
471                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
472                                FragmentManagerImpl.VIEW_STATE_TAG);
473                        f.mTarget = getFragment(f.mSavedFragmentState,
474                                FragmentManagerImpl.TARGET_STATE_TAG);
475                        if (f.mTarget != null) {
476                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
477                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
478                        }
479                    }
480                    f.mActivity = mActivity;
481                    f.mCalled = false;
482                    f.onAttach(mActivity);
483                    if (!f.mCalled) {
484                        throw new SuperNotCalledException("Fragment " + f
485                                + " did not call through to super.onAttach()");
486                    }
487                    mActivity.onAttachFragment(f);
488
489                    if (!f.mRetaining) {
490                        f.mCalled = false;
491                        f.onCreate(f.mSavedFragmentState);
492                        if (!f.mCalled) {
493                            throw new SuperNotCalledException("Fragment " + f
494                                    + " did not call through to super.onCreate()");
495                        }
496                    }
497                    f.mRetaining = false;
498                    if (f.mFromLayout) {
499                        // For fragments that are part of the content view
500                        // layout, we need to instantiate the view immediately
501                        // and the inflater will take care of adding it.
502                        f.mView = f.onCreateView(mActivity.getLayoutInflater(),
503                                null, f.mSavedFragmentState);
504                        if (f.mView != null) {
505                            f.mView.setSaveFromParentEnabled(false);
506                            f.restoreViewState();
507                            if (f.mHidden) f.mView.setVisibility(View.GONE);
508                        }
509                    }
510                case Fragment.CREATED:
511                    if (newState > Fragment.CREATED) {
512                        if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f);
513                        if (!f.mFromLayout) {
514                            ViewGroup container = null;
515                            if (f.mContainerId != 0) {
516                                container = (ViewGroup)mActivity.findViewById(f.mContainerId);
517                                if (container == null) {
518                                    throw new IllegalArgumentException("No view found for id 0x"
519                                            + Integer.toHexString(f.mContainerId)
520                                            + " for fragment " + f);
521                                }
522                            }
523                            f.mContainer = container;
524                            f.mView = f.onCreateView(mActivity.getLayoutInflater(),
525                                    container, f.mSavedFragmentState);
526                            if (f.mView != null) {
527                                f.mView.setSaveFromParentEnabled(false);
528                                if (container != null) {
529                                    Animator anim = loadAnimator(f, transit, true,
530                                            transitionStyle);
531                                    if (anim != null) {
532                                        anim.setTarget(f.mView);
533                                        anim.start();
534                                    }
535                                    container.addView(f.mView);
536                                    f.restoreViewState();
537                                }
538                                if (f.mHidden) f.mView.setVisibility(View.GONE);
539                            }
540                        }
541
542                        f.mCalled = false;
543                        f.onActivityCreated(f.mSavedFragmentState);
544                        if (!f.mCalled) {
545                            throw new SuperNotCalledException("Fragment " + f
546                                    + " did not call through to super.onReady()");
547                        }
548                        f.mSavedFragmentState = null;
549                    }
550                case Fragment.ACTIVITY_CREATED:
551                    if (newState > Fragment.ACTIVITY_CREATED) {
552                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
553                        f.mCalled = false;
554                        f.onStart();
555                        if (!f.mCalled) {
556                            throw new SuperNotCalledException("Fragment " + f
557                                    + " did not call through to super.onStart()");
558                        }
559                    }
560                case Fragment.STARTED:
561                    if (newState > Fragment.STARTED) {
562                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
563                        f.mCalled = false;
564                        f.mResumed = true;
565                        f.onResume();
566                        if (!f.mCalled) {
567                            throw new SuperNotCalledException("Fragment " + f
568                                    + " did not call through to super.onResume()");
569                        }
570                    }
571            }
572        } else if (f.mState > newState) {
573            switch (f.mState) {
574                case Fragment.RESUMED:
575                    if (newState < Fragment.RESUMED) {
576                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
577                        f.mCalled = false;
578                        f.onPause();
579                        if (!f.mCalled) {
580                            throw new SuperNotCalledException("Fragment " + f
581                                    + " did not call through to super.onPause()");
582                        }
583                        f.mResumed = false;
584                    }
585                case Fragment.STARTED:
586                    if (newState < Fragment.STARTED) {
587                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
588                        f.mCalled = false;
589                        f.performStop();
590                        if (!f.mCalled) {
591                            throw new SuperNotCalledException("Fragment " + f
592                                    + " did not call through to super.onStop()");
593                        }
594                    }
595                case Fragment.ACTIVITY_CREATED:
596                    if (newState < Fragment.ACTIVITY_CREATED) {
597                        if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
598                        if (f.mView != null) {
599                            // Need to save the current view state if not
600                            // done already.
601                            if (!mActivity.isFinishing() && f.mSavedViewState == null) {
602                                saveFragmentViewState(f);
603                            }
604                        }
605                        f.mCalled = false;
606                        f.onDestroyView();
607                        if (!f.mCalled) {
608                            throw new SuperNotCalledException("Fragment " + f
609                                    + " did not call through to super.onDestroyedView()");
610                        }
611                        if (f.mView != null && f.mContainer != null) {
612                            Animator anim = null;
613                            if (mCurState > Fragment.INITIALIZING) {
614                                anim = loadAnimator(f, transit, false,
615                                        transitionStyle);
616                            }
617                            if (anim != null) {
618                                final ViewGroup container = f.mContainer;
619                                final View view = f.mView;
620                                container.startViewTransition(view);
621                                anim.addListener(new AnimatorListenerAdapter() {
622                                    @Override
623                                    public void onAnimationEnd(Animator anim) {
624                                        container.endViewTransition(view);
625                                    }
626                                });
627                                anim.setTarget(f.mView);
628                                anim.start();
629
630                            }
631                            f.mContainer.removeView(f.mView);
632                        }
633                        f.mContainer = null;
634                        f.mView = null;
635                    }
636                case Fragment.CREATED:
637                    if (newState < Fragment.CREATED) {
638                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
639                        if (!f.mRetaining) {
640                            f.mCalled = false;
641                            f.onDestroy();
642                            if (!f.mCalled) {
643                                throw new SuperNotCalledException("Fragment " + f
644                                        + " did not call through to super.onDestroy()");
645                            }
646                        }
647
648                        f.mCalled = false;
649                        f.onDetach();
650                        if (!f.mCalled) {
651                            throw new SuperNotCalledException("Fragment " + f
652                                    + " did not call through to super.onDetach()");
653                        }
654                        f.mImmediateActivity = null;
655                        f.mActivity = null;
656                    }
657            }
658        }
659
660        f.mState = newState;
661    }
662
663    void moveToState(Fragment f) {
664        moveToState(f, mCurState, 0, 0);
665    }
666
667    void moveToState(int newState, boolean always) {
668        moveToState(newState, 0, 0, always);
669    }
670
671    void moveToState(int newState, int transit, int transitStyle, boolean always) {
672        if (mActivity == null && newState != Fragment.INITIALIZING) {
673            throw new IllegalStateException("No activity");
674        }
675
676        if (!always && mCurState == newState) {
677            return;
678        }
679
680        mCurState = newState;
681        if (mActive != null) {
682            for (int i=0; i<mActive.size(); i++) {
683                Fragment f = mActive.get(i);
684                if (f != null) {
685                    moveToState(f, newState, transit, transitStyle);
686                }
687            }
688
689            if (mNeedMenuInvalidate && mActivity != null) {
690                mActivity.invalidateOptionsMenu();
691                mNeedMenuInvalidate = false;
692            }
693        }
694    }
695
696    void makeActive(Fragment f) {
697        if (f.mIndex >= 0) {
698            return;
699        }
700
701        if (mAvailIndices == null || mAvailIndices.size() <= 0) {
702            if (mActive == null) {
703                mActive = new ArrayList<Fragment>();
704            }
705            f.setIndex(mActive.size());
706            mActive.add(f);
707
708        } else {
709            f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1));
710            mActive.set(f.mIndex, f);
711        }
712    }
713
714    void makeInactive(Fragment f) {
715        if (f.mIndex < 0) {
716            return;
717        }
718
719        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex);
720        mActive.set(f.mIndex, null);
721        if (mAvailIndices == null) {
722            mAvailIndices = new ArrayList<Integer>();
723        }
724        mAvailIndices.add(f.mIndex);
725        mActivity.invalidateFragmentIndex(f.mIndex);
726        f.clearIndex();
727    }
728
729    public void addFragment(Fragment fragment, boolean moveToStateNow) {
730        if (mAdded == null) {
731            mAdded = new ArrayList<Fragment>();
732        }
733        mAdded.add(fragment);
734        makeActive(fragment);
735        if (DEBUG) Log.v(TAG, "add: " + fragment);
736        fragment.mAdded = true;
737        if (fragment.mHasMenu) {
738            mNeedMenuInvalidate = true;
739        }
740        if (moveToStateNow) {
741            moveToState(fragment);
742        }
743    }
744
745    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
746        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
747        mAdded.remove(fragment);
748        final boolean inactive = fragment.mBackStackNesting <= 0;
749        if (fragment.mHasMenu) {
750            mNeedMenuInvalidate = true;
751        }
752        fragment.mAdded = false;
753        moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
754                transition, transitionStyle);
755        if (inactive) {
756            makeInactive(fragment);
757        }
758    }
759
760    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
761        if (DEBUG) Log.v(TAG, "hide: " + fragment);
762        if (!fragment.mHidden) {
763            fragment.mHidden = true;
764            if (fragment.mView != null) {
765                Animator anim = loadAnimator(fragment, transition, true,
766                        transitionStyle);
767                if (anim != null) {
768                    anim.setTarget(fragment.mView);
769                    anim.start();
770                }
771                fragment.mView.setVisibility(View.GONE);
772            }
773            if (fragment.mAdded && fragment.mHasMenu) {
774                mNeedMenuInvalidate = true;
775            }
776            fragment.onHiddenChanged(true);
777        }
778    }
779
780    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
781        if (DEBUG) Log.v(TAG, "show: " + fragment);
782        if (fragment.mHidden) {
783            fragment.mHidden = false;
784            if (fragment.mView != null) {
785                Animator anim = loadAnimator(fragment, transition, true,
786                        transitionStyle);
787                if (anim != null) {
788                    anim.setTarget(fragment.mView);
789                    anim.start();
790                }
791                fragment.mView.setVisibility(View.VISIBLE);
792            }
793            if (fragment.mAdded && fragment.mHasMenu) {
794                mNeedMenuInvalidate = true;
795            }
796            fragment.onHiddenChanged(false);
797        }
798    }
799
800    public Fragment findFragmentById(int id) {
801        if (mActive != null) {
802            // First look through added fragments.
803            for (int i=mAdded.size()-1; i>=0; i--) {
804                Fragment f = mAdded.get(i);
805                if (f != null && f.mFragmentId == id) {
806                    return f;
807                }
808            }
809            // Now for any known fragment.
810            for (int i=mActive.size()-1; i>=0; i--) {
811                Fragment f = mActive.get(i);
812                if (f != null && f.mFragmentId == id) {
813                    return f;
814                }
815            }
816        }
817        return null;
818    }
819
820    public Fragment findFragmentByTag(String tag) {
821        if (mActive != null && tag != null) {
822            // First look through added fragments.
823            for (int i=mAdded.size()-1; i>=0; i--) {
824                Fragment f = mAdded.get(i);
825                if (f != null && tag.equals(f.mTag)) {
826                    return f;
827                }
828            }
829            // Now for any known fragment.
830            for (int i=mActive.size()-1; i>=0; i--) {
831                Fragment f = mActive.get(i);
832                if (f != null && tag.equals(f.mTag)) {
833                    return f;
834                }
835            }
836        }
837        return null;
838    }
839
840    public Fragment findFragmentByWho(String who) {
841        if (mActive != null && who != null) {
842            for (int i=mActive.size()-1; i>=0; i--) {
843                Fragment f = mActive.get(i);
844                if (f != null && who.equals(f.mWho)) {
845                    return f;
846                }
847            }
848        }
849        return null;
850    }
851
852    public void enqueueAction(Runnable action, boolean allowStateLoss) {
853        if (!allowStateLoss && mStateSaved) {
854            throw new IllegalStateException(
855                    "Can not perform this action after onSaveInstanceState");
856        }
857        if (mNoTransactionsBecause != null) {
858            throw new IllegalStateException(
859                    "Can not perform this action inside of " + mNoTransactionsBecause);
860        }
861        synchronized (this) {
862            if (mPendingActions == null) {
863                mPendingActions = new ArrayList<Runnable>();
864            }
865            mPendingActions.add(action);
866            if (mPendingActions.size() == 1) {
867                mActivity.mHandler.removeCallbacks(mExecCommit);
868                mActivity.mHandler.post(mExecCommit);
869            }
870        }
871    }
872
873    public int allocBackStackIndex(BackStackRecord bse) {
874        synchronized (this) {
875            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
876                if (mBackStackIndices == null) {
877                    mBackStackIndices = new ArrayList<BackStackRecord>();
878                }
879                int index = mBackStackIndices.size();
880                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
881                mBackStackIndices.add(bse);
882                return index;
883
884            } else {
885                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
886                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
887                mBackStackIndices.set(index, bse);
888                return index;
889            }
890        }
891    }
892
893    public void setBackStackIndex(int index, BackStackRecord bse) {
894        synchronized (this) {
895            if (mBackStackIndices == null) {
896                mBackStackIndices = new ArrayList<BackStackRecord>();
897            }
898            int N = mBackStackIndices.size();
899            if (index < N) {
900                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
901                mBackStackIndices.set(index, bse);
902            } else {
903                while (N < index) {
904                    mBackStackIndices.add(null);
905                    if (mAvailBackStackIndices == null) {
906                        mAvailBackStackIndices = new ArrayList<Integer>();
907                    }
908                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
909                    mAvailBackStackIndices.add(N);
910                    N++;
911                }
912                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
913                mBackStackIndices.add(bse);
914            }
915        }
916    }
917
918    public void freeBackStackIndex(int index) {
919        synchronized (this) {
920            mBackStackIndices.set(index, null);
921            if (mAvailBackStackIndices == null) {
922                mAvailBackStackIndices = new ArrayList<Integer>();
923            }
924            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
925            mAvailBackStackIndices.add(index);
926        }
927    }
928
929    /**
930     * Only call from main thread!
931     */
932    public void execPendingActions() {
933        if (mExecutingActions) {
934            throw new IllegalStateException("Recursive entry to execPendingActions");
935        }
936
937        while (true) {
938            int numActions;
939
940            synchronized (this) {
941                if (mPendingActions == null || mPendingActions.size() == 0) {
942                    return;
943                }
944
945                numActions = mPendingActions.size();
946                if (mTmpActions == null || mTmpActions.length < numActions) {
947                    mTmpActions = new Runnable[numActions];
948                }
949                mPendingActions.toArray(mTmpActions);
950                mPendingActions.clear();
951                mActivity.mHandler.removeCallbacks(mExecCommit);
952            }
953
954            mExecutingActions = true;
955            for (int i=0; i<numActions; i++) {
956                mTmpActions[i].run();
957            }
958            mExecutingActions = false;
959        }
960    }
961
962    void reportBackStackChanged() {
963        if (mBackStackChangeListeners != null) {
964            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
965                mBackStackChangeListeners.get(i).onBackStackChanged();
966            }
967        }
968    }
969
970    void addBackStackState(BackStackRecord state) {
971        if (mBackStack == null) {
972            mBackStack = new ArrayList<BackStackRecord>();
973        }
974        mBackStack.add(state);
975        reportBackStackChanged();
976    }
977
978    boolean popBackStackState(Handler handler, String name, int id, int flags) {
979        if (mBackStack == null) {
980            return false;
981        }
982        if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) {
983            int last = mBackStack.size()-1;
984            if (last < 0) {
985                return false;
986            }
987            final BackStackRecord bss = mBackStack.remove(last);
988            enqueueAction(new Runnable() {
989                public void run() {
990                    if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
991                    bss.popFromBackStack(true);
992                    reportBackStackChanged();
993                }
994            }, false);
995        } else {
996            int index = -1;
997            if (name != null || id >= 0) {
998                // If a name or ID is specified, look for that place in
999                // the stack.
1000                index = mBackStack.size()-1;
1001                while (index >= 0) {
1002                    BackStackRecord bss = mBackStack.get(index);
1003                    if (name != null && name.equals(bss.getName())) {
1004                        break;
1005                    }
1006                    if (id >= 0 && id == bss.mIndex) {
1007                        break;
1008                    }
1009                    index--;
1010                }
1011                if (index < 0) {
1012                    return false;
1013                }
1014                if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) {
1015                    index--;
1016                    // Consume all following entries that match.
1017                    while (index >= 0) {
1018                        BackStackRecord bss = mBackStack.get(index);
1019                        if ((name != null && name.equals(bss.getName()))
1020                                || (id >= 0 && id == bss.mIndex)) {
1021                            index--;
1022                            continue;
1023                        }
1024                        break;
1025                    }
1026                }
1027            }
1028            if (index == mBackStack.size()-1) {
1029                return false;
1030            }
1031            final ArrayList<BackStackRecord> states
1032                    = new ArrayList<BackStackRecord>();
1033            for (int i=mBackStack.size()-1; i>index; i--) {
1034                states.add(mBackStack.remove(i));
1035            }
1036            enqueueAction(new Runnable() {
1037                public void run() {
1038                    final int LAST = states.size()-1;
1039                    for (int i=0; i<=LAST; i++) {
1040                        if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
1041                        states.get(i).popFromBackStack(i == LAST);
1042                    }
1043                    reportBackStackChanged();
1044                }
1045            }, false);
1046        }
1047        return true;
1048    }
1049
1050    ArrayList<Fragment> retainNonConfig() {
1051        ArrayList<Fragment> fragments = null;
1052        if (mActive != null) {
1053            for (int i=0; i<mActive.size(); i++) {
1054                Fragment f = mActive.get(i);
1055                if (f != null && f.mRetainInstance) {
1056                    if (fragments == null) {
1057                        fragments = new ArrayList<Fragment>();
1058                    }
1059                    fragments.add(f);
1060                    f.mRetaining = true;
1061                }
1062            }
1063        }
1064        return fragments;
1065    }
1066
1067    void saveFragmentViewState(Fragment f) {
1068        if (f.mView == null) {
1069            return;
1070        }
1071        if (mStateArray == null) {
1072            mStateArray = new SparseArray<Parcelable>();
1073        }
1074        f.mView.saveHierarchyState(mStateArray);
1075        if (mStateArray.size() > 0) {
1076            f.mSavedViewState = mStateArray;
1077            mStateArray = null;
1078        }
1079    }
1080
1081    Parcelable saveAllState() {
1082        mStateSaved = true;
1083
1084        if (mActive == null || mActive.size() <= 0) {
1085            return null;
1086        }
1087
1088        // First collect all active fragments.
1089        int N = mActive.size();
1090        FragmentState[] active = new FragmentState[N];
1091        boolean haveFragments = false;
1092        for (int i=0; i<N; i++) {
1093            Fragment f = mActive.get(i);
1094            if (f != null) {
1095                haveFragments = true;
1096
1097                FragmentState fs = new FragmentState(f);
1098                active[i] = fs;
1099
1100                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
1101                    if (mStateBundle == null) {
1102                        mStateBundle = new Bundle();
1103                    }
1104                    f.onSaveInstanceState(mStateBundle);
1105                    if (!mStateBundle.isEmpty()) {
1106                        fs.mSavedFragmentState = mStateBundle;
1107                        mStateBundle = null;
1108                    }
1109
1110                    if (f.mView != null) {
1111                        saveFragmentViewState(f);
1112                        if (f.mSavedViewState != null) {
1113                            if (fs.mSavedFragmentState == null) {
1114                                fs.mSavedFragmentState = new Bundle();
1115                            }
1116                            fs.mSavedFragmentState.putSparseParcelableArray(
1117                                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
1118                        }
1119                    }
1120
1121                    if (f.mTarget != null) {
1122                        if (fs.mSavedFragmentState == null) {
1123                            fs.mSavedFragmentState = new Bundle();
1124                        }
1125                        putFragment(fs.mSavedFragmentState,
1126                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
1127                        if (f.mTargetRequestCode != 0) {
1128                            fs.mSavedFragmentState.putInt(
1129                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
1130                                    f.mTargetRequestCode);
1131                        }
1132                    }
1133
1134                } else {
1135                    fs.mSavedFragmentState = f.mSavedFragmentState;
1136                }
1137
1138                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
1139                        + fs.mSavedFragmentState);
1140            }
1141        }
1142
1143        if (!haveFragments) {
1144            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
1145            return null;
1146        }
1147
1148        int[] added = null;
1149        BackStackState[] backStack = null;
1150
1151        // Build list of currently added fragments.
1152        if (mAdded != null) {
1153            N = mAdded.size();
1154            if (N > 0) {
1155                added = new int[N];
1156                for (int i=0; i<N; i++) {
1157                    added[i] = mAdded.get(i).mIndex;
1158                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
1159                            + ": " + mAdded.get(i));
1160                }
1161            }
1162        }
1163
1164        // Now save back stack.
1165        if (mBackStack != null) {
1166            N = mBackStack.size();
1167            if (N > 0) {
1168                backStack = new BackStackState[N];
1169                for (int i=0; i<N; i++) {
1170                    backStack[i] = new BackStackState(this, mBackStack.get(i));
1171                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
1172                            + ": " + mBackStack.get(i));
1173                }
1174            }
1175        }
1176
1177        FragmentManagerState fms = new FragmentManagerState();
1178        fms.mActive = active;
1179        fms.mAdded = added;
1180        fms.mBackStack = backStack;
1181        return fms;
1182    }
1183
1184    void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
1185        // If there is no saved state at all, then there can not be
1186        // any nonConfig fragments either, so that is that.
1187        if (state == null) return;
1188        FragmentManagerState fms = (FragmentManagerState)state;
1189        if (fms.mActive == null) return;
1190
1191        // First re-attach any non-config instances we are retaining back
1192        // to their saved state, so we don't try to instantiate them again.
1193        if (nonConfig != null) {
1194            for (int i=0; i<nonConfig.size(); i++) {
1195                Fragment f = nonConfig.get(i);
1196                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
1197                FragmentState fs = fms.mActive[f.mIndex];
1198                fs.mInstance = f;
1199                f.mSavedViewState = null;
1200                f.mBackStackNesting = 0;
1201                f.mInLayout = false;
1202                f.mAdded = false;
1203                if (fs.mSavedFragmentState != null) {
1204                    fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
1205                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
1206                            FragmentManagerImpl.VIEW_STATE_TAG);
1207                }
1208            }
1209        }
1210
1211        // Build the full list of active fragments, instantiating them from
1212        // their saved state.
1213        mActive = new ArrayList<Fragment>(fms.mActive.length);
1214        if (mAvailIndices != null) {
1215            mAvailIndices.clear();
1216        }
1217        for (int i=0; i<fms.mActive.length; i++) {
1218            FragmentState fs = fms.mActive[i];
1219            if (fs != null) {
1220                Fragment f = fs.instantiate(mActivity);
1221                if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f);
1222                mActive.add(f);
1223            } else {
1224                if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)");
1225                mActive.add(null);
1226                if (mAvailIndices == null) {
1227                    mAvailIndices = new ArrayList<Integer>();
1228                }
1229                if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i);
1230                mAvailIndices.add(i);
1231            }
1232        }
1233
1234        // Update the target of all retained fragments.
1235        if (nonConfig != null) {
1236            for (int i=0; i<nonConfig.size(); i++) {
1237                Fragment f = nonConfig.get(i);
1238                if (f.mTarget != null) {
1239                    if (f.mTarget.mIndex < mActive.size()) {
1240                        f.mTarget = mActive.get(f.mTarget.mIndex);
1241                    } else {
1242                        Log.w(TAG, "Re-attaching retained fragment " + f
1243                                + " target no longer exists: " + f.mTarget);
1244                        f.mTarget = null;
1245                    }
1246                }
1247            }
1248        }
1249
1250        // Build the list of currently added fragments.
1251        if (fms.mAdded != null) {
1252            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
1253            for (int i=0; i<fms.mAdded.length; i++) {
1254                Fragment f = mActive.get(fms.mAdded[i]);
1255                if (f == null) {
1256                    throw new IllegalStateException(
1257                            "No instantiated fragment for index #" + fms.mAdded[i]);
1258                }
1259                f.mAdded = true;
1260                f.mImmediateActivity = mActivity;
1261                if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f);
1262                mAdded.add(f);
1263            }
1264        } else {
1265            mAdded = null;
1266        }
1267
1268        // Build the back stack.
1269        if (fms.mBackStack != null) {
1270            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
1271            for (int i=0; i<fms.mBackStack.length; i++) {
1272                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
1273                if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i
1274                        + " (index " + bse.mIndex + "): " + bse);
1275                mBackStack.add(bse);
1276                if (bse.mIndex >= 0) {
1277                    setBackStackIndex(bse.mIndex, bse);
1278                }
1279            }
1280        } else {
1281            mBackStack = null;
1282        }
1283    }
1284
1285    public void attachActivity(Activity activity) {
1286        if (mActivity != null) throw new IllegalStateException();
1287        mActivity = activity;
1288    }
1289
1290    public void noteStateNotSaved() {
1291        mStateSaved = false;
1292    }
1293
1294    public void dispatchCreate() {
1295        mStateSaved = false;
1296        moveToState(Fragment.CREATED, false);
1297    }
1298
1299    public void dispatchActivityCreated() {
1300        mStateSaved = false;
1301        moveToState(Fragment.ACTIVITY_CREATED, false);
1302    }
1303
1304    public void dispatchStart() {
1305        mStateSaved = false;
1306        moveToState(Fragment.STARTED, false);
1307    }
1308
1309    public void dispatchResume() {
1310        mStateSaved = false;
1311        moveToState(Fragment.RESUMED, false);
1312    }
1313
1314    public void dispatchPause() {
1315        moveToState(Fragment.STARTED, false);
1316    }
1317
1318    public void dispatchStop() {
1319        moveToState(Fragment.ACTIVITY_CREATED, false);
1320    }
1321
1322    public void dispatchDestroy() {
1323        moveToState(Fragment.INITIALIZING, false);
1324        mActivity = null;
1325    }
1326
1327    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1328        boolean show = false;
1329        ArrayList<Fragment> newMenus = null;
1330        if (mActive != null) {
1331            for (int i=0; i<mAdded.size(); i++) {
1332                Fragment f = mAdded.get(i);
1333                if (f != null && !f.mHidden && f.mHasMenu) {
1334                    show = true;
1335                    f.onCreateOptionsMenu(menu, inflater);
1336                    if (newMenus == null) {
1337                        newMenus = new ArrayList<Fragment>();
1338                    }
1339                    newMenus.add(f);
1340                }
1341            }
1342        }
1343
1344        if (mCreatedMenus != null) {
1345            for (int i=0; i<mCreatedMenus.size(); i++) {
1346                Fragment f = mCreatedMenus.get(i);
1347                if (newMenus == null || !newMenus.contains(f)) {
1348                    f.onDestroyOptionsMenu();
1349                }
1350            }
1351        }
1352
1353        mCreatedMenus = newMenus;
1354
1355        return show;
1356    }
1357
1358    public boolean dispatchPrepareOptionsMenu(Menu menu) {
1359        boolean show = false;
1360        if (mActive != null) {
1361            for (int i=0; i<mAdded.size(); i++) {
1362                Fragment f = mAdded.get(i);
1363                if (f != null && !f.mHidden && f.mHasMenu) {
1364                    show = true;
1365                    f.onPrepareOptionsMenu(menu);
1366                }
1367            }
1368        }
1369        return show;
1370    }
1371
1372    public boolean dispatchOptionsItemSelected(MenuItem item) {
1373        if (mActive != null) {
1374            for (int i=0; i<mAdded.size(); i++) {
1375                Fragment f = mAdded.get(i);
1376                if (f != null && !f.mHidden && f.mHasMenu) {
1377                    if (f.onOptionsItemSelected(item)) {
1378                        return true;
1379                    }
1380                }
1381            }
1382        }
1383        return false;
1384    }
1385
1386    public boolean dispatchContextItemSelected(MenuItem item) {
1387        if (mActive != null) {
1388            for (int i=0; i<mAdded.size(); i++) {
1389                Fragment f = mAdded.get(i);
1390                if (f != null && !f.mHidden) {
1391                    if (f.onContextItemSelected(item)) {
1392                        return true;
1393                    }
1394                }
1395            }
1396        }
1397        return false;
1398    }
1399
1400    public void dispatchOptionsMenuClosed(Menu menu) {
1401        if (mActive != null) {
1402            for (int i=0; i<mAdded.size(); i++) {
1403                Fragment f = mAdded.get(i);
1404                if (f != null && !f.mHidden && f.mHasMenu) {
1405                    f.onOptionsMenuClosed(menu);
1406                }
1407            }
1408        }
1409    }
1410
1411    public static int reverseTransit(int transit) {
1412        int rev = 0;
1413        switch (transit) {
1414            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
1415                rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
1416                break;
1417            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
1418                rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
1419                break;
1420            case FragmentTransaction.TRANSIT_FRAGMENT_NEXT:
1421                rev = FragmentTransaction.TRANSIT_FRAGMENT_PREV;
1422                break;
1423            case FragmentTransaction.TRANSIT_FRAGMENT_PREV:
1424                rev = FragmentTransaction.TRANSIT_FRAGMENT_NEXT;
1425                break;
1426        }
1427        return rev;
1428
1429    }
1430
1431    public static int transitToStyleIndex(int transit, boolean enter) {
1432        int animAttr = -1;
1433        switch (transit) {
1434            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
1435                animAttr = enter
1436                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation
1437                    : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation;
1438                break;
1439            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
1440                animAttr = enter
1441                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation
1442                    : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation;
1443                break;
1444            case FragmentTransaction.TRANSIT_FRAGMENT_NEXT:
1445                animAttr = enter
1446                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentNextEnterAnimation
1447                    : com.android.internal.R.styleable.FragmentAnimation_fragmentNextExitAnimation;
1448                break;
1449            case FragmentTransaction.TRANSIT_FRAGMENT_PREV:
1450                animAttr = enter
1451                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentPrevEnterAnimation
1452                    : com.android.internal.R.styleable.FragmentAnimation_fragmentPrevExitAnimation;
1453                break;
1454        }
1455        return animAttr;
1456    }
1457}
1458