1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v4.app;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.support.v4.util.LogWriter;
22import android.text.TextUtils;
23import android.util.Log;
24
25import java.io.FileDescriptor;
26import java.io.PrintWriter;
27import java.util.ArrayList;
28
29final class BackStackState implements Parcelable {
30    final int[] mOps;
31    final int mTransition;
32    final int mTransitionStyle;
33    final String mName;
34    final int mIndex;
35    final int mBreadCrumbTitleRes;
36    final CharSequence mBreadCrumbTitleText;
37    final int mBreadCrumbShortTitleRes;
38    final CharSequence mBreadCrumbShortTitleText;
39
40    public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
41        int numRemoved = 0;
42        BackStackRecord.Op op = bse.mHead;
43        while (op != null) {
44            if (op.removed != null) numRemoved += op.removed.size();
45            op = op.next;
46        }
47        mOps = new int[bse.mNumOp*7 + numRemoved];
48
49        if (!bse.mAddToBackStack) {
50            throw new IllegalStateException("Not on back stack");
51        }
52
53        op = bse.mHead;
54        int pos = 0;
55        while (op != null) {
56            mOps[pos++] = op.cmd;
57            mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
58            mOps[pos++] = op.enterAnim;
59            mOps[pos++] = op.exitAnim;
60            mOps[pos++] = op.popEnterAnim;
61            mOps[pos++] = op.popExitAnim;
62            if (op.removed != null) {
63                final int N = op.removed.size();
64                mOps[pos++] = N;
65                for (int i=0; i<N; i++) {
66                    mOps[pos++] = op.removed.get(i).mIndex;
67                }
68            } else {
69                mOps[pos++] = 0;
70            }
71            op = op.next;
72        }
73        mTransition = bse.mTransition;
74        mTransitionStyle = bse.mTransitionStyle;
75        mName = bse.mName;
76        mIndex = bse.mIndex;
77        mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
78        mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
79        mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
80        mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
81    }
82
83    public BackStackState(Parcel in) {
84        mOps = in.createIntArray();
85        mTransition = in.readInt();
86        mTransitionStyle = in.readInt();
87        mName = in.readString();
88        mIndex = in.readInt();
89        mBreadCrumbTitleRes = in.readInt();
90        mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
91        mBreadCrumbShortTitleRes = in.readInt();
92        mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
93    }
94
95    public BackStackRecord instantiate(FragmentManagerImpl fm) {
96        BackStackRecord bse = new BackStackRecord(fm);
97        int pos = 0;
98        int num = 0;
99        while (pos < mOps.length) {
100            BackStackRecord.Op op = new BackStackRecord.Op();
101            op.cmd = mOps[pos++];
102            if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
103                    "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
104            int findex = mOps[pos++];
105            if (findex >= 0) {
106                Fragment f = fm.mActive.get(findex);
107                op.fragment = f;
108            } else {
109                op.fragment = null;
110            }
111            op.enterAnim = mOps[pos++];
112            op.exitAnim = mOps[pos++];
113            op.popEnterAnim = mOps[pos++];
114            op.popExitAnim = mOps[pos++];
115            final int N = mOps[pos++];
116            if (N > 0) {
117                op.removed = new ArrayList<Fragment>(N);
118                for (int i=0; i<N; i++) {
119                    if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
120                            "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
121                    Fragment r = fm.mActive.get(mOps[pos++]);
122                    op.removed.add(r);
123                }
124            }
125            bse.addOp(op);
126            num++;
127        }
128        bse.mTransition = mTransition;
129        bse.mTransitionStyle = mTransitionStyle;
130        bse.mName = mName;
131        bse.mIndex = mIndex;
132        bse.mAddToBackStack = true;
133        bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
134        bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
135        bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
136        bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
137        bse.bumpBackStackNesting(1);
138        return bse;
139    }
140
141    public int describeContents() {
142        return 0;
143    }
144
145    public void writeToParcel(Parcel dest, int flags) {
146        dest.writeIntArray(mOps);
147        dest.writeInt(mTransition);
148        dest.writeInt(mTransitionStyle);
149        dest.writeString(mName);
150        dest.writeInt(mIndex);
151        dest.writeInt(mBreadCrumbTitleRes);
152        TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
153        dest.writeInt(mBreadCrumbShortTitleRes);
154        TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
155    }
156
157    public static final Parcelable.Creator<BackStackState> CREATOR
158            = new Parcelable.Creator<BackStackState>() {
159        public BackStackState createFromParcel(Parcel in) {
160            return new BackStackState(in);
161        }
162
163        public BackStackState[] newArray(int size) {
164            return new BackStackState[size];
165        }
166    };
167}
168
169/**
170 * @hide Entry of an operation on the fragment back stack.
171 */
172final class BackStackRecord extends FragmentTransaction implements
173        FragmentManager.BackStackEntry, Runnable {
174    static final String TAG = FragmentManagerImpl.TAG;
175
176    final FragmentManagerImpl mManager;
177
178    static final int OP_NULL = 0;
179    static final int OP_ADD = 1;
180    static final int OP_REPLACE = 2;
181    static final int OP_REMOVE = 3;
182    static final int OP_HIDE = 4;
183    static final int OP_SHOW = 5;
184    static final int OP_DETACH = 6;
185    static final int OP_ATTACH = 7;
186
187    static final class Op {
188        Op next;
189        Op prev;
190        int cmd;
191        Fragment fragment;
192        int enterAnim;
193        int exitAnim;
194        int popEnterAnim;
195        int popExitAnim;
196        ArrayList<Fragment> removed;
197    }
198
199    Op mHead;
200    Op mTail;
201    int mNumOp;
202    int mEnterAnim;
203    int mExitAnim;
204    int mPopEnterAnim;
205    int mPopExitAnim;
206    int mTransition;
207    int mTransitionStyle;
208    boolean mAddToBackStack;
209    boolean mAllowAddToBackStack = true;
210    String mName;
211    boolean mCommitted;
212    int mIndex = -1;
213
214    int mBreadCrumbTitleRes;
215    CharSequence mBreadCrumbTitleText;
216    int mBreadCrumbShortTitleRes;
217    CharSequence mBreadCrumbShortTitleText;
218
219    @Override
220    public String toString() {
221        StringBuilder sb = new StringBuilder(128);
222        sb.append("BackStackEntry{");
223        sb.append(Integer.toHexString(System.identityHashCode(this)));
224        if (mIndex >= 0) {
225            sb.append(" #");
226            sb.append(mIndex);
227        }
228        if (mName != null) {
229            sb.append(" ");
230            sb.append(mName);
231        }
232        sb.append("}");
233        return sb.toString();
234    }
235
236    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
237        dump(prefix, writer, true);
238    }
239
240    public void dump(String prefix, PrintWriter writer, boolean full) {
241        if (full) {
242            writer.print(prefix); writer.print("mName="); writer.print(mName);
243                    writer.print(" mIndex="); writer.print(mIndex);
244                    writer.print(" mCommitted="); writer.println(mCommitted);
245            if (mTransition != FragmentTransaction.TRANSIT_NONE) {
246                writer.print(prefix); writer.print("mTransition=#");
247                        writer.print(Integer.toHexString(mTransition));
248                        writer.print(" mTransitionStyle=#");
249                        writer.println(Integer.toHexString(mTransitionStyle));
250            }
251            if (mEnterAnim != 0 || mExitAnim !=0) {
252                writer.print(prefix); writer.print("mEnterAnim=#");
253                        writer.print(Integer.toHexString(mEnterAnim));
254                        writer.print(" mExitAnim=#");
255                        writer.println(Integer.toHexString(mExitAnim));
256            }
257            if (mPopEnterAnim != 0 || mPopExitAnim !=0) {
258                writer.print(prefix); writer.print("mPopEnterAnim=#");
259                        writer.print(Integer.toHexString(mPopEnterAnim));
260                        writer.print(" mPopExitAnim=#");
261                        writer.println(Integer.toHexString(mPopExitAnim));
262            }
263            if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
264                writer.print(prefix); writer.print("mBreadCrumbTitleRes=#");
265                        writer.print(Integer.toHexString(mBreadCrumbTitleRes));
266                        writer.print(" mBreadCrumbTitleText=");
267                        writer.println(mBreadCrumbTitleText);
268            }
269            if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
270                writer.print(prefix); writer.print("mBreadCrumbShortTitleRes=#");
271                        writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
272                        writer.print(" mBreadCrumbShortTitleText=");
273                        writer.println(mBreadCrumbShortTitleText);
274            }
275        }
276
277        if (mHead != null) {
278            writer.print(prefix); writer.println("Operations:");
279            String innerPrefix = prefix + "    ";
280            Op op = mHead;
281            int num = 0;
282            while (op != null) {
283                String cmdStr;
284                switch (op.cmd) {
285                    case OP_NULL: cmdStr="NULL"; break;
286                    case OP_ADD: cmdStr="ADD"; break;
287                    case OP_REPLACE: cmdStr="REPLACE"; break;
288                    case OP_REMOVE: cmdStr="REMOVE"; break;
289                    case OP_HIDE: cmdStr="HIDE"; break;
290                    case OP_SHOW: cmdStr="SHOW"; break;
291                    case OP_DETACH: cmdStr="DETACH"; break;
292                    case OP_ATTACH: cmdStr="ATTACH"; break;
293                    default: cmdStr="cmd=" + op.cmd; break;
294                }
295                writer.print(prefix); writer.print("  Op #"); writer.print(num);
296                        writer.print(": "); writer.print(cmdStr);
297                        writer.print(" "); writer.println(op.fragment);
298                if (full) {
299                    if (op.enterAnim != 0 || op.exitAnim != 0) {
300                        writer.print(prefix); writer.print("enterAnim=#");
301                                writer.print(Integer.toHexString(op.enterAnim));
302                                writer.print(" exitAnim=#");
303                                writer.println(Integer.toHexString(op.exitAnim));
304                    }
305                    if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
306                        writer.print(prefix); writer.print("popEnterAnim=#");
307                                writer.print(Integer.toHexString(op.popEnterAnim));
308                                writer.print(" popExitAnim=#");
309                                writer.println(Integer.toHexString(op.popExitAnim));
310                    }
311                }
312                if (op.removed != null && op.removed.size() > 0) {
313                    for (int i=0; i<op.removed.size(); i++) {
314                        writer.print(innerPrefix);
315                        if (op.removed.size() == 1) {
316                            writer.print("Removed: ");
317                        } else {
318                            if (i == 0) {
319                                writer.println("Removed:");
320                            }
321                            writer.print(innerPrefix); writer.print("  #"); writer.print(i);
322                                    writer.print(": ");
323                        }
324                        writer.println(op.removed.get(i));
325                    }
326                }
327                op = op.next;
328                num++;
329            }
330        }
331    }
332
333    public BackStackRecord(FragmentManagerImpl manager) {
334        mManager = manager;
335    }
336
337    public int getId() {
338        return mIndex;
339    }
340
341    public int getBreadCrumbTitleRes() {
342        return mBreadCrumbTitleRes;
343    }
344
345    public int getBreadCrumbShortTitleRes() {
346        return mBreadCrumbShortTitleRes;
347    }
348
349    public CharSequence getBreadCrumbTitle() {
350        if (mBreadCrumbTitleRes != 0) {
351            return mManager.mActivity.getText(mBreadCrumbTitleRes);
352        }
353        return mBreadCrumbTitleText;
354    }
355
356    public CharSequence getBreadCrumbShortTitle() {
357        if (mBreadCrumbShortTitleRes != 0) {
358            return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
359        }
360        return mBreadCrumbShortTitleText;
361    }
362
363    void addOp(Op op) {
364        if (mHead == null) {
365            mHead = mTail = op;
366        } else {
367            op.prev = mTail;
368            mTail.next = op;
369            mTail = op;
370        }
371        op.enterAnim = mEnterAnim;
372        op.exitAnim = mExitAnim;
373        op.popEnterAnim = mPopEnterAnim;
374        op.popExitAnim = mPopExitAnim;
375        mNumOp++;
376    }
377
378    public FragmentTransaction add(Fragment fragment, String tag) {
379        doAddOp(0, fragment, tag, OP_ADD);
380        return this;
381    }
382
383    public FragmentTransaction add(int containerViewId, Fragment fragment) {
384        doAddOp(containerViewId, fragment, null, OP_ADD);
385        return this;
386    }
387
388    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
389        doAddOp(containerViewId, fragment, tag, OP_ADD);
390        return this;
391    }
392
393    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
394        fragment.mFragmentManager = mManager;
395
396        if (tag != null) {
397            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
398                throw new IllegalStateException("Can't change tag of fragment "
399                        + fragment + ": was " + fragment.mTag
400                        + " now " + tag);
401            }
402            fragment.mTag = tag;
403        }
404
405        if (containerViewId != 0) {
406            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
407                throw new IllegalStateException("Can't change container ID of fragment "
408                        + fragment + ": was " + fragment.mFragmentId
409                        + " now " + containerViewId);
410            }
411            fragment.mContainerId = fragment.mFragmentId = containerViewId;
412        }
413
414        Op op = new Op();
415        op.cmd = opcmd;
416        op.fragment = fragment;
417        addOp(op);
418    }
419
420    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
421        return replace(containerViewId, fragment, null);
422    }
423
424    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
425        if (containerViewId == 0) {
426            throw new IllegalArgumentException("Must use non-zero containerViewId");
427        }
428
429        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
430        return this;
431    }
432
433    public FragmentTransaction remove(Fragment fragment) {
434        Op op = new Op();
435        op.cmd = OP_REMOVE;
436        op.fragment = fragment;
437        addOp(op);
438
439        return this;
440    }
441
442    public FragmentTransaction hide(Fragment fragment) {
443        Op op = new Op();
444        op.cmd = OP_HIDE;
445        op.fragment = fragment;
446        addOp(op);
447
448        return this;
449    }
450
451    public FragmentTransaction show(Fragment fragment) {
452        Op op = new Op();
453        op.cmd = OP_SHOW;
454        op.fragment = fragment;
455        addOp(op);
456
457        return this;
458    }
459
460    public FragmentTransaction detach(Fragment fragment) {
461        Op op = new Op();
462        op.cmd = OP_DETACH;
463        op.fragment = fragment;
464        addOp(op);
465
466        return this;
467    }
468
469    public FragmentTransaction attach(Fragment fragment) {
470        Op op = new Op();
471        op.cmd = OP_ATTACH;
472        op.fragment = fragment;
473        addOp(op);
474
475        return this;
476    }
477
478    public FragmentTransaction setCustomAnimations(int enter, int exit) {
479        return setCustomAnimations(enter, exit, 0, 0);
480    }
481
482    public FragmentTransaction setCustomAnimations(int enter, int exit,
483            int popEnter, int popExit) {
484        mEnterAnim = enter;
485        mExitAnim = exit;
486        mPopEnterAnim = popEnter;
487        mPopExitAnim = popExit;
488        return this;
489    }
490
491    public FragmentTransaction setTransition(int transition) {
492        mTransition = transition;
493        return this;
494    }
495
496    public FragmentTransaction setTransitionStyle(int styleRes) {
497        mTransitionStyle = styleRes;
498        return this;
499    }
500
501    public FragmentTransaction addToBackStack(String name) {
502        if (!mAllowAddToBackStack) {
503            throw new IllegalStateException(
504                    "This FragmentTransaction is not allowed to be added to the back stack.");
505        }
506        mAddToBackStack = true;
507        mName = name;
508        return this;
509    }
510
511    public boolean isAddToBackStackAllowed() {
512        return mAllowAddToBackStack;
513    }
514
515    public FragmentTransaction disallowAddToBackStack() {
516        if (mAddToBackStack) {
517            throw new IllegalStateException(
518                    "This transaction is already being added to the back stack");
519        }
520        mAllowAddToBackStack = false;
521        return this;
522    }
523
524    public FragmentTransaction setBreadCrumbTitle(int res) {
525        mBreadCrumbTitleRes = res;
526        mBreadCrumbTitleText = null;
527        return this;
528    }
529
530    public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
531        mBreadCrumbTitleRes = 0;
532        mBreadCrumbTitleText = text;
533        return this;
534    }
535
536    public FragmentTransaction setBreadCrumbShortTitle(int res) {
537        mBreadCrumbShortTitleRes = res;
538        mBreadCrumbShortTitleText = null;
539        return this;
540    }
541
542    public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
543        mBreadCrumbShortTitleRes = 0;
544        mBreadCrumbShortTitleText = text;
545        return this;
546    }
547
548    void bumpBackStackNesting(int amt) {
549        if (!mAddToBackStack) {
550            return;
551        }
552        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
553                + " by " + amt);
554        Op op = mHead;
555        while (op != null) {
556            if (op.fragment != null) {
557                op.fragment.mBackStackNesting += amt;
558                if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
559                        + op.fragment + " to " + op.fragment.mBackStackNesting);
560            }
561            if (op.removed != null) {
562                for (int i=op.removed.size()-1; i>=0; i--) {
563                    Fragment r = op.removed.get(i);
564                    r.mBackStackNesting += amt;
565                    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
566                            + r + " to " + r.mBackStackNesting);
567                }
568            }
569            op = op.next;
570        }
571    }
572
573    public int commit() {
574        return commitInternal(false);
575    }
576
577    public int commitAllowingStateLoss() {
578        return commitInternal(true);
579    }
580
581    int commitInternal(boolean allowStateLoss) {
582        if (mCommitted) throw new IllegalStateException("commit already called");
583        if (FragmentManagerImpl.DEBUG) {
584            Log.v(TAG, "Commit: " + this);
585            LogWriter logw = new LogWriter(TAG);
586            PrintWriter pw = new PrintWriter(logw);
587            dump("  ", null, pw, null);
588        }
589        mCommitted = true;
590        if (mAddToBackStack) {
591            mIndex = mManager.allocBackStackIndex(this);
592        } else {
593            mIndex = -1;
594        }
595        mManager.enqueueAction(this, allowStateLoss);
596        return mIndex;
597    }
598
599    public void run() {
600        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
601
602        if (mAddToBackStack) {
603            if (mIndex < 0) {
604                throw new IllegalStateException("addToBackStack() called after commit()");
605            }
606        }
607
608        bumpBackStackNesting(1);
609
610        Op op = mHead;
611        while (op != null) {
612            switch (op.cmd) {
613                case OP_ADD: {
614                    Fragment f = op.fragment;
615                    f.mNextAnim = op.enterAnim;
616                    mManager.addFragment(f, false);
617                } break;
618                case OP_REPLACE: {
619                    Fragment f = op.fragment;
620                    if (mManager.mAdded != null) {
621                        for (int i=0; i<mManager.mAdded.size(); i++) {
622                            Fragment old = mManager.mAdded.get(i);
623                            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
624                                    "OP_REPLACE: adding=" + f + " old=" + old);
625                            if (f == null || old.mContainerId == f.mContainerId) {
626                                if (old == f) {
627                                    op.fragment = f = null;
628                                } else {
629                                    if (op.removed == null) {
630                                        op.removed = new ArrayList<Fragment>();
631                                    }
632                                    op.removed.add(old);
633                                    old.mNextAnim = op.exitAnim;
634                                    if (mAddToBackStack) {
635                                        old.mBackStackNesting += 1;
636                                        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
637                                                + old + " to " + old.mBackStackNesting);
638                                    }
639                                    mManager.removeFragment(old, mTransition, mTransitionStyle);
640                                }
641                            }
642                        }
643                    }
644                    if (f != null) {
645                        f.mNextAnim = op.enterAnim;
646                        mManager.addFragment(f, false);
647                    }
648                } break;
649                case OP_REMOVE: {
650                    Fragment f = op.fragment;
651                    f.mNextAnim = op.exitAnim;
652                    mManager.removeFragment(f, mTransition, mTransitionStyle);
653                } break;
654                case OP_HIDE: {
655                    Fragment f = op.fragment;
656                    f.mNextAnim = op.exitAnim;
657                    mManager.hideFragment(f, mTransition, mTransitionStyle);
658                } break;
659                case OP_SHOW: {
660                    Fragment f = op.fragment;
661                    f.mNextAnim = op.enterAnim;
662                    mManager.showFragment(f, mTransition, mTransitionStyle);
663                } break;
664                case OP_DETACH: {
665                    Fragment f = op.fragment;
666                    f.mNextAnim = op.exitAnim;
667                    mManager.detachFragment(f, mTransition, mTransitionStyle);
668                } break;
669                case OP_ATTACH: {
670                    Fragment f = op.fragment;
671                    f.mNextAnim = op.enterAnim;
672                    mManager.attachFragment(f, mTransition, mTransitionStyle);
673                } break;
674                default: {
675                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
676                }
677            }
678
679            op = op.next;
680        }
681
682        mManager.moveToState(mManager.mCurState, mTransition,
683                mTransitionStyle, true);
684
685        if (mAddToBackStack) {
686            mManager.addBackStackState(this);
687        }
688    }
689
690    public void popFromBackStack(boolean doStateMove) {
691        if (FragmentManagerImpl.DEBUG) {
692            Log.v(TAG, "popFromBackStack: " + this);
693            LogWriter logw = new LogWriter(TAG);
694            PrintWriter pw = new PrintWriter(logw);
695            dump("  ", null, pw, null);
696        }
697
698        bumpBackStackNesting(-1);
699
700        Op op = mTail;
701        while (op != null) {
702            switch (op.cmd) {
703                case OP_ADD: {
704                    Fragment f = op.fragment;
705                    f.mNextAnim = op.popExitAnim;
706                    mManager.removeFragment(f,
707                            FragmentManagerImpl.reverseTransit(mTransition),
708                            mTransitionStyle);
709                } break;
710                case OP_REPLACE: {
711                    Fragment f = op.fragment;
712                    if (f != null) {
713                        f.mNextAnim = op.popExitAnim;
714                        mManager.removeFragment(f,
715                                FragmentManagerImpl.reverseTransit(mTransition),
716                                mTransitionStyle);
717                    }
718                    if (op.removed != null) {
719                        for (int i=0; i<op.removed.size(); i++) {
720                            Fragment old = op.removed.get(i);
721                            old.mNextAnim = op.popEnterAnim;
722                            mManager.addFragment(old, false);
723                        }
724                    }
725                } break;
726                case OP_REMOVE: {
727                    Fragment f = op.fragment;
728                    f.mNextAnim = op.popEnterAnim;
729                    mManager.addFragment(f, false);
730                } break;
731                case OP_HIDE: {
732                    Fragment f = op.fragment;
733                    f.mNextAnim = op.popEnterAnim;
734                    mManager.showFragment(f,
735                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
736                } break;
737                case OP_SHOW: {
738                    Fragment f = op.fragment;
739                    f.mNextAnim = op.popExitAnim;
740                    mManager.hideFragment(f,
741                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
742                } break;
743                case OP_DETACH: {
744                    Fragment f = op.fragment;
745                    f.mNextAnim = op.popEnterAnim;
746                    mManager.attachFragment(f,
747                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
748                } break;
749                case OP_ATTACH: {
750                    Fragment f = op.fragment;
751                    f.mNextAnim = op.popEnterAnim;
752                    mManager.detachFragment(f,
753                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
754                } break;
755                default: {
756                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
757                }
758            }
759
760            op = op.prev;
761        }
762
763        if (doStateMove) {
764            mManager.moveToState(mManager.mCurState,
765                    FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
766        }
767
768        if (mIndex >= 0) {
769            mManager.freeBackStackIndex(mIndex);
770            mIndex = -1;
771        }
772    }
773
774    public String getName() {
775        return mName;
776    }
777
778    public int getTransition() {
779        return mTransition;
780    }
781
782    public int getTransitionStyle() {
783        return mTransitionStyle;
784    }
785
786    public boolean isEmpty() {
787        return mNumOp == 0;
788    }
789}
790