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