BackStackRecord.java revision d4c3c91dd0757eec9703ef90ea4c5a7ee99f18ca
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import com.android.internal.util.FastPrintWriter;
20
21import android.graphics.Rect;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.text.TextUtils;
25import android.transition.Transition;
26import android.transition.TransitionInflater;
27import android.transition.TransitionManager;
28import android.transition.TransitionSet;
29import android.util.ArrayMap;
30import android.util.Log;
31import android.util.LogWriter;
32import android.util.Pair;
33import android.view.View;
34import android.view.ViewGroup;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38import java.util.ArrayList;
39import java.util.Collection;
40
41final class BackStackState implements Parcelable {
42    final int[] mOps;
43    final int mTransition;
44    final int mTransitionStyle;
45    final int mCustomTransition;
46    final int mSceneRoot;
47    final String mName;
48    final int mIndex;
49    final int mBreadCrumbTitleRes;
50    final CharSequence mBreadCrumbTitleText;
51    final int mBreadCrumbShortTitleRes;
52    final CharSequence mBreadCrumbShortTitleText;
53    final ArrayList<String> mSharedElementSourceNames;
54    final ArrayList<String> mSharedElementTargetNames;
55
56    public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
57        int numRemoved = 0;
58        BackStackRecord.Op op = bse.mHead;
59        while (op != null) {
60            if (op.removed != null) {
61                numRemoved += op.removed.size();
62            }
63            op = op.next;
64        }
65        mOps = new int[bse.mNumOp * 7 + numRemoved];
66
67        if (!bse.mAddToBackStack) {
68            throw new IllegalStateException("Not on back stack");
69        }
70
71        op = bse.mHead;
72        int pos = 0;
73        while (op != null) {
74            mOps[pos++] = op.cmd;
75            mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
76            mOps[pos++] = op.enterAnim;
77            mOps[pos++] = op.exitAnim;
78            mOps[pos++] = op.popEnterAnim;
79            mOps[pos++] = op.popExitAnim;
80            if (op.removed != null) {
81                final int N = op.removed.size();
82                mOps[pos++] = N;
83                for (int i = 0; i < N; i++) {
84                    mOps[pos++] = op.removed.get(i).mIndex;
85                }
86            } else {
87                mOps[pos++] = 0;
88            }
89            op = op.next;
90        }
91        mTransition = bse.mTransition;
92        mTransitionStyle = bse.mTransitionStyle;
93        mName = bse.mName;
94        mIndex = bse.mIndex;
95        mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
96        mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
97        mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
98        mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
99        mCustomTransition = bse.mCustomTransition;
100        mSceneRoot = bse.mSceneRoot;
101        mSharedElementSourceNames = bse.mSharedElementSourceNames;
102        mSharedElementTargetNames = bse.mSharedElementTargetNames;
103    }
104
105    public BackStackState(Parcel in) {
106        mOps = in.createIntArray();
107        mTransition = in.readInt();
108        mTransitionStyle = in.readInt();
109        mName = in.readString();
110        mIndex = in.readInt();
111        mBreadCrumbTitleRes = in.readInt();
112        mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
113        mBreadCrumbShortTitleRes = in.readInt();
114        mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
115        mCustomTransition = in.readInt();
116        mSceneRoot = in.readInt();
117        mSharedElementSourceNames = in.createStringArrayList();
118        mSharedElementTargetNames = in.createStringArrayList();
119    }
120
121    public BackStackRecord instantiate(FragmentManagerImpl fm) {
122        BackStackRecord bse = new BackStackRecord(fm);
123        int pos = 0;
124        int num = 0;
125        while (pos < mOps.length) {
126            BackStackRecord.Op op = new BackStackRecord.Op();
127            op.cmd = mOps[pos++];
128            if (FragmentManagerImpl.DEBUG) {
129                Log.v(FragmentManagerImpl.TAG,
130                        "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
131            }
132            int findex = mOps[pos++];
133            if (findex >= 0) {
134                Fragment f = fm.mActive.get(findex);
135                op.fragment = f;
136            } else {
137                op.fragment = null;
138            }
139            op.enterAnim = mOps[pos++];
140            op.exitAnim = mOps[pos++];
141            op.popEnterAnim = mOps[pos++];
142            op.popExitAnim = mOps[pos++];
143            final int N = mOps[pos++];
144            if (N > 0) {
145                op.removed = new ArrayList<Fragment>(N);
146                for (int i = 0; i < N; i++) {
147                    if (FragmentManagerImpl.DEBUG) {
148                        Log.v(FragmentManagerImpl.TAG,
149                                "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
150                    }
151                    Fragment r = fm.mActive.get(mOps[pos++]);
152                    op.removed.add(r);
153                }
154            }
155            bse.addOp(op);
156            num++;
157        }
158        bse.mTransition = mTransition;
159        bse.mTransitionStyle = mTransitionStyle;
160        bse.mName = mName;
161        bse.mIndex = mIndex;
162        bse.mAddToBackStack = true;
163        bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
164        bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
165        bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
166        bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
167        bse.mCustomTransition = mCustomTransition;
168        bse.mSceneRoot = mSceneRoot;
169        bse.mSharedElementSourceNames = mSharedElementSourceNames;
170        bse.mSharedElementTargetNames = mSharedElementTargetNames;
171        bse.bumpBackStackNesting(1);
172        return bse;
173    }
174
175    public int describeContents() {
176        return 0;
177    }
178
179    public void writeToParcel(Parcel dest, int flags) {
180        dest.writeIntArray(mOps);
181        dest.writeInt(mTransition);
182        dest.writeInt(mTransitionStyle);
183        dest.writeString(mName);
184        dest.writeInt(mIndex);
185        dest.writeInt(mBreadCrumbTitleRes);
186        TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
187        dest.writeInt(mBreadCrumbShortTitleRes);
188        TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
189        dest.writeInt(mCustomTransition);
190        dest.writeInt(mSceneRoot);
191        dest.writeStringList(mSharedElementSourceNames);
192        dest.writeStringList(mSharedElementTargetNames);
193    }
194
195    public static final Parcelable.Creator<BackStackState> CREATOR
196            = new Parcelable.Creator<BackStackState>() {
197        public BackStackState createFromParcel(Parcel in) {
198            return new BackStackState(in);
199        }
200
201        public BackStackState[] newArray(int size) {
202            return new BackStackState[size];
203        }
204    };
205}
206
207/**
208 * @hide Entry of an operation on the fragment back stack.
209 */
210final class BackStackRecord extends FragmentTransaction implements
211        FragmentManager.BackStackEntry, Runnable {
212    static final String TAG = FragmentManagerImpl.TAG;
213
214    final FragmentManagerImpl mManager;
215
216    static final int OP_NULL = 0;
217    static final int OP_ADD = 1;
218    static final int OP_REPLACE = 2;
219    static final int OP_REMOVE = 3;
220    static final int OP_HIDE = 4;
221    static final int OP_SHOW = 5;
222    static final int OP_DETACH = 6;
223    static final int OP_ATTACH = 7;
224
225    static final class Op {
226        Op next;
227        Op prev;
228        int cmd;
229        Fragment fragment;
230        int enterAnim;
231        int exitAnim;
232        int popEnterAnim;
233        int popExitAnim;
234        ArrayList<Fragment> removed;
235    }
236
237    Op mHead;
238    Op mTail;
239    int mNumOp;
240    int mEnterAnim;
241    int mExitAnim;
242    int mPopEnterAnim;
243    int mPopExitAnim;
244    int mTransition;
245    int mTransitionStyle;
246    boolean mAddToBackStack;
247    boolean mAllowAddToBackStack = true;
248    String mName;
249    boolean mCommitted;
250    int mIndex = -1;
251
252    int mBreadCrumbTitleRes;
253    CharSequence mBreadCrumbTitleText;
254    int mBreadCrumbShortTitleRes;
255    CharSequence mBreadCrumbShortTitleText;
256
257    int mCustomTransition;
258    int mSceneRoot;
259    ArrayList<String> mSharedElementSourceNames;
260    ArrayList<String> mSharedElementTargetNames;
261
262    @Override
263    public String toString() {
264        StringBuilder sb = new StringBuilder(128);
265        sb.append("BackStackEntry{");
266        sb.append(Integer.toHexString(System.identityHashCode(this)));
267        if (mIndex >= 0) {
268            sb.append(" #");
269            sb.append(mIndex);
270        }
271        if (mName != null) {
272            sb.append(" ");
273            sb.append(mName);
274        }
275        sb.append("}");
276        return sb.toString();
277    }
278
279    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
280        dump(prefix, writer, true);
281    }
282
283    void dump(String prefix, PrintWriter writer, boolean full) {
284        if (full) {
285            writer.print(prefix);
286            writer.print("mName=");
287            writer.print(mName);
288            writer.print(" mIndex=");
289            writer.print(mIndex);
290            writer.print(" mCommitted=");
291            writer.println(mCommitted);
292            if (mTransition != FragmentTransaction.TRANSIT_NONE) {
293                writer.print(prefix);
294                writer.print("mTransition=#");
295                writer.print(Integer.toHexString(mTransition));
296                writer.print(" mTransitionStyle=#");
297                writer.println(Integer.toHexString(mTransitionStyle));
298            }
299            if (mEnterAnim != 0 || mExitAnim != 0) {
300                writer.print(prefix);
301                writer.print("mEnterAnim=#");
302                writer.print(Integer.toHexString(mEnterAnim));
303                writer.print(" mExitAnim=#");
304                writer.println(Integer.toHexString(mExitAnim));
305            }
306            if (mPopEnterAnim != 0 || mPopExitAnim != 0) {
307                writer.print(prefix);
308                writer.print("mPopEnterAnim=#");
309                writer.print(Integer.toHexString(mPopEnterAnim));
310                writer.print(" mPopExitAnim=#");
311                writer.println(Integer.toHexString(mPopExitAnim));
312            }
313            if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
314                writer.print(prefix);
315                writer.print("mBreadCrumbTitleRes=#");
316                writer.print(Integer.toHexString(mBreadCrumbTitleRes));
317                writer.print(" mBreadCrumbTitleText=");
318                writer.println(mBreadCrumbTitleText);
319            }
320            if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
321                writer.print(prefix);
322                writer.print("mBreadCrumbShortTitleRes=#");
323                writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
324                writer.print(" mBreadCrumbShortTitleText=");
325                writer.println(mBreadCrumbShortTitleText);
326            }
327        }
328
329        if (mHead != null) {
330            writer.print(prefix);
331            writer.println("Operations:");
332            String innerPrefix = prefix + "    ";
333            Op op = mHead;
334            int num = 0;
335            while (op != null) {
336                String cmdStr;
337                switch (op.cmd) {
338                    case OP_NULL:
339                        cmdStr = "NULL";
340                        break;
341                    case OP_ADD:
342                        cmdStr = "ADD";
343                        break;
344                    case OP_REPLACE:
345                        cmdStr = "REPLACE";
346                        break;
347                    case OP_REMOVE:
348                        cmdStr = "REMOVE";
349                        break;
350                    case OP_HIDE:
351                        cmdStr = "HIDE";
352                        break;
353                    case OP_SHOW:
354                        cmdStr = "SHOW";
355                        break;
356                    case OP_DETACH:
357                        cmdStr = "DETACH";
358                        break;
359                    case OP_ATTACH:
360                        cmdStr = "ATTACH";
361                        break;
362                    default:
363                        cmdStr = "cmd=" + op.cmd;
364                        break;
365                }
366                writer.print(prefix);
367                writer.print("  Op #");
368                writer.print(num);
369                writer.print(": ");
370                writer.print(cmdStr);
371                writer.print(" ");
372                writer.println(op.fragment);
373                if (full) {
374                    if (op.enterAnim != 0 || op.exitAnim != 0) {
375                        writer.print(innerPrefix);
376                        writer.print("enterAnim=#");
377                        writer.print(Integer.toHexString(op.enterAnim));
378                        writer.print(" exitAnim=#");
379                        writer.println(Integer.toHexString(op.exitAnim));
380                    }
381                    if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
382                        writer.print(innerPrefix);
383                        writer.print("popEnterAnim=#");
384                        writer.print(Integer.toHexString(op.popEnterAnim));
385                        writer.print(" popExitAnim=#");
386                        writer.println(Integer.toHexString(op.popExitAnim));
387                    }
388                }
389                if (op.removed != null && op.removed.size() > 0) {
390                    for (int i = 0; i < op.removed.size(); i++) {
391                        writer.print(innerPrefix);
392                        if (op.removed.size() == 1) {
393                            writer.print("Removed: ");
394                        } else {
395                            if (i == 0) {
396                                writer.println("Removed:");
397                            }
398                            writer.print(innerPrefix);
399                            writer.print("  #");
400                            writer.print(i);
401                            writer.print(": ");
402                        }
403                        writer.println(op.removed.get(i));
404                    }
405                }
406                op = op.next;
407                num++;
408            }
409        }
410    }
411
412    public BackStackRecord(FragmentManagerImpl manager) {
413        mManager = manager;
414    }
415
416    public int getId() {
417        return mIndex;
418    }
419
420    public int getBreadCrumbTitleRes() {
421        return mBreadCrumbTitleRes;
422    }
423
424    public int getBreadCrumbShortTitleRes() {
425        return mBreadCrumbShortTitleRes;
426    }
427
428    public CharSequence getBreadCrumbTitle() {
429        if (mBreadCrumbTitleRes != 0) {
430            return mManager.mActivity.getText(mBreadCrumbTitleRes);
431        }
432        return mBreadCrumbTitleText;
433    }
434
435    public CharSequence getBreadCrumbShortTitle() {
436        if (mBreadCrumbShortTitleRes != 0) {
437            return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
438        }
439        return mBreadCrumbShortTitleText;
440    }
441
442    void addOp(Op op) {
443        if (mHead == null) {
444            mHead = mTail = op;
445        } else {
446            op.prev = mTail;
447            mTail.next = op;
448            mTail = op;
449        }
450        op.enterAnim = mEnterAnim;
451        op.exitAnim = mExitAnim;
452        op.popEnterAnim = mPopEnterAnim;
453        op.popExitAnim = mPopExitAnim;
454        mNumOp++;
455    }
456
457    public FragmentTransaction add(Fragment fragment, String tag) {
458        doAddOp(0, fragment, tag, OP_ADD);
459        return this;
460    }
461
462    public FragmentTransaction add(int containerViewId, Fragment fragment) {
463        doAddOp(containerViewId, fragment, null, OP_ADD);
464        return this;
465    }
466
467    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
468        doAddOp(containerViewId, fragment, tag, OP_ADD);
469        return this;
470    }
471
472    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
473        fragment.mFragmentManager = mManager;
474
475        if (tag != null) {
476            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
477                throw new IllegalStateException("Can't change tag of fragment "
478                        + fragment + ": was " + fragment.mTag
479                        + " now " + tag);
480            }
481            fragment.mTag = tag;
482        }
483
484        if (containerViewId != 0) {
485            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
486                throw new IllegalStateException("Can't change container ID of fragment "
487                        + fragment + ": was " + fragment.mFragmentId
488                        + " now " + containerViewId);
489            }
490            fragment.mContainerId = fragment.mFragmentId = containerViewId;
491        }
492
493        Op op = new Op();
494        op.cmd = opcmd;
495        op.fragment = fragment;
496        addOp(op);
497    }
498
499    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
500        return replace(containerViewId, fragment, null);
501    }
502
503    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
504        if (containerViewId == 0) {
505            throw new IllegalArgumentException("Must use non-zero containerViewId");
506        }
507
508        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
509        return this;
510    }
511
512    public FragmentTransaction remove(Fragment fragment) {
513        Op op = new Op();
514        op.cmd = OP_REMOVE;
515        op.fragment = fragment;
516        addOp(op);
517
518        return this;
519    }
520
521    public FragmentTransaction hide(Fragment fragment) {
522        Op op = new Op();
523        op.cmd = OP_HIDE;
524        op.fragment = fragment;
525        addOp(op);
526
527        return this;
528    }
529
530    public FragmentTransaction show(Fragment fragment) {
531        Op op = new Op();
532        op.cmd = OP_SHOW;
533        op.fragment = fragment;
534        addOp(op);
535
536        return this;
537    }
538
539    public FragmentTransaction detach(Fragment fragment) {
540        Op op = new Op();
541        op.cmd = OP_DETACH;
542        op.fragment = fragment;
543        addOp(op);
544
545        return this;
546    }
547
548    public FragmentTransaction attach(Fragment fragment) {
549        Op op = new Op();
550        op.cmd = OP_ATTACH;
551        op.fragment = fragment;
552        addOp(op);
553
554        return this;
555    }
556
557    public FragmentTransaction setCustomAnimations(int enter, int exit) {
558        return setCustomAnimations(enter, exit, 0, 0);
559    }
560
561    public FragmentTransaction setCustomAnimations(int enter, int exit,
562            int popEnter, int popExit) {
563        mEnterAnim = enter;
564        mExitAnim = exit;
565        mPopEnterAnim = popEnter;
566        mPopExitAnim = popExit;
567        return this;
568    }
569
570    public FragmentTransaction setTransition(int transition) {
571        mTransition = transition;
572        return this;
573    }
574
575    @Override
576    public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) {
577        mSceneRoot = sceneRootId;
578        mCustomTransition = transitionId;
579        return this;
580    }
581
582    @Override
583    public FragmentTransaction setSharedElement(View sharedElement, String name) {
584        String viewName = sharedElement.getViewName();
585        if (viewName == null) {
586            throw new IllegalArgumentException("Unique viewNames are required for all" +
587                    " sharedElements");
588        }
589        mSharedElementSourceNames = new ArrayList<String>(1);
590        mSharedElementSourceNames.add(viewName);
591
592        mSharedElementTargetNames = new ArrayList<String>(1);
593        mSharedElementTargetNames.add(name);
594        return this;
595    }
596
597    @Override
598    public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) {
599        if (sharedElements == null || sharedElements.length == 0) {
600            mSharedElementSourceNames = null;
601            mSharedElementTargetNames = null;
602        } else {
603            ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length);
604            ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length);
605            for (int i = 0; i < sharedElements.length; i++) {
606                String viewName = sharedElements[i].first.getViewName();
607                if (viewName == null) {
608                    throw new IllegalArgumentException("Unique viewNames are required for all" +
609                            " sharedElements");
610                }
611                sourceNames.add(viewName);
612                targetNames.add(sharedElements[i].second);
613            }
614            mSharedElementSourceNames = sourceNames;
615            mSharedElementTargetNames = targetNames;
616        }
617        return this;
618    }
619
620    public FragmentTransaction setTransitionStyle(int styleRes) {
621        mTransitionStyle = styleRes;
622        return this;
623    }
624
625    public FragmentTransaction addToBackStack(String name) {
626        if (!mAllowAddToBackStack) {
627            throw new IllegalStateException(
628                    "This FragmentTransaction is not allowed to be added to the back stack.");
629        }
630        mAddToBackStack = true;
631        mName = name;
632        return this;
633    }
634
635    public boolean isAddToBackStackAllowed() {
636        return mAllowAddToBackStack;
637    }
638
639    public FragmentTransaction disallowAddToBackStack() {
640        if (mAddToBackStack) {
641            throw new IllegalStateException(
642                    "This transaction is already being added to the back stack");
643        }
644        mAllowAddToBackStack = false;
645        return this;
646    }
647
648    public FragmentTransaction setBreadCrumbTitle(int res) {
649        mBreadCrumbTitleRes = res;
650        mBreadCrumbTitleText = null;
651        return this;
652    }
653
654    public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
655        mBreadCrumbTitleRes = 0;
656        mBreadCrumbTitleText = text;
657        return this;
658    }
659
660    public FragmentTransaction setBreadCrumbShortTitle(int res) {
661        mBreadCrumbShortTitleRes = res;
662        mBreadCrumbShortTitleText = null;
663        return this;
664    }
665
666    public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
667        mBreadCrumbShortTitleRes = 0;
668        mBreadCrumbShortTitleText = text;
669        return this;
670    }
671
672    void bumpBackStackNesting(int amt) {
673        if (!mAddToBackStack) {
674            return;
675        }
676        if (FragmentManagerImpl.DEBUG) {
677            Log.v(TAG, "Bump nesting in " + this
678                    + " by " + amt);
679        }
680        Op op = mHead;
681        while (op != null) {
682            if (op.fragment != null) {
683                op.fragment.mBackStackNesting += amt;
684                if (FragmentManagerImpl.DEBUG) {
685                    Log.v(TAG, "Bump nesting of "
686                            + op.fragment + " to " + op.fragment.mBackStackNesting);
687                }
688            }
689            if (op.removed != null) {
690                for (int i = op.removed.size() - 1; i >= 0; i--) {
691                    Fragment r = op.removed.get(i);
692                    r.mBackStackNesting += amt;
693                    if (FragmentManagerImpl.DEBUG) {
694                        Log.v(TAG, "Bump nesting of "
695                                + r + " to " + r.mBackStackNesting);
696                    }
697                }
698            }
699            op = op.next;
700        }
701    }
702
703    public int commit() {
704        return commitInternal(false);
705    }
706
707    public int commitAllowingStateLoss() {
708        return commitInternal(true);
709    }
710
711    int commitInternal(boolean allowStateLoss) {
712        if (mCommitted) {
713            throw new IllegalStateException("commit already called");
714        }
715        if (FragmentManagerImpl.DEBUG) {
716            Log.v(TAG, "Commit: " + this);
717            LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
718            PrintWriter pw = new FastPrintWriter(logw, false, 1024);
719            dump("  ", null, pw, null);
720            pw.flush();
721        }
722        mCommitted = true;
723        if (mAddToBackStack) {
724            mIndex = mManager.allocBackStackIndex(this);
725        } else {
726            mIndex = -1;
727        }
728        mManager.enqueueAction(this, allowStateLoss);
729        return mIndex;
730    }
731
732    public void run() {
733        if (FragmentManagerImpl.DEBUG) {
734            Log.v(TAG, "Run: " + this);
735        }
736
737        if (mAddToBackStack) {
738            if (mIndex < 0) {
739                throw new IllegalStateException("addToBackStack() called after commit()");
740            }
741        }
742
743        bumpBackStackNesting(1);
744
745        TransitionState state = beginTransition(mSharedElementSourceNames,
746                mSharedElementTargetNames);
747
748        Op op = mHead;
749        while (op != null) {
750            switch (op.cmd) {
751                case OP_ADD: {
752                    Fragment f = op.fragment;
753                    f.mNextAnim = op.enterAnim;
754                    mManager.addFragment(f, false);
755                }
756                break;
757                case OP_REPLACE: {
758                    Fragment f = op.fragment;
759                    if (mManager.mAdded != null) {
760                        for (int i = 0; i < mManager.mAdded.size(); i++) {
761                            Fragment old = mManager.mAdded.get(i);
762                            if (FragmentManagerImpl.DEBUG) {
763                                Log.v(TAG,
764                                        "OP_REPLACE: adding=" + f + " old=" + old);
765                            }
766                            if (f == null || old.mContainerId == f.mContainerId) {
767                                if (old == f) {
768                                    op.fragment = f = null;
769                                } else {
770                                    if (op.removed == null) {
771                                        op.removed = new ArrayList<Fragment>();
772                                    }
773                                    op.removed.add(old);
774                                    old.mNextAnim = op.exitAnim;
775                                    if (mAddToBackStack) {
776                                        old.mBackStackNesting += 1;
777                                        if (FragmentManagerImpl.DEBUG) {
778                                            Log.v(TAG, "Bump nesting of "
779                                                    + old + " to " + old.mBackStackNesting);
780                                        }
781                                    }
782                                    mManager.removeFragment(old, mTransition, mTransitionStyle);
783                                }
784                            }
785                        }
786                    }
787                    if (f != null) {
788                        f.mNextAnim = op.enterAnim;
789                        mManager.addFragment(f, false);
790                    }
791                }
792                break;
793                case OP_REMOVE: {
794                    Fragment f = op.fragment;
795                    f.mNextAnim = op.exitAnim;
796                    mManager.removeFragment(f, mTransition, mTransitionStyle);
797                }
798                break;
799                case OP_HIDE: {
800                    Fragment f = op.fragment;
801                    f.mNextAnim = op.exitAnim;
802                    mManager.hideFragment(f, mTransition, mTransitionStyle);
803                }
804                break;
805                case OP_SHOW: {
806                    Fragment f = op.fragment;
807                    f.mNextAnim = op.enterAnim;
808                    mManager.showFragment(f, mTransition, mTransitionStyle);
809                }
810                break;
811                case OP_DETACH: {
812                    Fragment f = op.fragment;
813                    f.mNextAnim = op.exitAnim;
814                    mManager.detachFragment(f, mTransition, mTransitionStyle);
815                }
816                break;
817                case OP_ATTACH: {
818                    Fragment f = op.fragment;
819                    f.mNextAnim = op.enterAnim;
820                    mManager.attachFragment(f, mTransition, mTransitionStyle);
821                }
822                break;
823                default: {
824                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
825                }
826            }
827
828            op = op.next;
829        }
830
831        mManager.moveToState(mManager.mCurState, mTransition,
832                mTransitionStyle, true);
833
834        if (mAddToBackStack) {
835            mManager.addBackStackState(this);
836        }
837
838        if (state != null) {
839            updateTransitionEndState(state, mSharedElementTargetNames);
840        }
841    }
842
843    private TransitionState beginTransition(ArrayList<String> sourceNames,
844            ArrayList<String> targetNames) {
845        if (mCustomTransition <= 0 || mSceneRoot <= 0) {
846            return null;
847        }
848        View rootView = mManager.mContainer.findViewById(mSceneRoot);
849        if (!(rootView instanceof ViewGroup)) {
850            throw new IllegalArgumentException("SceneRoot is not a ViewGroup");
851        }
852        TransitionState state = new TransitionState();
853        // get Transition scene root and create Transitions
854        state.sceneRoot = (ViewGroup) rootView;
855        state.sceneRoot.captureTransitioningViews(state.transitioningViews);
856
857        state.exitTransition = TransitionInflater.from(mManager.mActivity)
858                .inflateTransition(mCustomTransition);
859        state.sharedElementTransition = TransitionInflater.from(mManager.mActivity)
860                .inflateTransition(mCustomTransition);
861        state.enterTransition = TransitionInflater.from(mManager.mActivity)
862                .inflateTransition(mCustomTransition);
863        // Adding a non-existent target view makes sure that the transitions don't target
864        // any views by default. They'll only target the views we tell add. If we don't
865        // add any, then no views will be targeted.
866        View nonExistentView = new View(mManager.mActivity);
867        state.enterTransition.addTarget(nonExistentView);
868        state.exitTransition.addTarget(nonExistentView);
869        state.sharedElementTransition.addTarget(nonExistentView);
870
871        setSharedElementEpicenter(state.enterTransition, state);
872
873        state.excludingTransition = new TransitionSet()
874                .addTransition(state.exitTransition)
875                .addTransition(state.enterTransition);
876
877        if (sourceNames != null) {
878            // Map shared elements.
879            state.sceneRoot.findNamedViews(state.namedViews);
880            state.namedViews.retainAll(sourceNames);
881            View epicenterView = state.namedViews.get(sourceNames.get(0));
882            if (epicenterView != null) {
883                // The epicenter is only the first shared element.
884                setEpicenter(state.exitTransition, epicenterView);
885                setEpicenter(state.sharedElementTransition, epicenterView);
886            }
887            state.transitioningViews.removeAll(state.namedViews.values());
888            state.excludingTransition.addTransition(state.sharedElementTransition);
889            addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
890        }
891
892        // Adds the (maybe) exiting views, not including the shared element.
893        // If some stay, that's ok.
894        addTransitioningViews(state.exitTransition, state.transitioningViews);
895
896        // Prepare for shared element name mapping. This could be chained in the case
897        // of popping several back stack states.
898        state.excludingTransition.setNameOverrides(new ArrayMap<String, String>());
899        setNameOverrides(state, sourceNames, targetNames);
900
901        // Don't include any subtree in the views that are hidden when capturing the
902        // view hierarchy transitions. They should be as if not there.
903        excludeHiddenFragments(state, true);
904
905        TransitionManager.beginDelayedTransition(state.sceneRoot, state.excludingTransition);
906        return state;
907    }
908
909    private void updateTransitionEndState(TransitionState state, ArrayList<String> names) {
910        // Find all views that are entering.
911        ArrayList<View> enteringViews = new ArrayList<View>();
912        state.sceneRoot.captureTransitioningViews(enteringViews);
913        enteringViews.removeAll(state.transitioningViews);
914
915        if (names != null) {
916            // find all shared elements.
917            state.namedViews.clear();
918            state.sceneRoot.findNamedViews(state.namedViews);
919            state.namedViews.retainAll(names);
920            if (!state.namedViews.isEmpty()) {
921                enteringViews.removeAll(state.namedViews.values());
922                addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
923                // now we know the epicenter of the entering transition.
924                state.mEnteringEpicenterView = state.namedViews.get(names.get(0));
925            }
926        }
927
928        // Add all entering views to the enter transition.
929        addTransitioningViews(state.enterTransition, enteringViews);
930
931        // Don't allow capturing state for the newly-hidden fragments.
932        excludeHiddenFragments(state, false);
933
934        // Allow capturing state for the newly-shown fragments
935        includeVisibleFragments(state.excludingTransition);
936    }
937
938    private void addTransitioningViews(Transition transition, Collection<View> views) {
939        if (views.isEmpty()) {
940            // Add a view so that we can modify the valid views at the end of the
941            // fragment transaction.
942            transition.addTarget(new View(mManager.mActivity));
943        } else {
944            for (View view : views) {
945                transition.addTarget(view);
946            }
947        }
948    }
949
950    private void excludeHiddenFragments(TransitionState state, boolean forceExclude) {
951        if (mManager.mAdded != null) {
952            for (int i = 0; i < mManager.mAdded.size(); i++) {
953                Fragment fragment = mManager.mAdded.get(i);
954                if (fragment.mView != null && fragment.mHidden
955                        && (forceExclude || !state.hiddenViews.contains(fragment.mView))) {
956                    state.excludingTransition.excludeTarget(fragment.mView, true);
957                    state.hiddenViews.add(fragment.mView);
958                }
959            }
960        }
961        if (forceExclude && state.hiddenViews.isEmpty()) {
962            state.excludingTransition.excludeTarget(new View(mManager.mActivity), true);
963        }
964    }
965
966    private void includeVisibleFragments(Transition transition) {
967        if (mManager.mAdded != null) {
968            for (int i = 0; i < mManager.mAdded.size(); i++) {
969                Fragment fragment = mManager.mAdded.get(i);
970                if (fragment.mView != null && !fragment.mHidden) {
971                    transition.excludeTarget(fragment.mView, false);
972                }
973            }
974        }
975    }
976
977    private static void setEpicenter(Transition transition, View view) {
978        final Rect epicenter = new Rect();
979        view.getBoundsOnScreen(epicenter);
980
981        transition.setEpicenterCallback(new Transition.EpicenterCallback() {
982            @Override
983            public Rect onGetEpicenter(Transition transition) {
984                return epicenter;
985            }
986        });
987    }
988
989    private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
990        transition.setEpicenterCallback(new Transition.EpicenterCallback() {
991            private Rect mEpicenter;
992
993            @Override
994            public Rect onGetEpicenter(Transition transition) {
995                if (mEpicenter == null && state.mEnteringEpicenterView != null) {
996                    mEpicenter = new Rect();
997                    state.mEnteringEpicenterView.getBoundsOnScreen(mEpicenter);
998                }
999                return mEpicenter;
1000            }
1001        });
1002    }
1003
1004    public TransitionState popFromBackStack(boolean doStateMove, TransitionState state) {
1005        if (FragmentManagerImpl.DEBUG) {
1006            Log.v(TAG, "popFromBackStack: " + this);
1007            LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
1008            PrintWriter pw = new FastPrintWriter(logw, false, 1024);
1009            dump("  ", null, pw, null);
1010            pw.flush();
1011        }
1012
1013        if (state == null) {
1014            state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames);
1015        } else {
1016            setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
1017        }
1018
1019        bumpBackStackNesting(-1);
1020
1021        Op op = mTail;
1022        while (op != null) {
1023            switch (op.cmd) {
1024                case OP_ADD: {
1025                    Fragment f = op.fragment;
1026                    f.mNextAnim = op.popExitAnim;
1027                    mManager.removeFragment(f,
1028                            FragmentManagerImpl.reverseTransit(mTransition),
1029                            mTransitionStyle);
1030                }
1031                break;
1032                case OP_REPLACE: {
1033                    Fragment f = op.fragment;
1034                    if (f != null) {
1035                        f.mNextAnim = op.popExitAnim;
1036                        mManager.removeFragment(f,
1037                                FragmentManagerImpl.reverseTransit(mTransition),
1038                                mTransitionStyle);
1039                    }
1040                    if (op.removed != null) {
1041                        for (int i = 0; i < op.removed.size(); i++) {
1042                            Fragment old = op.removed.get(i);
1043                            old.mNextAnim = op.popEnterAnim;
1044                            mManager.addFragment(old, false);
1045                        }
1046                    }
1047                }
1048                break;
1049                case OP_REMOVE: {
1050                    Fragment f = op.fragment;
1051                    f.mNextAnim = op.popEnterAnim;
1052                    mManager.addFragment(f, false);
1053                }
1054                break;
1055                case OP_HIDE: {
1056                    Fragment f = op.fragment;
1057                    f.mNextAnim = op.popEnterAnim;
1058                    mManager.showFragment(f,
1059                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
1060                }
1061                break;
1062                case OP_SHOW: {
1063                    Fragment f = op.fragment;
1064                    f.mNextAnim = op.popExitAnim;
1065                    mManager.hideFragment(f,
1066                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
1067                }
1068                break;
1069                case OP_DETACH: {
1070                    Fragment f = op.fragment;
1071                    f.mNextAnim = op.popEnterAnim;
1072                    mManager.attachFragment(f,
1073                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
1074                }
1075                break;
1076                case OP_ATTACH: {
1077                    Fragment f = op.fragment;
1078                    f.mNextAnim = op.popExitAnim;
1079                    mManager.detachFragment(f,
1080                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
1081                }
1082                break;
1083                default: {
1084                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
1085                }
1086            }
1087
1088            op = op.prev;
1089        }
1090
1091        if (doStateMove) {
1092            mManager.moveToState(mManager.mCurState,
1093                    FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
1094            if (state != null) {
1095                updateTransitionEndState(state, mSharedElementSourceNames);
1096                state = null;
1097            }
1098        }
1099
1100        if (mIndex >= 0) {
1101            mManager.freeBackStackIndex(mIndex);
1102            mIndex = -1;
1103        }
1104        return state;
1105    }
1106
1107    private static void setNameOverride(Transition transition, String source, String target) {
1108        ArrayMap<String, String> overrides = transition.getNameOverrides();
1109        for (int index = 0; index < overrides.size(); index++) {
1110            if (source.equals(overrides.valueAt(index))) {
1111                overrides.setValueAt(index, target);
1112                return;
1113            }
1114        }
1115        overrides.put(source, target);
1116    }
1117
1118    private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
1119            ArrayList<String> targetNames) {
1120        if (sourceNames != null) {
1121            for (int i = 0; i < sourceNames.size(); i++) {
1122                String source = sourceNames.get(i);
1123                String target = targetNames.get(i);
1124                setNameOverride(state.excludingTransition, source, target);
1125            }
1126        }
1127    }
1128
1129    public String getName() {
1130        return mName;
1131    }
1132
1133    public int getTransition() {
1134        return mTransition;
1135    }
1136
1137    public int getTransitionStyle() {
1138        return mTransitionStyle;
1139    }
1140
1141    public boolean isEmpty() {
1142        return mNumOp == 0;
1143    }
1144
1145    public class TransitionState {
1146        public ArrayList<View> hiddenViews = new ArrayList<View>();
1147        public ArrayList<View> transitioningViews = new ArrayList<View>();
1148        public ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
1149        public Transition exitTransition;
1150        public Transition sharedElementTransition;
1151        public Transition enterTransition;
1152        public TransitionSet excludingTransition;
1153        public ViewGroup sceneRoot;
1154        public View mEnteringEpicenterView;
1155    }
1156}
1157