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