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