FragmentManager.java revision 5164246d7e47b9c995ca1e1587f3056eb777f60b
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 interface 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 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 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 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 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 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 boolean popBackStack(int id, int flags);
159
160    /**
161     * Return the number of entries currently in the back stack.
162     */
163    public 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 BackStackEntry getBackStackEntry(int index);
170
171    /**
172     * Add a new listener for changes to the fragment back stack.
173     */
174    public void addOnBackStackChangedListener(OnBackStackChangedListener listener);
175
176    /**
177     * Remove a listener that was previously added with
178     * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
179     */
180    public 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 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 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 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 implements 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
272    // Must be accessed while locked.
273    ArrayList<BackStackRecord> mBackStackIndices;
274    ArrayList<Integer> mAvailBackStackIndices;
275
276    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
277
278    int mCurState = Fragment.INITIALIZING;
279    Activity mActivity;
280
281    boolean mNeedMenuInvalidate;
282    boolean mStateSaved;
283    String mNoTransactionsBecause;
284
285    // Temporary vars for state save and restore.
286    Bundle mStateBundle = null;
287    SparseArray<Parcelable> mStateArray = null;
288
289    Runnable mExecCommit = new Runnable() {
290        @Override
291        public void run() {
292            execPendingActions();
293        }
294    };
295
296    @Override
297    public FragmentTransaction openTransaction() {
298        return new BackStackRecord(this);
299    }
300
301    @Override
302    public boolean popBackStack() {
303        return popBackStackState(mActivity.mHandler, null, -1, 0);
304    }
305
306    @Override
307    public boolean popBackStack(String name, int flags) {
308        return popBackStackState(mActivity.mHandler, name, -1, flags);
309    }
310
311    @Override
312    public boolean popBackStack(int id, int flags) {
313        if (id < 0) {
314            throw new IllegalArgumentException("Bad id: " + id);
315        }
316        return popBackStackState(mActivity.mHandler, null, id, flags);
317    }
318
319    @Override
320    public int countBackStackEntries() {
321        return mBackStack != null ? mBackStack.size() : 0;
322    }
323
324    @Override
325    public BackStackEntry getBackStackEntry(int index) {
326        return mBackStack.get(index);
327    }
328
329    @Override
330    public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
331        if (mBackStackChangeListeners == null) {
332            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
333        }
334        mBackStackChangeListeners.add(listener);
335    }
336
337    @Override
338    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
339        if (mBackStackChangeListeners != null) {
340            mBackStackChangeListeners.remove(listener);
341        }
342    }
343
344    @Override
345    public void putFragment(Bundle bundle, String key, Fragment fragment) {
346        if (fragment.mIndex < 0) {
347            throw new IllegalStateException("Fragment " + fragment
348                    + " is not currently in the FragmentManager");
349        }
350        bundle.putInt(key, fragment.mIndex);
351    }
352
353    @Override
354    public Fragment getFragment(Bundle bundle, String key) {
355        int index = bundle.getInt(key, -1);
356        if (index == -1) {
357            return null;
358        }
359        if (index >= mActive.size()) {
360            throw new IllegalStateException("Fragement no longer exists for key "
361                    + key + ": index " + index);
362        }
363        Fragment f = mActive.get(index);
364        if (f == null) {
365            throw new IllegalStateException("Fragement no longer exists for key "
366                    + key + ": index " + index);
367        }
368        return f;
369    }
370
371    @Override
372    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
373        if (mActive == null || mActive.size() <= 0) {
374            return;
375        }
376
377        writer.print(prefix); writer.println("Active Fragments:");
378
379        String innerPrefix = prefix + "    ";
380
381        int N = mActive.size();
382        for (int i=0; i<N; i++) {
383            Fragment f = mActive.get(i);
384            if (f != null) {
385                writer.print(prefix); writer.print("  #"); writer.print(i);
386                        writer.print(": "); writer.println(f.toString());
387                f.dump(innerPrefix, fd, writer, args);
388            }
389        }
390
391        if (mAdded != null) {
392            N = mAdded.size();
393            if (N > 0) {
394                writer.print(prefix); writer.println("Added Fragments:");
395                for (int i=0; i<N; i++) {
396                    Fragment f = mAdded.get(i);
397                    writer.print(prefix); writer.print("  #"); writer.print(i);
398                            writer.print(": "); writer.println(f.toString());
399                }
400            }
401        }
402
403        if (mBackStack != null) {
404            N = mBackStack.size();
405            if (N > 0) {
406                writer.print(prefix); writer.println("Back Stack:");
407                for (int i=0; i<N; i++) {
408                    BackStackRecord bs = mBackStack.get(i);
409                    writer.print(prefix); writer.print("  #"); writer.print(i);
410                            writer.print(": "); writer.println(bs.toString());
411                }
412            }
413        }
414    }
415
416    Animator loadAnimator(Fragment fragment, int transit, boolean enter,
417            int transitionStyle) {
418        Animator animObj = fragment.onCreateAnimator(transit, enter,
419                fragment.mNextAnim);
420        if (animObj != null) {
421            return animObj;
422        }
423
424        if (fragment.mNextAnim != 0) {
425            Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim);
426            if (anim != null) {
427                return anim;
428            }
429        }
430
431        if (transit == 0) {
432            return null;
433        }
434
435        int styleIndex = transitToStyleIndex(transit, enter);
436        if (styleIndex < 0) {
437            return null;
438        }
439
440        if (transitionStyle == 0 && mActivity.getWindow() != null) {
441            transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
442        }
443        if (transitionStyle == 0) {
444            return null;
445        }
446
447        TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
448                com.android.internal.R.styleable.FragmentAnimation);
449        int anim = attrs.getResourceId(styleIndex, 0);
450        attrs.recycle();
451
452        if (anim == 0) {
453            return null;
454        }
455
456        return AnimatorInflater.loadAnimator(mActivity, anim);
457    }
458
459    void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
460        // Fragments that are not currently added will sit in the onCreate() state.
461        if (!f.mAdded && newState > Fragment.CREATED) {
462            newState = Fragment.CREATED;
463        }
464
465        if (f.mState < newState) {
466            switch (f.mState) {
467                case Fragment.INITIALIZING:
468                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
469                    if (f.mSavedFragmentState != null) {
470                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
471                                FragmentManagerImpl.VIEW_STATE_TAG);
472                        f.mTarget = getFragment(f.mSavedFragmentState,
473                                FragmentManagerImpl.TARGET_STATE_TAG);
474                        if (f.mTarget != null) {
475                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
476                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
477                        }
478                    }
479                    f.mActivity = mActivity;
480                    f.mCalled = false;
481                    f.onAttach(mActivity);
482                    if (!f.mCalled) {
483                        throw new SuperNotCalledException("Fragment " + f
484                                + " did not call through to super.onAttach()");
485                    }
486                    mActivity.onAttachFragment(f);
487
488                    if (!f.mRetaining) {
489                        f.mCalled = false;
490                        f.onCreate(f.mSavedFragmentState);
491                        if (!f.mCalled) {
492                            throw new SuperNotCalledException("Fragment " + f
493                                    + " did not call through to super.onCreate()");
494                        }
495                    }
496                    f.mRetaining = false;
497                    if (f.mFromLayout) {
498                        // For fragments that are part of the content view
499                        // layout, we need to instantiate the view immediately
500                        // and the inflater will take care of adding it.
501                        f.mView = f.onCreateView(mActivity.getLayoutInflater(),
502                                null, f.mSavedFragmentState);
503                        if (f.mView != null) {
504                            f.mView.setSaveFromParentEnabled(false);
505                            f.restoreViewState();
506                            if (f.mHidden) f.mView.setVisibility(View.GONE);
507                        }
508                    }
509                case Fragment.CREATED:
510                    if (newState > Fragment.CREATED) {
511                        if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f);
512                        if (!f.mFromLayout) {
513                            ViewGroup container = null;
514                            if (f.mContainerId != 0) {
515                                container = (ViewGroup)mActivity.findViewById(f.mContainerId);
516                                if (container == null) {
517                                    throw new IllegalArgumentException("No view found for id 0x"
518                                            + Integer.toHexString(f.mContainerId)
519                                            + " for fragment " + f);
520                                }
521                            }
522                            f.mContainer = container;
523                            f.mView = f.onCreateView(mActivity.getLayoutInflater(),
524                                    container, f.mSavedFragmentState);
525                            if (f.mView != null) {
526                                f.mView.setSaveFromParentEnabled(false);
527                                if (container != null) {
528                                    Animator anim = loadAnimator(f, transit, true,
529                                            transitionStyle);
530                                    if (anim != null) {
531                                        anim.setTarget(f.mView);
532                                        anim.start();
533                                    }
534                                    container.addView(f.mView);
535                                    f.restoreViewState();
536                                }
537                                if (f.mHidden) f.mView.setVisibility(View.GONE);
538                            }
539                        }
540
541                        f.mCalled = false;
542                        f.onActivityCreated(f.mSavedFragmentState);
543                        if (!f.mCalled) {
544                            throw new SuperNotCalledException("Fragment " + f
545                                    + " did not call through to super.onReady()");
546                        }
547                        f.mSavedFragmentState = null;
548                    }
549                case Fragment.ACTIVITY_CREATED:
550                    if (newState > Fragment.ACTIVITY_CREATED) {
551                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
552                        f.mCalled = false;
553                        f.onStart();
554                        if (!f.mCalled) {
555                            throw new SuperNotCalledException("Fragment " + f
556                                    + " did not call through to super.onStart()");
557                        }
558                    }
559                case Fragment.STARTED:
560                    if (newState > Fragment.STARTED) {
561                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
562                        f.mCalled = false;
563                        f.mResumed = true;
564                        f.onResume();
565                        if (!f.mCalled) {
566                            throw new SuperNotCalledException("Fragment " + f
567                                    + " did not call through to super.onResume()");
568                        }
569                    }
570            }
571        } else if (f.mState > newState) {
572            switch (f.mState) {
573                case Fragment.RESUMED:
574                    if (newState < Fragment.RESUMED) {
575                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
576                        f.mCalled = false;
577                        f.onPause();
578                        if (!f.mCalled) {
579                            throw new SuperNotCalledException("Fragment " + f
580                                    + " did not call through to super.onPause()");
581                        }
582                        f.mResumed = false;
583                    }
584                case Fragment.STARTED:
585                    if (newState < Fragment.STARTED) {
586                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
587                        f.mCalled = false;
588                        f.performStop();
589                        if (!f.mCalled) {
590                            throw new SuperNotCalledException("Fragment " + f
591                                    + " did not call through to super.onStop()");
592                        }
593                    }
594                case Fragment.ACTIVITY_CREATED:
595                    if (newState < Fragment.ACTIVITY_CREATED) {
596                        if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
597                        if (f.mView != null) {
598                            // Need to save the current view state if not
599                            // done already.
600                            if (!mActivity.isFinishing() && f.mSavedViewState == null) {
601                                saveFragmentViewState(f);
602                            }
603                        }
604                        f.mCalled = false;
605                        f.onDestroyView();
606                        if (!f.mCalled) {
607                            throw new SuperNotCalledException("Fragment " + f
608                                    + " did not call through to super.onDestroyedView()");
609                        }
610                        if (f.mView != null && f.mContainer != null) {
611                            Animator anim = null;
612                            if (mCurState > Fragment.INITIALIZING) {
613                                anim = loadAnimator(f, transit, false,
614                                        transitionStyle);
615                            }
616                            if (anim != null) {
617                                final ViewGroup container = f.mContainer;
618                                final View view = f.mView;
619                                container.startViewTransition(view);
620                                anim.addListener(new AnimatorListenerAdapter() {
621                                    @Override
622                                    public void onAnimationEnd(Animator anim) {
623                                        container.endViewTransition(view);
624                                    }
625                                });
626                                anim.setTarget(f.mView);
627                                anim.start();
628
629                            }
630                            f.mContainer.removeView(f.mView);
631                        }
632                        f.mContainer = null;
633                        f.mView = null;
634                    }
635                case Fragment.CREATED:
636                    if (newState < Fragment.CREATED) {
637                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
638                        if (!f.mRetaining) {
639                            f.mCalled = false;
640                            f.onDestroy();
641                            if (!f.mCalled) {
642                                throw new SuperNotCalledException("Fragment " + f
643                                        + " did not call through to super.onDestroy()");
644                            }
645                        }
646
647                        f.mCalled = false;
648                        f.onDetach();
649                        if (!f.mCalled) {
650                            throw new SuperNotCalledException("Fragment " + f
651                                    + " did not call through to super.onDetach()");
652                        }
653                        f.mImmediateActivity = null;
654                        f.mActivity = null;
655                    }
656            }
657        }
658
659        f.mState = newState;
660    }
661
662    void moveToState(Fragment f) {
663        moveToState(f, mCurState, 0, 0);
664    }
665
666    void moveToState(int newState, boolean always) {
667        moveToState(newState, 0, 0, always);
668    }
669
670    void moveToState(int newState, int transit, int transitStyle, boolean always) {
671        if (mActivity == null && newState != Fragment.INITIALIZING) {
672            throw new IllegalStateException("No activity");
673        }
674
675        if (!always && mCurState == newState) {
676            return;
677        }
678
679        mCurState = newState;
680        if (mActive != null) {
681            for (int i=0; i<mActive.size(); i++) {
682                Fragment f = mActive.get(i);
683                if (f != null) {
684                    moveToState(f, newState, transit, transitStyle);
685                }
686            }
687
688            if (mNeedMenuInvalidate && mActivity != null) {
689                mActivity.invalidateOptionsMenu();
690                mNeedMenuInvalidate = false;
691            }
692        }
693    }
694
695    void makeActive(Fragment f) {
696        if (f.mIndex >= 0) {
697            return;
698        }
699
700        if (mAvailIndices == null || mAvailIndices.size() <= 0) {
701            if (mActive == null) {
702                mActive = new ArrayList<Fragment>();
703            }
704            f.setIndex(mActive.size());
705            mActive.add(f);
706
707        } else {
708            f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1));
709            mActive.set(f.mIndex, f);
710        }
711    }
712
713    void makeInactive(Fragment f) {
714        if (f.mIndex < 0) {
715            return;
716        }
717
718        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex);
719        mActive.set(f.mIndex, null);
720        if (mAvailIndices == null) {
721            mAvailIndices = new ArrayList<Integer>();
722        }
723        mAvailIndices.add(f.mIndex);
724        mActivity.invalidateFragmentIndex(f.mIndex);
725        f.clearIndex();
726    }
727
728    public void addFragment(Fragment fragment, boolean moveToStateNow) {
729        if (mAdded == null) {
730            mAdded = new ArrayList<Fragment>();
731        }
732        mAdded.add(fragment);
733        makeActive(fragment);
734        if (DEBUG) Log.v(TAG, "add: " + fragment);
735        fragment.mAdded = true;
736        if (fragment.mHasMenu) {
737            mNeedMenuInvalidate = true;
738        }
739        if (moveToStateNow) {
740            moveToState(fragment);
741        }
742    }
743
744    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
745        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
746        mAdded.remove(fragment);
747        final boolean inactive = fragment.mBackStackNesting <= 0;
748        if (fragment.mHasMenu) {
749            mNeedMenuInvalidate = true;
750        }
751        fragment.mAdded = false;
752        moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
753                transition, transitionStyle);
754        if (inactive) {
755            makeInactive(fragment);
756        }
757    }
758
759    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
760        if (DEBUG) Log.v(TAG, "hide: " + fragment);
761        if (!fragment.mHidden) {
762            fragment.mHidden = true;
763            if (fragment.mView != null) {
764                Animator anim = loadAnimator(fragment, transition, true,
765                        transitionStyle);
766                if (anim != null) {
767                    anim.setTarget(fragment.mView);
768                    anim.start();
769                }
770                fragment.mView.setVisibility(View.GONE);
771            }
772            if (fragment.mAdded && fragment.mHasMenu) {
773                mNeedMenuInvalidate = true;
774            }
775            fragment.onHiddenChanged(true);
776        }
777    }
778
779    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
780        if (DEBUG) Log.v(TAG, "show: " + fragment);
781        if (fragment.mHidden) {
782            fragment.mHidden = false;
783            if (fragment.mView != null) {
784                Animator anim = loadAnimator(fragment, transition, true,
785                        transitionStyle);
786                if (anim != null) {
787                    anim.setTarget(fragment.mView);
788                    anim.start();
789                }
790                fragment.mView.setVisibility(View.VISIBLE);
791            }
792            if (fragment.mAdded && fragment.mHasMenu) {
793                mNeedMenuInvalidate = true;
794            }
795            fragment.onHiddenChanged(false);
796        }
797    }
798
799    public Fragment findFragmentById(int id) {
800        if (mActive != null) {
801            // First look through added fragments.
802            for (int i=mAdded.size()-1; i>=0; i--) {
803                Fragment f = mAdded.get(i);
804                if (f != null && f.mFragmentId == id) {
805                    return f;
806                }
807            }
808            // Now for any known fragment.
809            for (int i=mActive.size()-1; i>=0; i--) {
810                Fragment f = mActive.get(i);
811                if (f != null && f.mFragmentId == id) {
812                    return f;
813                }
814            }
815        }
816        return null;
817    }
818
819    public Fragment findFragmentByTag(String tag) {
820        if (mActive != null && tag != null) {
821            // First look through added fragments.
822            for (int i=mAdded.size()-1; i>=0; i--) {
823                Fragment f = mAdded.get(i);
824                if (f != null && tag.equals(f.mTag)) {
825                    return f;
826                }
827            }
828            // Now for any known fragment.
829            for (int i=mActive.size()-1; i>=0; i--) {
830                Fragment f = mActive.get(i);
831                if (f != null && tag.equals(f.mTag)) {
832                    return f;
833                }
834            }
835        }
836        return null;
837    }
838
839    public Fragment findFragmentByWho(String who) {
840        if (mActive != null && who != null) {
841            for (int i=mActive.size()-1; i>=0; i--) {
842                Fragment f = mActive.get(i);
843                if (f != null && who.equals(f.mWho)) {
844                    return f;
845                }
846            }
847        }
848        return null;
849    }
850
851    public void enqueueAction(Runnable action) {
852        if (mStateSaved) {
853            throw new IllegalStateException(
854                    "Can not perform this action after onSaveInstanceState");
855        }
856        if (mNoTransactionsBecause != null) {
857            throw new IllegalStateException(
858                    "Can not perform this action inside of " + mNoTransactionsBecause);
859        }
860        synchronized (this) {
861            if (mPendingActions == null) {
862                mPendingActions = new ArrayList<Runnable>();
863            }
864            mPendingActions.add(action);
865            if (mPendingActions.size() == 1) {
866                mActivity.mHandler.removeCallbacks(mExecCommit);
867                mActivity.mHandler.post(mExecCommit);
868            }
869        }
870    }
871
872    public int allocBackStackIndex(BackStackRecord bse) {
873        synchronized (this) {
874            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
875                if (mBackStackIndices == null) {
876                    mBackStackIndices = new ArrayList<BackStackRecord>();
877                }
878                int index = mBackStackIndices.size();
879                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
880                mBackStackIndices.add(bse);
881                return index;
882
883            } else {
884                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
885                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
886                mBackStackIndices.set(index, bse);
887                return index;
888            }
889        }
890    }
891
892    public void setBackStackIndex(int index, BackStackRecord bse) {
893        synchronized (this) {
894            if (mBackStackIndices == null) {
895                mBackStackIndices = new ArrayList<BackStackRecord>();
896            }
897            int N = mBackStackIndices.size();
898            if (index < N) {
899                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
900                mBackStackIndices.set(index, bse);
901            } else {
902                while (N < index) {
903                    mBackStackIndices.add(null);
904                    if (mAvailBackStackIndices == null) {
905                        mAvailBackStackIndices = new ArrayList<Integer>();
906                    }
907                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
908                    mAvailBackStackIndices.add(N);
909                    N++;
910                }
911                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
912                mBackStackIndices.add(bse);
913            }
914        }
915    }
916
917    public void freeBackStackIndex(int index) {
918        synchronized (this) {
919            mBackStackIndices.set(index, null);
920            if (mAvailBackStackIndices == null) {
921                mAvailBackStackIndices = new ArrayList<Integer>();
922            }
923            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
924            mAvailBackStackIndices.add(index);
925        }
926    }
927
928    /**
929     * Only call from main thread!
930     */
931    public void execPendingActions() {
932        if (mExecutingActions) {
933            throw new IllegalStateException("Recursive entry to execPendingActions");
934        }
935
936        while (true) {
937            int numActions;
938
939            synchronized (this) {
940                if (mPendingActions == null || mPendingActions.size() == 0) {
941                    return;
942                }
943
944                numActions = mPendingActions.size();
945                if (mTmpActions == null || mTmpActions.length < numActions) {
946                    mTmpActions = new Runnable[numActions];
947                }
948                mPendingActions.toArray(mTmpActions);
949                mPendingActions.clear();
950                mActivity.mHandler.removeCallbacks(mExecCommit);
951            }
952
953            mExecutingActions = true;
954            for (int i=0; i<numActions; i++) {
955                mTmpActions[i].run();
956            }
957            mExecutingActions = false;
958        }
959    }
960
961    void reportBackStackChanged() {
962        if (mBackStackChangeListeners != null) {
963            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
964                mBackStackChangeListeners.get(i).onBackStackChanged();
965            }
966        }
967    }
968
969    void addBackStackState(BackStackRecord state) {
970        if (mBackStack == null) {
971            mBackStack = new ArrayList<BackStackRecord>();
972        }
973        mBackStack.add(state);
974        reportBackStackChanged();
975    }
976
977    boolean popBackStackState(Handler handler, String name, int id, int flags) {
978        if (mBackStack == null) {
979            return false;
980        }
981        if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) {
982            int last = mBackStack.size()-1;
983            if (last < 0) {
984                return false;
985            }
986            final BackStackRecord bss = mBackStack.remove(last);
987            enqueueAction(new Runnable() {
988                public void run() {
989                    if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
990                    bss.popFromBackStack(true);
991                    reportBackStackChanged();
992                }
993            });
994        } else {
995            int index = -1;
996            if (name != null || id >= 0) {
997                // If a name or ID is specified, look for that place in
998                // the stack.
999                index = mBackStack.size()-1;
1000                while (index >= 0) {
1001                    BackStackRecord bss = mBackStack.get(index);
1002                    if (name != null && name.equals(bss.getName())) {
1003                        break;
1004                    }
1005                    if (id >= 0 && id == bss.mIndex) {
1006                        break;
1007                    }
1008                    index--;
1009                }
1010                if (index < 0) {
1011                    return false;
1012                }
1013                if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) {
1014                    index--;
1015                    // Consume all following entries that match.
1016                    while (index >= 0) {
1017                        BackStackRecord bss = mBackStack.get(index);
1018                        if ((name != null && name.equals(bss.getName()))
1019                                || (id >= 0 && id == bss.mIndex)) {
1020                            index--;
1021                            continue;
1022                        }
1023                        break;
1024                    }
1025                }
1026            }
1027            if (index == mBackStack.size()-1) {
1028                return false;
1029            }
1030            final ArrayList<BackStackRecord> states
1031                    = new ArrayList<BackStackRecord>();
1032            for (int i=mBackStack.size()-1; i>index; i--) {
1033                states.add(mBackStack.remove(i));
1034            }
1035            enqueueAction(new Runnable() {
1036                public void run() {
1037                    final int LAST = states.size()-1;
1038                    for (int i=0; i<=LAST; i++) {
1039                        if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
1040                        states.get(i).popFromBackStack(i == LAST);
1041                    }
1042                    reportBackStackChanged();
1043                }
1044            });
1045        }
1046        return true;
1047    }
1048
1049    ArrayList<Fragment> retainNonConfig() {
1050        ArrayList<Fragment> fragments = null;
1051        if (mActive != null) {
1052            for (int i=0; i<mActive.size(); i++) {
1053                Fragment f = mActive.get(i);
1054                if (f != null && f.mRetainInstance) {
1055                    if (fragments == null) {
1056                        fragments = new ArrayList<Fragment>();
1057                    }
1058                    fragments.add(f);
1059                    f.mRetaining = true;
1060                }
1061            }
1062        }
1063        return fragments;
1064    }
1065
1066    void saveFragmentViewState(Fragment f) {
1067        if (f.mView == null) {
1068            return;
1069        }
1070        if (mStateArray == null) {
1071            mStateArray = new SparseArray<Parcelable>();
1072        }
1073        f.mView.saveHierarchyState(mStateArray);
1074        if (mStateArray.size() > 0) {
1075            f.mSavedViewState = mStateArray;
1076            mStateArray = null;
1077        }
1078    }
1079
1080    Parcelable saveAllState() {
1081        mStateSaved = true;
1082
1083        if (mActive == null || mActive.size() <= 0) {
1084            return null;
1085        }
1086
1087        // First collect all active fragments.
1088        int N = mActive.size();
1089        FragmentState[] active = new FragmentState[N];
1090        boolean haveFragments = false;
1091        for (int i=0; i<N; i++) {
1092            Fragment f = mActive.get(i);
1093            if (f != null) {
1094                haveFragments = true;
1095
1096                FragmentState fs = new FragmentState(f);
1097                active[i] = fs;
1098
1099                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
1100                    if (mStateBundle == null) {
1101                        mStateBundle = new Bundle();
1102                    }
1103                    f.onSaveInstanceState(mStateBundle);
1104                    if (!mStateBundle.isEmpty()) {
1105                        fs.mSavedFragmentState = mStateBundle;
1106                        mStateBundle = null;
1107                    }
1108
1109                    if (f.mView != null) {
1110                        saveFragmentViewState(f);
1111                        if (f.mSavedViewState != null) {
1112                            if (fs.mSavedFragmentState == null) {
1113                                fs.mSavedFragmentState = new Bundle();
1114                            }
1115                            fs.mSavedFragmentState.putSparseParcelableArray(
1116                                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
1117                        }
1118                    }
1119
1120                    if (f.mTarget != null) {
1121                        if (fs.mSavedFragmentState == null) {
1122                            fs.mSavedFragmentState = new Bundle();
1123                        }
1124                        putFragment(fs.mSavedFragmentState,
1125                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
1126                        if (f.mTargetRequestCode != 0) {
1127                            fs.mSavedFragmentState.putInt(
1128                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
1129                                    f.mTargetRequestCode);
1130                        }
1131                    }
1132
1133                } else {
1134                    fs.mSavedFragmentState = f.mSavedFragmentState;
1135                }
1136
1137                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
1138                        + fs.mSavedFragmentState);
1139            }
1140        }
1141
1142        if (!haveFragments) {
1143            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
1144            return null;
1145        }
1146
1147        int[] added = null;
1148        BackStackState[] backStack = null;
1149
1150        // Build list of currently added fragments.
1151        if (mAdded != null) {
1152            N = mAdded.size();
1153            if (N > 0) {
1154                added = new int[N];
1155                for (int i=0; i<N; i++) {
1156                    added[i] = mAdded.get(i).mIndex;
1157                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
1158                            + ": " + mAdded.get(i));
1159                }
1160            }
1161        }
1162
1163        // Now save back stack.
1164        if (mBackStack != null) {
1165            N = mBackStack.size();
1166            if (N > 0) {
1167                backStack = new BackStackState[N];
1168                for (int i=0; i<N; i++) {
1169                    backStack[i] = new BackStackState(this, mBackStack.get(i));
1170                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
1171                            + ": " + mBackStack.get(i));
1172                }
1173            }
1174        }
1175
1176        FragmentManagerState fms = new FragmentManagerState();
1177        fms.mActive = active;
1178        fms.mAdded = added;
1179        fms.mBackStack = backStack;
1180        return fms;
1181    }
1182
1183    void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
1184        // If there is no saved state at all, then there can not be
1185        // any nonConfig fragments either, so that is that.
1186        if (state == null) return;
1187        FragmentManagerState fms = (FragmentManagerState)state;
1188        if (fms.mActive == null) return;
1189
1190        // First re-attach any non-config instances we are retaining back
1191        // to their saved state, so we don't try to instantiate them again.
1192        if (nonConfig != null) {
1193            for (int i=0; i<nonConfig.size(); i++) {
1194                Fragment f = nonConfig.get(i);
1195                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
1196                FragmentState fs = fms.mActive[f.mIndex];
1197                fs.mInstance = f;
1198                f.mSavedViewState = null;
1199                f.mBackStackNesting = 0;
1200                f.mInLayout = false;
1201                f.mAdded = false;
1202                if (fs.mSavedFragmentState != null) {
1203                    fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
1204                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
1205                            FragmentManagerImpl.VIEW_STATE_TAG);
1206                }
1207            }
1208        }
1209
1210        // Build the full list of active fragments, instantiating them from
1211        // their saved state.
1212        mActive = new ArrayList<Fragment>(fms.mActive.length);
1213        if (mAvailIndices != null) {
1214            mAvailIndices.clear();
1215        }
1216        for (int i=0; i<fms.mActive.length; i++) {
1217            FragmentState fs = fms.mActive[i];
1218            if (fs != null) {
1219                Fragment f = fs.instantiate(mActivity);
1220                if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f);
1221                mActive.add(f);
1222            } else {
1223                if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)");
1224                mActive.add(null);
1225                if (mAvailIndices == null) {
1226                    mAvailIndices = new ArrayList<Integer>();
1227                }
1228                if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i);
1229                mAvailIndices.add(i);
1230            }
1231        }
1232
1233        // Update the target of all retained fragments.
1234        if (nonConfig != null) {
1235            for (int i=0; i<nonConfig.size(); i++) {
1236                Fragment f = nonConfig.get(i);
1237                if (f.mTarget != null) {
1238                    if (f.mTarget.mIndex < mActive.size()) {
1239                        f.mTarget = mActive.get(f.mTarget.mIndex);
1240                    } else {
1241                        Log.w(TAG, "Re-attaching retained fragment " + f
1242                                + " target no longer exists: " + f.mTarget);
1243                        f.mTarget = null;
1244                    }
1245                }
1246            }
1247        }
1248
1249        // Build the list of currently added fragments.
1250        if (fms.mAdded != null) {
1251            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
1252            for (int i=0; i<fms.mAdded.length; i++) {
1253                Fragment f = mActive.get(fms.mAdded[i]);
1254                if (f == null) {
1255                    throw new IllegalStateException(
1256                            "No instantiated fragment for index #" + fms.mAdded[i]);
1257                }
1258                f.mAdded = true;
1259                f.mImmediateActivity = mActivity;
1260                if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f);
1261                mAdded.add(f);
1262            }
1263        } else {
1264            mAdded = null;
1265        }
1266
1267        // Build the back stack.
1268        if (fms.mBackStack != null) {
1269            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
1270            for (int i=0; i<fms.mBackStack.length; i++) {
1271                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
1272                if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i
1273                        + " (index " + bse.mIndex + "): " + bse);
1274                mBackStack.add(bse);
1275                if (bse.mIndex >= 0) {
1276                    setBackStackIndex(bse.mIndex, bse);
1277                }
1278            }
1279        } else {
1280            mBackStack = null;
1281        }
1282    }
1283
1284    public void attachActivity(Activity activity) {
1285        if (mActivity != null) throw new IllegalStateException();
1286        mActivity = activity;
1287    }
1288
1289    public void noteStateNotSaved() {
1290        mStateSaved = false;
1291    }
1292
1293    public void dispatchCreate() {
1294        mStateSaved = false;
1295        moveToState(Fragment.CREATED, false);
1296    }
1297
1298    public void dispatchActivityCreated() {
1299        mStateSaved = false;
1300        moveToState(Fragment.ACTIVITY_CREATED, false);
1301    }
1302
1303    public void dispatchStart() {
1304        mStateSaved = false;
1305        moveToState(Fragment.STARTED, false);
1306    }
1307
1308    public void dispatchResume() {
1309        mStateSaved = false;
1310        moveToState(Fragment.RESUMED, false);
1311    }
1312
1313    public void dispatchPause() {
1314        moveToState(Fragment.STARTED, false);
1315    }
1316
1317    public void dispatchStop() {
1318        moveToState(Fragment.ACTIVITY_CREATED, false);
1319    }
1320
1321    public void dispatchDestroy() {
1322        moveToState(Fragment.INITIALIZING, false);
1323        mActivity = null;
1324    }
1325
1326    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1327        boolean show = false;
1328        if (mActive != null) {
1329            for (int i=0; i<mAdded.size(); i++) {
1330                Fragment f = mAdded.get(i);
1331                if (f != null && !f.mHidden && f.mHasMenu) {
1332                    show = true;
1333                    f.onCreateOptionsMenu(menu, inflater);
1334                }
1335            }
1336        }
1337        return show;
1338    }
1339
1340    public boolean dispatchPrepareOptionsMenu(Menu menu) {
1341        boolean show = false;
1342        if (mActive != null) {
1343            for (int i=0; i<mAdded.size(); i++) {
1344                Fragment f = mAdded.get(i);
1345                if (f != null && !f.mHidden && f.mHasMenu) {
1346                    show = true;
1347                    f.onPrepareOptionsMenu(menu);
1348                }
1349            }
1350        }
1351        return show;
1352    }
1353
1354    public boolean dispatchOptionsItemSelected(MenuItem item) {
1355        if (mActive != null) {
1356            for (int i=0; i<mAdded.size(); i++) {
1357                Fragment f = mAdded.get(i);
1358                if (f != null && !f.mHidden && f.mHasMenu) {
1359                    if (f.onOptionsItemSelected(item)) {
1360                        return true;
1361                    }
1362                }
1363            }
1364        }
1365        return false;
1366    }
1367
1368    public boolean dispatchContextItemSelected(MenuItem item) {
1369        if (mActive != null) {
1370            for (int i=0; i<mAdded.size(); i++) {
1371                Fragment f = mAdded.get(i);
1372                if (f != null && !f.mHidden) {
1373                    if (f.onContextItemSelected(item)) {
1374                        return true;
1375                    }
1376                }
1377            }
1378        }
1379        return false;
1380    }
1381
1382    public void dispatchOptionsMenuClosed(Menu menu) {
1383        if (mActive != null) {
1384            for (int i=0; i<mAdded.size(); i++) {
1385                Fragment f = mAdded.get(i);
1386                if (f != null && !f.mHidden && f.mHasMenu) {
1387                    f.onOptionsMenuClosed(menu);
1388                }
1389            }
1390        }
1391    }
1392
1393    public static int reverseTransit(int transit) {
1394        int rev = 0;
1395        switch (transit) {
1396            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
1397                rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
1398                break;
1399            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
1400                rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
1401                break;
1402            case FragmentTransaction.TRANSIT_FRAGMENT_NEXT:
1403                rev = FragmentTransaction.TRANSIT_FRAGMENT_PREV;
1404                break;
1405            case FragmentTransaction.TRANSIT_FRAGMENT_PREV:
1406                rev = FragmentTransaction.TRANSIT_FRAGMENT_NEXT;
1407                break;
1408        }
1409        return rev;
1410
1411    }
1412
1413    public static int transitToStyleIndex(int transit, boolean enter) {
1414        int animAttr = -1;
1415        switch (transit) {
1416            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
1417                animAttr = enter
1418                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation
1419                    : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation;
1420                break;
1421            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
1422                animAttr = enter
1423                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation
1424                    : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation;
1425                break;
1426            case FragmentTransaction.TRANSIT_FRAGMENT_NEXT:
1427                animAttr = enter
1428                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentNextEnterAnimation
1429                    : com.android.internal.R.styleable.FragmentAnimation_fragmentNextExitAnimation;
1430                break;
1431            case FragmentTransaction.TRANSIT_FRAGMENT_PREV:
1432                animAttr = enter
1433                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentPrevEnterAnimation
1434                    : com.android.internal.R.styleable.FragmentAnimation_fragmentPrevExitAnimation;
1435                break;
1436        }
1437        return animAttr;
1438    }
1439}
1440