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