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.Build;
     20 import android.os.Parcel;
     21 import android.os.Parcelable;
     22 import android.text.TextUtils;
     23 import android.util.Log;
     24 import android.util.LogWriter;
     25 import android.view.View;
     26 
     27 import com.android.internal.util.FastPrintWriter;
     28 
     29 import java.io.FileDescriptor;
     30 import java.io.PrintWriter;
     31 import java.lang.reflect.Modifier;
     32 import java.util.ArrayList;
     33 
     34 final class BackStackState implements Parcelable {
     35     final int[] mOps;
     36     final int mTransition;
     37     final int mTransitionStyle;
     38     final String mName;
     39     final int mIndex;
     40     final int mBreadCrumbTitleRes;
     41     final CharSequence mBreadCrumbTitleText;
     42     final int mBreadCrumbShortTitleRes;
     43     final CharSequence mBreadCrumbShortTitleText;
     44     final ArrayList<String> mSharedElementSourceNames;
     45     final ArrayList<String> mSharedElementTargetNames;
     46     final boolean mReorderingAllowed;
     47 
     48     public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
     49         final int numOps = bse.mOps.size();
     50         mOps = new int[numOps * 6];
     51 
     52         if (!bse.mAddToBackStack) {
     53             throw new IllegalStateException("Not on back stack");
     54         }
     55 
     56         int pos = 0;
     57         for (int opNum = 0; opNum < numOps; opNum++) {
     58             final BackStackRecord.Op op = bse.mOps.get(opNum);
     59             mOps[pos++] = op.cmd;
     60             mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
     61             mOps[pos++] = op.enterAnim;
     62             mOps[pos++] = op.exitAnim;
     63             mOps[pos++] = op.popEnterAnim;
     64             mOps[pos++] = op.popExitAnim;
     65         }
     66         mTransition = bse.mTransition;
     67         mTransitionStyle = bse.mTransitionStyle;
     68         mName = bse.mName;
     69         mIndex = bse.mIndex;
     70         mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
     71         mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
     72         mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
     73         mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
     74         mSharedElementSourceNames = bse.mSharedElementSourceNames;
     75         mSharedElementTargetNames = bse.mSharedElementTargetNames;
     76         mReorderingAllowed = bse.mReorderingAllowed;
     77     }
     78 
     79     public BackStackState(Parcel in) {
     80         mOps = in.createIntArray();
     81         mTransition = in.readInt();
     82         mTransitionStyle = in.readInt();
     83         mName = in.readString();
     84         mIndex = in.readInt();
     85         mBreadCrumbTitleRes = in.readInt();
     86         mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
     87         mBreadCrumbShortTitleRes = in.readInt();
     88         mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
     89         mSharedElementSourceNames = in.createStringArrayList();
     90         mSharedElementTargetNames = in.createStringArrayList();
     91         mReorderingAllowed = in.readInt() != 0;
     92     }
     93 
     94     public BackStackRecord instantiate(FragmentManagerImpl fm) {
     95         BackStackRecord bse = new BackStackRecord(fm);
     96         int pos = 0;
     97         int num = 0;
     98         while (pos < mOps.length) {
     99             BackStackRecord.Op op = new BackStackRecord.Op();
    100             op.cmd = mOps[pos++];
    101             if (FragmentManagerImpl.DEBUG) {
    102                 Log.v(FragmentManagerImpl.TAG,
    103                         "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
    104             }
    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             bse.mEnterAnim = op.enterAnim;
    117             bse.mExitAnim = op.exitAnim;
    118             bse.mPopEnterAnim = op.popEnterAnim;
    119             bse.mPopExitAnim = op.popExitAnim;
    120             bse.addOp(op);
    121             num++;
    122         }
    123         bse.mTransition = mTransition;
    124         bse.mTransitionStyle = mTransitionStyle;
    125         bse.mName = mName;
    126         bse.mIndex = mIndex;
    127         bse.mAddToBackStack = true;
    128         bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
    129         bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
    130         bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
    131         bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
    132         bse.mSharedElementSourceNames = mSharedElementSourceNames;
    133         bse.mSharedElementTargetNames = mSharedElementTargetNames;
    134         bse.mReorderingAllowed = mReorderingAllowed;
    135         bse.bumpBackStackNesting(1);
    136         return bse;
    137     }
    138 
    139     public int describeContents() {
    140         return 0;
    141     }
    142 
    143     public void writeToParcel(Parcel dest, int flags) {
    144         dest.writeIntArray(mOps);
    145         dest.writeInt(mTransition);
    146         dest.writeInt(mTransitionStyle);
    147         dest.writeString(mName);
    148         dest.writeInt(mIndex);
    149         dest.writeInt(mBreadCrumbTitleRes);
    150         TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
    151         dest.writeInt(mBreadCrumbShortTitleRes);
    152         TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
    153         dest.writeStringList(mSharedElementSourceNames);
    154         dest.writeStringList(mSharedElementTargetNames);
    155         dest.writeInt(mReorderingAllowed ? 1 : 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  */
    173 final class BackStackRecord extends FragmentTransaction implements
    174         FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
    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     static final int OP_SET_PRIMARY_NAV = 8;
    188     static final int OP_UNSET_PRIMARY_NAV = 9;
    189 
    190     static final class Op {
    191         int cmd;
    192         Fragment fragment;
    193         int enterAnim;
    194         int exitAnim;
    195         int popEnterAnim;
    196         int popExitAnim;
    197 
    198         Op() {
    199         }
    200 
    201         Op(int cmd, Fragment fragment) {
    202             this.cmd = cmd;
    203             this.fragment = fragment;
    204         }
    205     }
    206 
    207     ArrayList<Op> mOps = new ArrayList<>();
    208     int mEnterAnim;
    209     int mExitAnim;
    210     int mPopEnterAnim;
    211     int mPopExitAnim;
    212     int mTransition;
    213     int mTransitionStyle;
    214     boolean mAddToBackStack;
    215     boolean mAllowAddToBackStack = true;
    216     String mName;
    217     boolean mCommitted;
    218     int mIndex = -1;
    219     boolean mReorderingAllowed;
    220 
    221     ArrayList<Runnable> mCommitRunnables;
    222 
    223     int mBreadCrumbTitleRes;
    224     CharSequence mBreadCrumbTitleText;
    225     int mBreadCrumbShortTitleRes;
    226     CharSequence mBreadCrumbShortTitleText;
    227 
    228     ArrayList<String> mSharedElementSourceNames;
    229     ArrayList<String> mSharedElementTargetNames;
    230 
    231     @Override
    232     public String toString() {
    233         StringBuilder sb = new StringBuilder(128);
    234         sb.append("BackStackEntry{");
    235         sb.append(Integer.toHexString(System.identityHashCode(this)));
    236         if (mIndex >= 0) {
    237             sb.append(" #");
    238             sb.append(mIndex);
    239         }
    240         if (mName != null) {
    241             sb.append(" ");
    242             sb.append(mName);
    243         }
    244         sb.append("}");
    245         return sb.toString();
    246     }
    247 
    248     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
    249         dump(prefix, writer, true);
    250     }
    251 
    252     void dump(String prefix, PrintWriter writer, boolean full) {
    253         if (full) {
    254             writer.print(prefix);
    255             writer.print("mName=");
    256             writer.print(mName);
    257             writer.print(" mIndex=");
    258             writer.print(mIndex);
    259             writer.print(" mCommitted=");
    260             writer.println(mCommitted);
    261             if (mTransition != FragmentTransaction.TRANSIT_NONE) {
    262                 writer.print(prefix);
    263                 writer.print("mTransition=#");
    264                 writer.print(Integer.toHexString(mTransition));
    265                 writer.print(" mTransitionStyle=#");
    266                 writer.println(Integer.toHexString(mTransitionStyle));
    267             }
    268             if (mEnterAnim != 0 || mExitAnim != 0) {
    269                 writer.print(prefix);
    270                 writer.print("mEnterAnim=#");
    271                 writer.print(Integer.toHexString(mEnterAnim));
    272                 writer.print(" mExitAnim=#");
    273                 writer.println(Integer.toHexString(mExitAnim));
    274             }
    275             if (mPopEnterAnim != 0 || mPopExitAnim != 0) {
    276                 writer.print(prefix);
    277                 writer.print("mPopEnterAnim=#");
    278                 writer.print(Integer.toHexString(mPopEnterAnim));
    279                 writer.print(" mPopExitAnim=#");
    280                 writer.println(Integer.toHexString(mPopExitAnim));
    281             }
    282             if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
    283                 writer.print(prefix);
    284                 writer.print("mBreadCrumbTitleRes=#");
    285                 writer.print(Integer.toHexString(mBreadCrumbTitleRes));
    286                 writer.print(" mBreadCrumbTitleText=");
    287                 writer.println(mBreadCrumbTitleText);
    288             }
    289             if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
    290                 writer.print(prefix);
    291                 writer.print("mBreadCrumbShortTitleRes=#");
    292                 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
    293                 writer.print(" mBreadCrumbShortTitleText=");
    294                 writer.println(mBreadCrumbShortTitleText);
    295             }
    296         }
    297 
    298         if (!mOps.isEmpty()) {
    299             writer.print(prefix);
    300             writer.println("Operations:");
    301             String innerPrefix = prefix + "    ";
    302             final int numOps = mOps.size();
    303             for (int opNum = 0; opNum < numOps; opNum++) {
    304                 final Op op = mOps.get(opNum);
    305                 String cmdStr;
    306                 switch (op.cmd) {
    307                     case OP_NULL:
    308                         cmdStr = "NULL";
    309                         break;
    310                     case OP_ADD:
    311                         cmdStr = "ADD";
    312                         break;
    313                     case OP_REPLACE:
    314                         cmdStr = "REPLACE";
    315                         break;
    316                     case OP_REMOVE:
    317                         cmdStr = "REMOVE";
    318                         break;
    319                     case OP_HIDE:
    320                         cmdStr = "HIDE";
    321                         break;
    322                     case OP_SHOW:
    323                         cmdStr = "SHOW";
    324                         break;
    325                     case OP_DETACH:
    326                         cmdStr = "DETACH";
    327                         break;
    328                     case OP_ATTACH:
    329                         cmdStr = "ATTACH";
    330                         break;
    331                     case OP_SET_PRIMARY_NAV:
    332                         cmdStr="SET_PRIMARY_NAV";
    333                         break;
    334                     case OP_UNSET_PRIMARY_NAV:
    335                         cmdStr="UNSET_PRIMARY_NAV";
    336                         break;
    337 
    338                     default:
    339                         cmdStr = "cmd=" + op.cmd;
    340                         break;
    341                 }
    342                 writer.print(prefix);
    343                 writer.print("  Op #");
    344                 writer.print(opNum);
    345                 writer.print(": ");
    346                 writer.print(cmdStr);
    347                 writer.print(" ");
    348                 writer.println(op.fragment);
    349                 if (full) {
    350                     if (op.enterAnim != 0 || op.exitAnim != 0) {
    351                         writer.print(innerPrefix);
    352                         writer.print("enterAnim=#");
    353                         writer.print(Integer.toHexString(op.enterAnim));
    354                         writer.print(" exitAnim=#");
    355                         writer.println(Integer.toHexString(op.exitAnim));
    356                     }
    357                     if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
    358                         writer.print(innerPrefix);
    359                         writer.print("popEnterAnim=#");
    360                         writer.print(Integer.toHexString(op.popEnterAnim));
    361                         writer.print(" popExitAnim=#");
    362                         writer.println(Integer.toHexString(op.popExitAnim));
    363                     }
    364                 }
    365             }
    366         }
    367     }
    368 
    369     public BackStackRecord(FragmentManagerImpl manager) {
    370         mManager = manager;
    371         mReorderingAllowed = mManager.getTargetSdk() > Build.VERSION_CODES.N_MR1;
    372     }
    373 
    374     public int getId() {
    375         return mIndex;
    376     }
    377 
    378     public int getBreadCrumbTitleRes() {
    379         return mBreadCrumbTitleRes;
    380     }
    381 
    382     public int getBreadCrumbShortTitleRes() {
    383         return mBreadCrumbShortTitleRes;
    384     }
    385 
    386     public CharSequence getBreadCrumbTitle() {
    387         if (mBreadCrumbTitleRes != 0 && mManager.mHost != null) {
    388             return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
    389         }
    390         return mBreadCrumbTitleText;
    391     }
    392 
    393     public CharSequence getBreadCrumbShortTitle() {
    394         if (mBreadCrumbShortTitleRes != 0 && mManager.mHost != null) {
    395             return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
    396         }
    397         return mBreadCrumbShortTitleText;
    398     }
    399 
    400     void addOp(Op op) {
    401         mOps.add(op);
    402         op.enterAnim = mEnterAnim;
    403         op.exitAnim = mExitAnim;
    404         op.popEnterAnim = mPopEnterAnim;
    405         op.popExitAnim = mPopExitAnim;
    406     }
    407 
    408     public FragmentTransaction add(Fragment fragment, String tag) {
    409         doAddOp(0, fragment, tag, OP_ADD);
    410         return this;
    411     }
    412 
    413     public FragmentTransaction add(int containerViewId, Fragment fragment) {
    414         doAddOp(containerViewId, fragment, null, OP_ADD);
    415         return this;
    416     }
    417 
    418     public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
    419         doAddOp(containerViewId, fragment, tag, OP_ADD);
    420         return this;
    421     }
    422 
    423     private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    424         if (mManager.getTargetSdk() > Build.VERSION_CODES.N_MR1) {
    425             final Class fragmentClass = fragment.getClass();
    426             final int modifiers = fragmentClass.getModifiers();
    427             if ((fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
    428                     || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers)))) {
    429                 throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
    430                         + " must be a public static class to be  properly recreated from"
    431                         + " instance state.");
    432             }
    433         }
    434         fragment.mFragmentManager = mManager;
    435 
    436         if (tag != null) {
    437             if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
    438                 throw new IllegalStateException("Can't change tag of fragment "
    439                         + fragment + ": was " + fragment.mTag
    440                         + " now " + tag);
    441             }
    442             fragment.mTag = tag;
    443         }
    444 
    445         if (containerViewId != 0) {
    446             if (containerViewId == View.NO_ID) {
    447                 throw new IllegalArgumentException("Can't add fragment "
    448                         + fragment + " with tag " + tag + " to container view with no id");
    449             }
    450             if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
    451                 throw new IllegalStateException("Can't change container ID of fragment "
    452                         + fragment + ": was " + fragment.mFragmentId
    453                         + " now " + containerViewId);
    454             }
    455             fragment.mContainerId = fragment.mFragmentId = containerViewId;
    456         }
    457 
    458         addOp(new Op(opcmd, fragment));
    459     }
    460 
    461     public FragmentTransaction replace(int containerViewId, Fragment fragment) {
    462         return replace(containerViewId, fragment, null);
    463     }
    464 
    465     public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
    466         if (containerViewId == 0) {
    467             throw new IllegalArgumentException("Must use non-zero containerViewId");
    468         }
    469 
    470         doAddOp(containerViewId, fragment, tag, OP_REPLACE);
    471         return this;
    472     }
    473 
    474     public FragmentTransaction remove(Fragment fragment) {
    475         addOp(new Op(OP_REMOVE, fragment));
    476 
    477         return this;
    478     }
    479 
    480     public FragmentTransaction hide(Fragment fragment) {
    481         addOp(new Op(OP_HIDE, fragment));
    482 
    483         return this;
    484     }
    485 
    486     public FragmentTransaction show(Fragment fragment) {
    487         addOp(new Op(OP_SHOW, fragment));
    488 
    489         return this;
    490     }
    491 
    492     public FragmentTransaction detach(Fragment fragment) {
    493         addOp(new Op(OP_DETACH, fragment));
    494 
    495         return this;
    496     }
    497 
    498     public FragmentTransaction attach(Fragment fragment) {
    499         addOp(new Op(OP_ATTACH, fragment));
    500 
    501         return this;
    502     }
    503 
    504     public FragmentTransaction setPrimaryNavigationFragment(Fragment fragment) {
    505         addOp(new Op(OP_SET_PRIMARY_NAV, fragment));
    506 
    507         return this;
    508     }
    509 
    510     public FragmentTransaction setCustomAnimations(int enter, int exit) {
    511         return setCustomAnimations(enter, exit, 0, 0);
    512     }
    513 
    514     public FragmentTransaction setCustomAnimations(int enter, int exit,
    515             int popEnter, int popExit) {
    516         mEnterAnim = enter;
    517         mExitAnim = exit;
    518         mPopEnterAnim = popEnter;
    519         mPopExitAnim = popExit;
    520         return this;
    521     }
    522 
    523     public FragmentTransaction setTransition(int transition) {
    524         mTransition = transition;
    525         return this;
    526     }
    527 
    528     @Override
    529     public FragmentTransaction addSharedElement(View sharedElement, String name) {
    530         String transitionName = sharedElement.getTransitionName();
    531         if (transitionName == null) {
    532             throw new IllegalArgumentException("Unique transitionNames are required for all" +
    533                     " sharedElements");
    534         }
    535         if (mSharedElementSourceNames == null) {
    536             mSharedElementSourceNames = new ArrayList<String>();
    537             mSharedElementTargetNames = new ArrayList<String>();
    538         } else if (mSharedElementTargetNames.contains(name)) {
    539             throw new IllegalArgumentException("A shared element with the target name '"
    540                     + name + "' has already been added to the transaction.");
    541         } else if (mSharedElementSourceNames.contains(transitionName)) {
    542             throw new IllegalArgumentException("A shared element with the source name '"
    543                     + transitionName + " has already been added to the transaction.");
    544         }
    545         mSharedElementSourceNames.add(transitionName);
    546         mSharedElementTargetNames.add(name);
    547         return this;
    548     }
    549 
    550     public FragmentTransaction setTransitionStyle(int styleRes) {
    551         mTransitionStyle = styleRes;
    552         return this;
    553     }
    554 
    555     public FragmentTransaction addToBackStack(String name) {
    556         if (!mAllowAddToBackStack) {
    557             throw new IllegalStateException(
    558                     "This FragmentTransaction is not allowed to be added to the back stack.");
    559         }
    560         mAddToBackStack = true;
    561         mName = name;
    562         return this;
    563     }
    564 
    565     public boolean isAddToBackStackAllowed() {
    566         return mAllowAddToBackStack;
    567     }
    568 
    569     public FragmentTransaction disallowAddToBackStack() {
    570         if (mAddToBackStack) {
    571             throw new IllegalStateException(
    572                     "This transaction is already being added to the back stack");
    573         }
    574         mAllowAddToBackStack = false;
    575         return this;
    576     }
    577 
    578     public FragmentTransaction setBreadCrumbTitle(int res) {
    579         mBreadCrumbTitleRes = res;
    580         mBreadCrumbTitleText = null;
    581         return this;
    582     }
    583 
    584     public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
    585         mBreadCrumbTitleRes = 0;
    586         mBreadCrumbTitleText = text;
    587         return this;
    588     }
    589 
    590     public FragmentTransaction setBreadCrumbShortTitle(int res) {
    591         mBreadCrumbShortTitleRes = res;
    592         mBreadCrumbShortTitleText = null;
    593         return this;
    594     }
    595 
    596     public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
    597         mBreadCrumbShortTitleRes = 0;
    598         mBreadCrumbShortTitleText = text;
    599         return this;
    600     }
    601 
    602     void bumpBackStackNesting(int amt) {
    603         if (!mAddToBackStack) {
    604             return;
    605         }
    606         if (FragmentManagerImpl.DEBUG) {
    607             Log.v(TAG, "Bump nesting in " + this
    608                     + " by " + amt);
    609         }
    610         final int numOps = mOps.size();
    611         for (int opNum = 0; opNum < numOps; opNum++) {
    612             final Op op = mOps.get(opNum);
    613             if (op.fragment != null) {
    614                 op.fragment.mBackStackNesting += amt;
    615                 if (FragmentManagerImpl.DEBUG) {
    616                     Log.v(TAG, "Bump nesting of "
    617                             + op.fragment + " to " + op.fragment.mBackStackNesting);
    618                 }
    619             }
    620         }
    621     }
    622 
    623     @Override
    624     public FragmentTransaction runOnCommit(Runnable runnable) {
    625         if (runnable == null) {
    626             throw new IllegalArgumentException("runnable cannot be null");
    627         }
    628         disallowAddToBackStack();
    629         if (mCommitRunnables == null) {
    630             mCommitRunnables = new ArrayList<>();
    631         }
    632         mCommitRunnables.add(runnable);
    633         return this;
    634     }
    635 
    636     public void runOnCommitRunnables() {
    637         if (mCommitRunnables != null) {
    638             for (int i = 0, N = mCommitRunnables.size(); i < N; i++) {
    639                 mCommitRunnables.get(i).run();
    640             }
    641             mCommitRunnables = null;
    642         }
    643     }
    644 
    645     public int commit() {
    646         return commitInternal(false);
    647     }
    648 
    649     public int commitAllowingStateLoss() {
    650         return commitInternal(true);
    651     }
    652 
    653     @Override
    654     public void commitNow() {
    655         disallowAddToBackStack();
    656         mManager.execSingleAction(this, false);
    657     }
    658 
    659     @Override
    660     public void commitNowAllowingStateLoss() {
    661         disallowAddToBackStack();
    662         mManager.execSingleAction(this, true);
    663     }
    664 
    665     @Override
    666     public FragmentTransaction setReorderingAllowed(boolean reorderingAllowed) {
    667         mReorderingAllowed = reorderingAllowed;
    668         return this;
    669     }
    670 
    671     int commitInternal(boolean allowStateLoss) {
    672         if (mCommitted) {
    673             throw new IllegalStateException("commit already called");
    674         }
    675         if (FragmentManagerImpl.DEBUG) {
    676             Log.v(TAG, "Commit: " + this);
    677             LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
    678             PrintWriter pw = new FastPrintWriter(logw, false, 1024);
    679             dump("  ", null, pw, null);
    680             pw.flush();
    681         }
    682         mCommitted = true;
    683         if (mAddToBackStack) {
    684             mIndex = mManager.allocBackStackIndex(this);
    685         } else {
    686             mIndex = -1;
    687         }
    688         mManager.enqueueAction(this, allowStateLoss);
    689         return mIndex;
    690     }
    691 
    692     /**
    693      * Implementation of {@link android.app.FragmentManagerImpl.OpGenerator}.
    694      * This operation is added to the list of pending actions during {@link #commit()}, and
    695      * will be executed on the UI thread to run this FragmentTransaction.
    696      *
    697      * @param records Modified to add this BackStackRecord
    698      * @param isRecordPop Modified to add a false (this isn't a pop)
    699      * @return true always because the records and isRecordPop will always be changed
    700      */
    701     @Override
    702     public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
    703         if (FragmentManagerImpl.DEBUG) {
    704             Log.v(TAG, "Run: " + this);
    705         }
    706 
    707         records.add(this);
    708         isRecordPop.add(false);
    709         if (mAddToBackStack) {
    710             mManager.addBackStackState(this);
    711         }
    712         return true;
    713     }
    714 
    715     boolean interactsWith(int containerId) {
    716         final int numOps = mOps.size();
    717         for (int opNum = 0; opNum < numOps; opNum++) {
    718             final Op op = mOps.get(opNum);
    719             final int fragContainer = op.fragment != null ? op.fragment.mContainerId : 0;
    720             if (fragContainer != 0 && fragContainer == containerId) {
    721                 return true;
    722             }
    723         }
    724         return false;
    725     }
    726 
    727     boolean interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex) {
    728         if (endIndex == startIndex) {
    729             return false;
    730         }
    731         final int numOps = mOps.size();
    732         int lastContainer = -1;
    733         for (int opNum = 0; opNum < numOps; opNum++) {
    734             final Op op = mOps.get(opNum);
    735             final int container = op.fragment != null ? op.fragment.mContainerId : 0;
    736             if (container != 0 && container != lastContainer) {
    737                 lastContainer = container;
    738                 for (int i = startIndex; i < endIndex; i++) {
    739                     BackStackRecord record = records.get(i);
    740                     final int numThoseOps = record.mOps.size();
    741                     for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) {
    742                         final Op thatOp = record.mOps.get(thoseOpIndex);
    743                         final int thatContainer = thatOp.fragment != null
    744                                 ? thatOp.fragment.mContainerId : 0;
    745                         if (thatContainer == container) {
    746                             return true;
    747                         }
    748                     }
    749                 }
    750             }
    751         }
    752         return false;
    753     }
    754 
    755     /**
    756      * Executes the operations contained within this transaction. The Fragment states will only
    757      * be modified if optimizations are not allowed.
    758      */
    759     void executeOps() {
    760         final int numOps = mOps.size();
    761         for (int opNum = 0; opNum < numOps; opNum++) {
    762             final Op op = mOps.get(opNum);
    763             final Fragment f = op.fragment;
    764             if (f != null) {
    765                 f.setNextTransition(mTransition, mTransitionStyle);
    766             }
    767             switch (op.cmd) {
    768                 case OP_ADD:
    769                     f.setNextAnim(op.enterAnim);
    770                     mManager.addFragment(f, false);
    771                     break;
    772                 case OP_REMOVE:
    773                     f.setNextAnim(op.exitAnim);
    774                     mManager.removeFragment(f);
    775                     break;
    776                 case OP_HIDE:
    777                     f.setNextAnim(op.exitAnim);
    778                     mManager.hideFragment(f);
    779                     break;
    780                 case OP_SHOW:
    781                     f.setNextAnim(op.enterAnim);
    782                     mManager.showFragment(f);
    783                     break;
    784                 case OP_DETACH:
    785                     f.setNextAnim(op.exitAnim);
    786                     mManager.detachFragment(f);
    787                     break;
    788                 case OP_ATTACH:
    789                     f.setNextAnim(op.enterAnim);
    790                     mManager.attachFragment(f);
    791                     break;
    792                 case OP_SET_PRIMARY_NAV:
    793                     mManager.setPrimaryNavigationFragment(f);
    794                     break;
    795                 case OP_UNSET_PRIMARY_NAV:
    796                     mManager.setPrimaryNavigationFragment(null);
    797                     break;
    798                 default:
    799                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
    800             }
    801             if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
    802                 mManager.moveFragmentToExpectedState(f);
    803             }
    804         }
    805         if (!mReorderingAllowed) {
    806             // Added fragments are added at the end to comply with prior behavior.
    807             mManager.moveToState(mManager.mCurState, true);
    808         }
    809     }
    810 
    811     /**
    812      * Reverses the execution of the operations within this transaction. The Fragment states will
    813      * only be modified if optimizations are not allowed.
    814      *
    815      * @param moveToState {@code true} if added fragments should be moved to their final state
    816      *                    in unoptimized transactions
    817      */
    818     void executePopOps(boolean moveToState) {
    819         for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
    820             final Op op = mOps.get(opNum);
    821             Fragment f = op.fragment;
    822             if (f != null) {
    823                 f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition),
    824                         mTransitionStyle);
    825             }
    826             switch (op.cmd) {
    827                 case OP_ADD:
    828                     f.setNextAnim(op.popExitAnim);
    829                     mManager.removeFragment(f);
    830                     break;
    831                 case OP_REMOVE:
    832                     f.setNextAnim(op.popEnterAnim);
    833                     mManager.addFragment(f, false);
    834                     break;
    835                 case OP_HIDE:
    836                     f.setNextAnim(op.popEnterAnim);
    837                     mManager.showFragment(f);
    838                     break;
    839                 case OP_SHOW:
    840                     f.setNextAnim(op.popExitAnim);
    841                     mManager.hideFragment(f);
    842                     break;
    843                 case OP_DETACH:
    844                     f.setNextAnim(op.popEnterAnim);
    845                     mManager.attachFragment(f);
    846                     break;
    847                 case OP_ATTACH:
    848                     f.setNextAnim(op.popExitAnim);
    849                     mManager.detachFragment(f);
    850                     break;
    851                 case OP_SET_PRIMARY_NAV:
    852                     mManager.setPrimaryNavigationFragment(null);
    853                     break;
    854                 case OP_UNSET_PRIMARY_NAV:
    855                     mManager.setPrimaryNavigationFragment(f);
    856                     break;
    857                 default:
    858                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
    859             }
    860             if (!mReorderingAllowed && op.cmd != OP_REMOVE && f != null) {
    861                 mManager.moveFragmentToExpectedState(f);
    862             }
    863         }
    864         if (!mReorderingAllowed && moveToState) {
    865             mManager.moveToState(mManager.mCurState, true);
    866         }
    867     }
    868 
    869     /**
    870      * Expands all meta-ops into their more primitive equivalents. This must be called prior to
    871      * {@link #executeOps()} or any other call that operations on mOps for forward navigation.
    872      * It should not be called for pop/reverse navigation operations.
    873      *
    874      * <p>Removes all OP_REPLACE ops and replaces them with the proper add and remove
    875      * operations that are equivalent to the replace.</p>
    876      *
    877      * <p>Adds OP_UNSET_PRIMARY_NAV ops to match OP_SET_PRIMARY_NAV, OP_REMOVE and OP_DETACH
    878      * ops so that we can restore the old primary nav fragment later. Since callers call this
    879      * method in a loop before running ops from several transactions at once, the caller should
    880      * pass the return value from this method as the oldPrimaryNav parameter for the next call.
    881      * The first call in such a loop should pass the value of
    882      * {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
    883      *
    884      * @param added Initialized to the fragments that are in the mManager.mAdded, this
    885      *              will be modified to contain the fragments that will be in mAdded
    886      *              after the execution ({@link #executeOps()}.
    887      * @param oldPrimaryNav The tracked primary navigation fragment as of the beginning of
    888      *                      this set of ops
    889      * @return the new oldPrimaryNav fragment after this record's ops would be run
    890      */
    891     @SuppressWarnings("ReferenceEquality")
    892     Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
    893         for (int opNum = 0; opNum < mOps.size(); opNum++) {
    894             final Op op = mOps.get(opNum);
    895             switch (op.cmd) {
    896                 case OP_ADD:
    897                 case OP_ATTACH:
    898                     added.add(op.fragment);
    899                     break;
    900                 case OP_REMOVE:
    901                 case OP_DETACH: {
    902                     added.remove(op.fragment);
    903                     if (op.fragment == oldPrimaryNav) {
    904                         mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment));
    905                         opNum++;
    906                         oldPrimaryNav = null;
    907                     }
    908                 }
    909                 break;
    910                 case OP_REPLACE: {
    911                     final Fragment f = op.fragment;
    912                     final int containerId = f.mContainerId;
    913                     boolean alreadyAdded = false;
    914                     for (int i = added.size() - 1; i >= 0; i--) {
    915                         final Fragment old = added.get(i);
    916                         if (old.mContainerId == containerId) {
    917                             if (old == f) {
    918                                 alreadyAdded = true;
    919                             } else {
    920                                 // This is duplicated from above since we only make
    921                                 // a single pass for expanding ops. Unset any outgoing primary nav.
    922                                 if (old == oldPrimaryNav) {
    923                                     mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
    924                                     opNum++;
    925                                     oldPrimaryNav = null;
    926                                 }
    927                                 final Op removeOp = new Op(OP_REMOVE, old);
    928                                 removeOp.enterAnim = op.enterAnim;
    929                                 removeOp.popEnterAnim = op.popEnterAnim;
    930                                 removeOp.exitAnim = op.exitAnim;
    931                                 removeOp.popExitAnim = op.popExitAnim;
    932                                 mOps.add(opNum, removeOp);
    933                                 added.remove(old);
    934                                 opNum++;
    935                             }
    936                         }
    937                     }
    938                     if (alreadyAdded) {
    939                         mOps.remove(opNum);
    940                         opNum--;
    941                     } else {
    942                         op.cmd = OP_ADD;
    943                         added.add(f);
    944                     }
    945                 }
    946                 break;
    947                 case OP_SET_PRIMARY_NAV: {
    948                     // It's ok if this is null, that means we will restore to no active
    949                     // primary navigation fragment on a pop.
    950                     mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, oldPrimaryNav));
    951                     opNum++;
    952                     // Will be set by the OP_SET_PRIMARY_NAV we inserted before when run
    953                     oldPrimaryNav = op.fragment;
    954                 }
    955                 break;
    956             }
    957         }
    958         return oldPrimaryNav;
    959     }
    960 
    961     /**
    962      * Removes fragments that are added or removed during a pop operation.
    963      *
    964      * @param added Initialized to the fragments that are in the mManager.mAdded, this
    965      *              will be modified to contain the fragments that will be in mAdded
    966      *              after the execution ({@link #executeOps()}.
    967      */
    968     void trackAddedFragmentsInPop(ArrayList<Fragment> added) {
    969         for (int opNum = 0; opNum < mOps.size(); opNum++) {
    970             final Op op = mOps.get(opNum);
    971             switch (op.cmd) {
    972                 case OP_ADD:
    973                 case OP_ATTACH:
    974                     added.remove(op.fragment);
    975                     break;
    976                 case OP_REMOVE:
    977                 case OP_DETACH:
    978                     added.add(op.fragment);
    979                     break;
    980             }
    981         }
    982     }
    983 
    984     boolean isPostponed() {
    985         for (int opNum = 0; opNum < mOps.size(); opNum++) {
    986             final Op op = mOps.get(opNum);
    987             if (isFragmentPostponed(op)) {
    988                 return true;
    989             }
    990         }
    991         return false;
    992     }
    993 
    994     void setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener) {
    995         for (int opNum = 0; opNum < mOps.size(); opNum++) {
    996             final Op op = mOps.get(opNum);
    997             if (isFragmentPostponed(op)) {
    998                 op.fragment.setOnStartEnterTransitionListener(listener);
    999             }
   1000         }
   1001     }
   1002 
   1003     private static boolean isFragmentPostponed(Op op) {
   1004         final Fragment fragment = op.fragment;
   1005         return fragment != null && fragment.mAdded && fragment.mView != null && !fragment.mDetached
   1006                 && !fragment.mHidden && fragment.isPostponed();
   1007     }
   1008 
   1009     public String getName() {
   1010         return mName;
   1011     }
   1012 
   1013     public int getTransition() {
   1014         return mTransition;
   1015     }
   1016 
   1017     public int getTransitionStyle() {
   1018         return mTransitionStyle;
   1019     }
   1020 
   1021     public boolean isEmpty() {
   1022         return mOps.isEmpty();
   1023     }
   1024 }
   1025