811a5a4a |
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.R; |
20 | import android.content.Context; |
21 | import android.content.res.Configuration; |
22 | import android.os.Bundle; |
23 | import android.os.Handler; |
24 | import android.os.Looper; |
25 | import android.os.Parcel; |
26 | import android.os.Parcelable; |
27 | import android.support.v2.util.DebugUtils; |
28 | import android.support.v2.util.LogWriter; |
29 | import android.util.Log; |
30 | import android.util.SparseArray; |
31 | import android.view.animation.AccelerateInterpolator; |
32 | import android.view.animation.AlphaAnimation; |
33 | import android.view.animation.Animation; |
34 | import android.view.animation.AnimationSet; |
35 | import android.view.animation.AnimationUtils; |
36 | import android.view.animation.DecelerateInterpolator; |
37 | import android.view.animation.Interpolator; |
38 | import android.view.animation.ScaleAnimation; |
39 | import android.view.animation.Animation.AnimationListener; |
40 | import android.view.Menu; |
41 | import android.view.MenuInflater; |
42 | import android.view.MenuItem; |
43 | import android.view.View; |
44 | import android.view.ViewGroup; |
45 | |
46 | import java.io.FileDescriptor; |
47 | import java.io.PrintWriter; |
48 | import java.util.ArrayList; |
49 | import java.util.Arrays; |
50 | |
51 | /** |
52 | * Static library support version of the framework's {@link android.app.FragmentManager}. |
53 | * Used to write apps that run on platforms prior to Android 3.0. When running |
54 | * on Android 3.0 or above, this implementation is still used; it does not try |
55 | * to switch to the framework's implementation. See the framework SDK |
56 | * documentation for a class overview. |
57 | * |
58 | * <p>Your activity must derive from {@link FragmentActivity} to use this. |
59 | */ |
60 | public abstract class FragmentManager { |
61 | /** |
62 | * Representation of an entry on the fragment back stack, as created |
63 | * with {@link FragmentTransaction#addToBackStack(String) |
64 | * FragmentTransaction.addToBackStack()}. Entries can later be |
65 | * retrieved with {@link FragmentManager#getBackStackEntryAt(int) |
66 | * FragmentManager.getBackStackEntry()}. |
67 | * |
68 | * <p>Note that you should never hold on to a BackStackEntry object; |
69 | * the identifier as returned by {@link #getId} is the only thing that |
70 | * will be persisted across activity instances. |
71 | */ |
72 | public interface BackStackEntry { |
73 | /** |
74 | * Return the unique identifier for the entry. This is the only |
75 | * representation of the entry that will persist across activity |
76 | * instances. |
77 | */ |
78 | public int getId(); |
79 | |
80 | /** |
81 | * Return the full bread crumb title resource identifier for the entry, |
82 | * or 0 if it does not have one. |
83 | */ |
84 | public int getBreadCrumbTitleRes(); |
85 | |
86 | /** |
87 | * Return the short bread crumb title resource identifier for the entry, |
88 | * or 0 if it does not have one. |
89 | */ |
90 | public int getBreadCrumbShortTitleRes(); |
91 | |
92 | /** |
93 | * Return the full bread crumb title for the entry, or null if it |
94 | * does not have one. |
95 | */ |
96 | public CharSequence getBreadCrumbTitle(); |
97 | |
98 | /** |
99 | * Return the short bread crumb title for the entry, or null if it |
100 | * does not have one. |
101 | */ |
102 | public CharSequence getBreadCrumbShortTitle(); |
103 | } |
104 | |
105 | /** |
106 | * Interface to watch for changes to the back stack. |
107 | */ |
108 | public interface OnBackStackChangedListener { |
109 | /** |
110 | * Called whenever the contents of the back stack change. |
111 | */ |
112 | public void onBackStackChanged(); |
113 | } |
114 | |
115 | /** |
116 | * Start a series of edit operations on the Fragments associated with |
117 | * this FragmentManager. |
118 | * |
119 | * <p>Note: A fragment transaction can only be created/committed prior |
120 | * to an activity saving its state. If you try to commit a transaction |
121 | * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()} |
122 | * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart} |
123 | * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error. |
124 | * This is because the framework takes care of saving your current fragments |
125 | * in the state, and if changes are made after the state is saved then they |
126 | * will be lost.</p> |
127 | */ |
128 | public abstract FragmentTransaction beginTransaction(); |
129 | |
130 | /** @hide -- remove once prebuilts are in. */ |
131 | @Deprecated |
132 | public FragmentTransaction openTransaction() { |
133 | return beginTransaction(); |
134 | } |
135 | |
136 | /** |
137 | * After a {@link FragmentTransaction} is committed with |
138 | * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it |
139 | * is scheduled to be executed asynchronously on the process's main thread. |
140 | * If you want to immediately executing any such pending operations, you |
141 | * can call this function (only from the main thread) to do so. Note that |
142 | * all callbacks and other related behavior will be done from within this |
143 | * call, so be careful about where this is called from. |
144 | * |
145 | * @return Returns true if there were any pending transactions to be |
146 | * executed. |
147 | */ |
148 | public abstract boolean executePendingTransactions(); |
149 | |
150 | /** |
151 | * Finds a fragment that was identified by the given id either when inflated |
152 | * from XML or as the container ID when added in a transaction. This first |
153 | * searches through fragments that are currently added to the manager's |
154 | * activity; if no such fragment is found, then all fragments currently |
155 | * on the back stack associated with this ID are searched. |
156 | * @return The fragment if found or null otherwise. |
157 | */ |
158 | public abstract Fragment findFragmentById(int id); |
159 | |
160 | /** |
161 | * Finds a fragment that was identified by the given tag either when inflated |
162 | * from XML or as supplied when added in a transaction. This first |
163 | * searches through fragments that are currently added to the manager's |
164 | * activity; if no such fragment is found, then all fragments currently |
165 | * on the back stack are searched. |
166 | * @return The fragment if found or null otherwise. |
167 | */ |
168 | public abstract Fragment findFragmentByTag(String tag); |
169 | |
170 | /** |
171 | * Flag for {@link #popBackStack(String, int)} |
172 | * and {@link #popBackStack(int, int)}: If set, and the name or ID of |
173 | * a back stack entry has been supplied, then all matching entries will |
174 | * be consumed until one that doesn't match is found or the bottom of |
175 | * the stack is reached. Otherwise, all entries up to but not including that entry |
176 | * will be removed. |
177 | */ |
178 | public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; |
179 | |
180 | /** |
181 | * Pop the top state off the back stack. Returns true if there was one |
182 | * to pop, else false. This function is asynchronous -- it enqueues the |
183 | * request to pop, but the action will not be performed until the application |
184 | * returns to its event loop. |
185 | */ |
186 | public abstract void popBackStack(); |
187 | |
188 | /** |
189 | * Like {@link #popBackStack()}, but performs the operation immediately |
190 | * inside of the call. This is like calling {@link #executePendingTransactions()} |
191 | * afterwards. |
192 | * @return Returns true if there was something popped, else false. |
193 | */ |
194 | public abstract boolean popBackStackImmediate(); |
195 | |
196 | /** |
197 | * Pop the last fragment transition from the manager's fragment |
198 | * back stack. If there is nothing to pop, false is returned. |
199 | * This function is asynchronous -- it enqueues the |
200 | * request to pop, but the action will not be performed until the application |
201 | * returns to its event loop. |
202 | * |
203 | * @param name If non-null, this is the name of a previous back state |
204 | * to look for; if found, all states up to that state will be popped. The |
205 | * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether |
206 | * the named state itself is popped. If null, only the top state is popped. |
207 | * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. |
208 | */ |
209 | public abstract void popBackStack(String name, int flags); |
210 | |
211 | /** |
212 | * Like {@link #popBackStack(String, int)}, but performs the operation immediately |
213 | * inside of the call. This is like calling {@link #executePendingTransactions()} |
214 | * afterwards. |
215 | * @return Returns true if there was something popped, else false. |
216 | */ |
217 | public abstract boolean popBackStackImmediate(String name, int flags); |
218 | |
219 | /** |
220 | * Pop all back stack states up to the one with the given identifier. |
221 | * This function is asynchronous -- it enqueues the |
222 | * request to pop, but the action will not be performed until the application |
223 | * returns to its event loop. |
224 | * |
225 | * @param id Identifier of the stated to be popped. If no identifier exists, |
226 | * false is returned. |
227 | * The identifier is the number returned by |
228 | * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The |
229 | * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether |
230 | * the named state itself is popped. |
231 | * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. |
232 | */ |
233 | public abstract void popBackStack(int id, int flags); |
234 | |
235 | /** |
236 | * Like {@link #popBackStack(int, int)}, but performs the operation immediately |
237 | * inside of the call. This is like calling {@link #executePendingTransactions()} |
238 | * afterwards. |
239 | * @return Returns true if there was something popped, else false. |
240 | */ |
241 | public abstract boolean popBackStackImmediate(int id, int flags); |
242 | |
243 | /** |
244 | * Return the number of entries currently in the back stack. |
245 | */ |
246 | public abstract int getBackStackEntryCount(); |
247 | |
248 | /** |
249 | * Return the BackStackEntry at index <var>index</var> in the back stack; |
250 | * entries start index 0 being the bottom of the stack. |
251 | */ |
252 | public abstract BackStackEntry getBackStackEntryAt(int index); |
253 | |
254 | /** |
255 | * Add a new listener for changes to the fragment back stack. |
256 | */ |
257 | public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener); |
258 | |
259 | /** |
260 | * Remove a listener that was previously added with |
261 | * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. |
262 | */ |
263 | public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener); |
264 | |
265 | /** |
266 | * Put a reference to a fragment in a Bundle. This Bundle can be |
267 | * persisted as saved state, and when later restoring |
268 | * {@link #getFragment(Bundle, String)} will return the current |
269 | * instance of the same fragment. |
270 | * |
271 | * @param bundle The bundle in which to put the fragment reference. |
272 | * @param key The name of the entry in the bundle. |
273 | * @param fragment The Fragment whose reference is to be stored. |
274 | */ |
275 | public abstract void putFragment(Bundle bundle, String key, Fragment fragment); |
276 | |
277 | /** |
278 | * Retrieve the current Fragment instance for a reference previously |
279 | * placed with {@link #putFragment(Bundle, String, Fragment)}. |
280 | * |
281 | * @param bundle The bundle from which to retrieve the fragment reference. |
282 | * @param key The name of the entry in the bundle. |
283 | * @return Returns the current Fragment instance that is associated with |
284 | * the given reference. |
285 | */ |
286 | public abstract Fragment getFragment(Bundle bundle, String key); |
287 | |
288 | /** |
289 | * Print the FragmentManager's state into the given stream. |
290 | * |
291 | * @param prefix Text to print at the front of each line. |
292 | * @param fd The raw file descriptor that the dump is being sent to. |
293 | * @param writer A PrintWriter to which the dump is to be set. |
294 | * @param args Additional arguments to the dump request. |
295 | */ |
296 | public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); |
297 | |
298 | /** |
299 | * Control whether the framework's internal fragment manager debugging |
300 | * logs are turned on. If enabled, you will see output in logcat as |
301 | * the framework performs fragment operations. |
302 | */ |
303 | public static void enableDebugLogging(boolean enabled) { |
304 | FragmentManagerImpl.DEBUG = enabled; |
305 | } |
306 | } |
307 | |
308 | final class FragmentManagerState implements Parcelable { |
309 | FragmentState[] mActive; |
310 | int[] mAdded; |
311 | BackStackState[] mBackStack; |
312 | |
313 | public FragmentManagerState() { |
314 | } |
315 | |
316 | public FragmentManagerState(Parcel in) { |
317 | mActive = in.createTypedArray(FragmentState.CREATOR); |
318 | mAdded = in.createIntArray(); |
319 | mBackStack = in.createTypedArray(BackStackState.CREATOR); |
320 | } |
321 | |
322 | public int describeContents() { |
323 | return 0; |
324 | } |
325 | |
326 | public void writeToParcel(Parcel dest, int flags) { |
327 | dest.writeTypedArray(mActive, flags); |
328 | dest.writeIntArray(mAdded); |
329 | dest.writeTypedArray(mBackStack, flags); |
330 | } |
331 | |
332 | public static final Parcelable.Creator<FragmentManagerState> CREATOR |
333 | = new Parcelable.Creator<FragmentManagerState>() { |
334 | public FragmentManagerState createFromParcel(Parcel in) { |
335 | return new FragmentManagerState(in); |
336 | } |
337 | |
338 | public FragmentManagerState[] newArray(int size) { |
339 | return new FragmentManagerState[size]; |
340 | } |
341 | }; |
342 | } |
343 | |
344 | /** |
345 | * Container for fragments associated with an activity. |
346 | */ |
347 | final class FragmentManagerImpl extends FragmentManager { |
348 | static boolean DEBUG = false; |
349 | static final String TAG = "FragmentManager"; |
350 | |
351 | //v4 static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11; |
352 | static final boolean HONEYCOMB = android.support.v2.os.Build.VERSION.SDK_INT >= 11; |
353 | |
354 | static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; |
355 | static final String TARGET_STATE_TAG = "android:target_state"; |
356 | static final String VIEW_STATE_TAG = "android:view_state"; |
357 | |
358 | ArrayList<Runnable> mPendingActions; |
359 | Runnable[] mTmpActions; |
360 | boolean mExecutingActions; |
361 | |
362 | ArrayList<Fragment> mActive; |
363 | ArrayList<Fragment> mAdded; |
364 | ArrayList<Integer> mAvailIndices; |
365 | ArrayList<BackStackRecord> mBackStack; |
366 | ArrayList<Fragment> mCreatedMenus; |
367 | |
368 | // Must be accessed while locked. |
369 | ArrayList<BackStackRecord> mBackStackIndices; |
370 | ArrayList<Integer> mAvailBackStackIndices; |
371 | |
372 | ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; |
373 | |
374 | int mCurState = Fragment.INITIALIZING; |
375 | FragmentActivity mActivity; |
376 | |
377 | boolean mNeedMenuInvalidate; |
378 | boolean mStateSaved; |
379 | boolean mDestroyed; |
380 | String mNoTransactionsBecause; |
381 | |
382 | // Temporary vars for state save and restore. |
383 | Bundle mStateBundle = null; |
384 | SparseArray<Parcelable> mStateArray = null; |
385 | |
386 | Runnable mExecCommit = new Runnable() { |
387 | @Override |
388 | public void run() { |
389 | execPendingActions(); |
390 | } |
391 | }; |
392 | |
393 | @Override |
394 | public FragmentTransaction beginTransaction() { |
395 | return new BackStackRecord(this); |
396 | } |
397 | |
398 | @Override |
399 | public boolean executePendingTransactions() { |
400 | return execPendingActions(); |
401 | } |
402 | |
403 | @Override |
404 | public void popBackStack() { |
405 | enqueueAction(new Runnable() { |
406 | @Override public void run() { |
407 | popBackStackState(mActivity.mHandler, null, -1, 0); |
408 | } |
409 | }, false); |
410 | } |
411 | |
412 | @Override |
413 | public boolean popBackStackImmediate() { |
414 | checkStateLoss(); |
415 | executePendingTransactions(); |
416 | return popBackStackState(mActivity.mHandler, null, -1, 0); |
417 | } |
418 | |
419 | @Override |
420 | public void popBackStack(final String name, final int flags) { |
421 | enqueueAction(new Runnable() { |
422 | @Override public void run() { |
423 | popBackStackState(mActivity.mHandler, name, -1, flags); |
424 | } |
425 | }, false); |
426 | } |
427 | |
428 | @Override |
429 | public boolean popBackStackImmediate(String name, int flags) { |
430 | checkStateLoss(); |
431 | executePendingTransactions(); |
432 | return popBackStackState(mActivity.mHandler, name, -1, flags); |
433 | } |
434 | |
435 | @Override |
436 | public void popBackStack(final int id, final int flags) { |
437 | if (id < 0) { |
438 | throw new IllegalArgumentException("Bad id: " + id); |
439 | } |
440 | enqueueAction(new Runnable() { |
441 | @Override public void run() { |
442 | popBackStackState(mActivity.mHandler, null, id, flags); |
443 | } |
444 | }, false); |
445 | } |
446 | |
447 | @Override |
448 | public boolean popBackStackImmediate(int id, int flags) { |
449 | checkStateLoss(); |
450 | executePendingTransactions(); |
451 | if (id < 0) { |
452 | throw new IllegalArgumentException("Bad id: " + id); |
453 | } |
454 | return popBackStackState(mActivity.mHandler, null, id, flags); |
455 | } |
456 | |
457 | @Override |
458 | public int getBackStackEntryCount() { |
459 | return mBackStack != null ? mBackStack.size() : 0; |
460 | } |
461 | |
462 | @Override |
463 | public BackStackEntry getBackStackEntryAt(int index) { |
464 | return mBackStack.get(index); |
465 | } |
466 | |
467 | @Override |
468 | public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { |
469 | if (mBackStackChangeListeners == null) { |
470 | mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>(); |
471 | } |
472 | mBackStackChangeListeners.add(listener); |
473 | } |
474 | |
475 | @Override |
476 | public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { |
477 | if (mBackStackChangeListeners != null) { |
478 | mBackStackChangeListeners.remove(listener); |
479 | } |
480 | } |
481 | |
482 | @Override |
483 | public void putFragment(Bundle bundle, String key, Fragment fragment) { |
484 | if (fragment.mIndex < 0) { |
485 | throw new IllegalStateException("Fragment " + fragment |
486 | + " is not currently in the FragmentManager"); |
487 | } |
488 | bundle.putInt(key, fragment.mIndex); |
489 | } |
490 | |
491 | @Override |
492 | public Fragment getFragment(Bundle bundle, String key) { |
493 | int index = bundle.getInt(key, -1); |
494 | if (index == -1) { |
495 | return null; |
496 | } |
497 | if (index >= mActive.size()) { |
498 | throw new IllegalStateException("Fragement no longer exists for key " |
499 | + key + ": index " + index); |
500 | } |
501 | Fragment f = mActive.get(index); |
502 | if (f == null) { |
503 | throw new IllegalStateException("Fragement no longer exists for key " |
504 | + key + ": index " + index); |
505 | } |
506 | return f; |
507 | } |
508 | |
509 | @Override |
510 | public String toString() { |
511 | StringBuilder sb = new StringBuilder(128); |
512 | sb.append("FragmentManager{"); |
513 | sb.append(Integer.toHexString(System.identityHashCode(this))); |
514 | sb.append(" in "); |
515 | DebugUtils.buildShortClassTag(mActivity, sb); |
516 | sb.append("}}"); |
517 | return sb.toString(); |
518 | } |
519 | |
520 | @Override |
521 | public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { |
522 | String innerPrefix = prefix + " "; |
523 | |
524 | int N; |
525 | if (mActive != null) { |
526 | N = mActive.size(); |
527 | if (N > 0) { |
528 | writer.print(prefix); writer.print("Active Fragments in "); |
529 | writer.print(Integer.toHexString(System.identityHashCode(this))); |
530 | writer.println(":"); |
531 | for (int i=0; i<N; i++) { |
532 | Fragment f = mActive.get(i); |
533 | writer.print(prefix); writer.print(" #"); writer.print(i); |
534 | writer.print(": "); writer.println(f); |
535 | if (f != null) { |
536 | f.dump(innerPrefix, fd, writer, args); |
537 | } |
538 | } |
539 | } |
540 | } |
541 | |
542 | if (mAdded != null) { |
543 | N = mAdded.size(); |
544 | if (N > 0) { |
545 | writer.print(prefix); writer.println("Added Fragments:"); |
546 | for (int i=0; i<N; i++) { |
547 | Fragment f = mAdded.get(i); |
548 | writer.print(prefix); writer.print(" #"); writer.print(i); |
549 | writer.print(": "); writer.println(f.toString()); |
550 | } |
551 | } |
552 | } |
553 | |
554 | if (mCreatedMenus != null) { |
555 | N = mCreatedMenus.size(); |
556 | if (N > 0) { |
557 | writer.print(prefix); writer.println("Fragments Created Menus:"); |
558 | for (int i=0; i<N; i++) { |
559 | Fragment f = mCreatedMenus.get(i); |
560 | writer.print(prefix); writer.print(" #"); writer.print(i); |
561 | writer.print(": "); writer.println(f.toString()); |
562 | } |
563 | } |
564 | } |
565 | |
566 | if (mBackStack != null) { |
567 | N = mBackStack.size(); |
568 | if (N > 0) { |
569 | writer.print(prefix); writer.println("Back Stack:"); |
570 | for (int i=0; i<N; i++) { |
571 | BackStackRecord bs = mBackStack.get(i); |
572 | writer.print(prefix); writer.print(" #"); writer.print(i); |
573 | writer.print(": "); writer.println(bs.toString()); |
574 | bs.dump(innerPrefix, fd, writer, args); |
575 | } |
576 | } |
577 | } |
578 | |
579 | synchronized (this) { |
580 | if (mBackStackIndices != null) { |
581 | N = mBackStackIndices.size(); |
582 | if (N > 0) { |
583 | writer.print(prefix); writer.println("Back Stack Indices:"); |
584 | for (int i=0; i<N; i++) { |
585 | BackStackRecord bs = mBackStackIndices.get(i); |
586 | writer.print(prefix); writer.print(" #"); writer.print(i); |
587 | writer.print(": "); writer.println(bs); |
588 | } |
589 | } |
590 | } |
591 | |
592 | if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) { |
593 | writer.print(prefix); writer.print("mAvailBackStackIndices: "); |
594 | writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); |
595 | } |
596 | } |
597 | |
598 | if (mPendingActions != null) { |
599 | N = mPendingActions.size(); |
600 | if (N > 0) { |
601 | writer.print(prefix); writer.println("Pending Actions:"); |
602 | for (int i=0; i<N; i++) { |
603 | Runnable r = mPendingActions.get(i); |
604 | writer.print(prefix); writer.print(" #"); writer.print(i); |
605 | writer.print(": "); writer.println(r); |
606 | } |
607 | } |
608 | } |
609 | |
610 | writer.print(prefix); writer.println("FragmentManager misc state:"); |
611 | writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState); |
612 | writer.print(" mStateSaved="); writer.print(mStateSaved); |
613 | writer.print(" mDestroyed="); writer.println(mDestroyed); |
614 | if (mNeedMenuInvalidate) { |
615 | writer.print(prefix); writer.print(" mNeedMenuInvalidate="); |
616 | writer.println(mNeedMenuInvalidate); |
617 | } |
618 | if (mNoTransactionsBecause != null) { |
619 | writer.print(prefix); writer.print(" mNoTransactionsBecause="); |
620 | writer.println(mNoTransactionsBecause); |
621 | } |
622 | if (mAvailIndices != null && mAvailIndices.size() > 0) { |
623 | writer.print(prefix); writer.print(" mAvailIndices: "); |
624 | writer.println(Arrays.toString(mAvailIndices.toArray())); |
625 | } |
626 | } |
627 | |
628 | static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); |
629 | static final Interpolator DECELERATE_CUBIC = new DecelerateInterpolator(1.5f); |
630 | static final Interpolator ACCELERATE_QUINT = new AccelerateInterpolator(2.5f); |
631 | static final Interpolator ACCELERATE_CUBIC = new AccelerateInterpolator(1.5f); |
632 | |
633 | static final int ANIM_DUR = 220; |
634 | |
635 | static Animation makeOpenCloseAnimation(Context context, float startScale, |
636 | float endScale, float startAlpha, float endAlpha) { |
637 | AnimationSet set = new AnimationSet(false); |
638 | ScaleAnimation scale = new ScaleAnimation(startScale, endScale, startScale, endScale, |
639 | Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f); |
640 | scale.setInterpolator(DECELERATE_QUINT); |
641 | scale.setDuration(ANIM_DUR); |
642 | set.addAnimation(scale); |
643 | AlphaAnimation alpha = new AlphaAnimation(startAlpha, endAlpha); |
644 | alpha.setInterpolator(DECELERATE_CUBIC); |
645 | alpha.setDuration(ANIM_DUR); |
646 | set.addAnimation(alpha); |
647 | return set; |
648 | } |
649 | |
650 | static Animation makeFadeAnimation(Context context, float start, float end) { |
651 | AlphaAnimation anim = new AlphaAnimation(start, end); |
652 | anim.setInterpolator(DECELERATE_CUBIC); |
653 | anim.setDuration(ANIM_DUR); |
654 | return anim; |
655 | } |
656 | |
657 | Animation loadAnimation(Fragment fragment, int transit, boolean enter, |
658 | int transitionStyle) { |
659 | Animation animObj = fragment.onCreateAnimation(transit, enter, |
660 | fragment.mNextAnim); |
661 | if (animObj != null) { |
662 | return animObj; |
663 | } |
664 | |
665 | if (fragment.mNextAnim != 0) { |
666 | Animation anim = AnimationUtils.loadAnimation(mActivity, fragment.mNextAnim); |
667 | if (anim != null) { |
668 | return anim; |
669 | } |
670 | } |
671 | |
672 | if (transit == 0) { |
673 | return null; |
674 | } |
675 | |
676 | int styleIndex = transitToStyleIndex(transit, enter); |
677 | if (styleIndex < 0) { |
678 | return null; |
679 | } |
680 | |
681 | switch (styleIndex) { |
682 | case ANIM_STYLE_OPEN_ENTER: |
683 | return makeOpenCloseAnimation(mActivity, 1.125f, 1.0f, 0, 1); |
684 | case ANIM_STYLE_OPEN_EXIT: |
685 | return makeOpenCloseAnimation(mActivity, 1.0f, .975f, 1, 0); |
686 | case ANIM_STYLE_CLOSE_ENTER: |
687 | return makeOpenCloseAnimation(mActivity, .975f, 1.0f, 0, 1); |
688 | case ANIM_STYLE_CLOSE_EXIT: |
689 | return makeOpenCloseAnimation(mActivity, 1.0f, 1.075f, 1, 0); |
690 | case ANIM_STYLE_FADE_ENTER: |
691 | return makeFadeAnimation(mActivity, 0, 1); |
692 | case ANIM_STYLE_FADE_EXIT: |
693 | return makeFadeAnimation(mActivity, 1, 0); |
694 | } |
695 | |
696 | if (transitionStyle == 0 && mActivity.getWindow() != null) { |
697 | transitionStyle = mActivity.getWindow().getAttributes().windowAnimations; |
698 | } |
699 | if (transitionStyle == 0) { |
700 | return null; |
701 | } |
702 | |
703 | //TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, |
704 | // com.android.internal.R.styleable.FragmentAnimation); |
705 | //int anim = attrs.getResourceId(styleIndex, 0); |
706 | //attrs.recycle(); |
707 | |
708 | //if (anim == 0) { |
709 | // return null; |
710 | //} |
711 | |
712 | //return AnimatorInflater.loadAnimator(mActivity, anim); |
713 | return null; |
714 | } |
715 | |
716 | void moveToState(Fragment f, int newState, int transit, int transitionStyle) { |
717 | // Fragments that are not currently added will sit in the onCreate() state. |
718 | if (!f.mAdded && newState > Fragment.CREATED) { |
719 | newState = Fragment.CREATED; |
720 | } |
721 | |
722 | if (f.mState < newState) { |
723 | // For fragments that are created from a layout, when restoring from |
724 | // state we don't want to allow them to be created until they are |
725 | // being reloaded from the layout. |
726 | if (f.mFromLayout && !f.mInLayout) { |
727 | return; |
728 | } |
729 | if (f.mAnimatingAway != null) { |
730 | // The fragment is currently being animated... but! Now we |
731 | // want to move our state back up. Give up on waiting for the |
732 | // animation, move to whatever the final state should be once |
733 | // the animation is done, and then we can proceed from there. |
734 | f.mAnimatingAway = null; |
735 | moveToState(f, f.mStateAfterAnimating, 0, 0); |
736 | } |
737 | switch (f.mState) { |
738 | case Fragment.INITIALIZING: |
739 | if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); |
740 | if (f.mSavedFragmentState != null) { |
741 | f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( |
742 | FragmentManagerImpl.VIEW_STATE_TAG); |
743 | f.mTarget = getFragment(f.mSavedFragmentState, |
744 | FragmentManagerImpl.TARGET_STATE_TAG); |
745 | if (f.mTarget != null) { |
746 | f.mTargetRequestCode = f.mSavedFragmentState.getInt( |
747 | FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); |
748 | } |
749 | } |
750 | f.mActivity = mActivity; |
751 | f.mFragmentManager = mActivity.mFragments; |
752 | f.mCalled = false; |
753 | f.onAttach(mActivity); |
754 | if (!f.mCalled) { |
755 | throw new SuperNotCalledException("Fragment " + f |
756 | + " did not call through to super.onAttach()"); |
757 | } |
758 | mActivity.onAttachFragment(f); |
759 | |
760 | if (!f.mRetaining) { |
761 | f.mCalled = false; |
762 | f.onCreate(f.mSavedFragmentState); |
763 | if (!f.mCalled) { |
764 | throw new SuperNotCalledException("Fragment " + f |
765 | + " did not call through to super.onCreate()"); |
766 | } |
767 | } |
768 | f.mRetaining = false; |
769 | if (f.mFromLayout) { |
770 | // For fragments that are part of the content view |
771 | // layout, we need to instantiate the view immediately |
772 | // and the inflater will take care of adding it. |
773 | f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), |
774 | null, f.mSavedFragmentState); |
775 | if (f.mView != null) { |
776 | f.mInnerView = f.mView; |
777 | f.mView = NoSaveStateFrameLayout.wrap(f.mView); |
778 | f.restoreViewState(); |
779 | if (f.mHidden) f.mView.setVisibility(View.GONE); |
780 | } else { |
781 | f.mInnerView = null; |
782 | } |
783 | } |
784 | case Fragment.CREATED: |
785 | if (newState > Fragment.CREATED) { |
786 | if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f); |
787 | if (!f.mFromLayout) { |
788 | ViewGroup container = null; |
789 | if (f.mContainerId != 0) { |
790 | container = (ViewGroup)mActivity.findViewById(f.mContainerId); |
791 | if (container == null && !f.mRestored) { |
792 | throw new IllegalArgumentException("No view found for id 0x" |
793 | + Integer.toHexString(f.mContainerId) |
794 | + " for fragment " + f); |
795 | } |
796 | } |
797 | f.mContainer = container; |
798 | f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), |
799 | container, f.mSavedFragmentState); |
800 | if (f.mView != null) { |
801 | f.mInnerView = f.mView; |
802 | f.mView = NoSaveStateFrameLayout.wrap(f.mView); |
803 | if (container != null) { |
804 | Animation anim = loadAnimation(f, transit, true, |
805 | transitionStyle); |
806 | if (anim != null) { |
807 | f.mView.startAnimation(anim); |
808 | } |
809 | container.addView(f.mView); |
810 | f.restoreViewState(); |
811 | } |
812 | if (f.mHidden) f.mView.setVisibility(View.GONE); |
813 | } else { |
814 | f.mInnerView = null; |
815 | } |
816 | } |
817 | |
818 | f.mCalled = false; |
819 | f.onActivityCreated(f.mSavedFragmentState); |
820 | if (!f.mCalled) { |
821 | throw new SuperNotCalledException("Fragment " + f |
822 | + " did not call through to super.onActivityCreated()"); |
823 | } |
824 | f.mSavedFragmentState = null; |
825 | } |
826 | case Fragment.ACTIVITY_CREATED: |
827 | if (newState > Fragment.ACTIVITY_CREATED) { |
828 | if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); |
829 | f.mCalled = false; |
830 | f.onStart(); |
831 | if (!f.mCalled) { |
832 | throw new SuperNotCalledException("Fragment " + f |
833 | + " did not call through to super.onStart()"); |
834 | } |
835 | } |
836 | case Fragment.STARTED: |
837 | if (newState > Fragment.STARTED) { |
838 | if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); |
839 | f.mCalled = false; |
840 | f.mResumed = true; |
841 | f.onResume(); |
842 | if (!f.mCalled) { |
843 | throw new SuperNotCalledException("Fragment " + f |
844 | + " did not call through to super.onResume()"); |
845 | } |
846 | } |
847 | } |
848 | } else if (f.mState > newState) { |
849 | switch (f.mState) { |
850 | case Fragment.RESUMED: |
851 | if (newState < Fragment.RESUMED) { |
852 | if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); |
853 | f.mCalled = false; |
854 | f.onPause(); |
855 | if (!f.mCalled) { |
856 | throw new SuperNotCalledException("Fragment " + f |
857 | + " did not call through to super.onPause()"); |
858 | } |
859 | f.mResumed = false; |
860 | } |
861 | case Fragment.STARTED: |
862 | if (newState < Fragment.STARTED) { |
863 | if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); |
864 | f.mCalled = false; |
865 | f.performStop(); |
866 | if (!f.mCalled) { |
867 | throw new SuperNotCalledException("Fragment " + f |
868 | + " did not call through to super.onStop()"); |
869 | } |
870 | } |
871 | case Fragment.ACTIVITY_CREATED: |
872 | if (newState < Fragment.ACTIVITY_CREATED) { |
873 | if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f); |
874 | if (f.mView != null) { |
875 | // Need to save the current view state if not |
876 | // done already. |
877 | if (!mActivity.isFinishing() && f.mSavedViewState == null) { |
878 | saveFragmentViewState(f); |
879 | } |
880 | } |
881 | f.mCalled = false; |
882 | f.onDestroyView(); |
883 | if (!f.mCalled) { |
884 | throw new SuperNotCalledException("Fragment " + f |
885 | + " did not call through to super.onDestroyView()"); |
886 | } |
887 | if (f.mView != null && f.mContainer != null) { |
888 | Animation anim = null; |
889 | if (mCurState > Fragment.INITIALIZING && !mDestroyed) { |
890 | anim = loadAnimation(f, transit, false, |
891 | transitionStyle); |
892 | } |
893 | if (anim != null) { |
894 | final Fragment fragment = f; |
895 | f.mAnimatingAway = f.mView; |
896 | f.mStateAfterAnimating = newState; |
897 | anim.setAnimationListener(new AnimationListener() { |
898 | @Override |
899 | public void onAnimationEnd(Animation animation) { |
900 | if (fragment.mAnimatingAway != null) { |
901 | fragment.mAnimatingAway = null; |
902 | moveToState(fragment, fragment.mStateAfterAnimating, |
903 | 0, 0); |
904 | } |
905 | } |
906 | @Override |
907 | public void onAnimationRepeat(Animation animation) { |
908 | } |
909 | @Override |
910 | public void onAnimationStart(Animation animation) { |
911 | } |
912 | }); |
913 | f.mView.startAnimation(anim); |
914 | } |
915 | f.mContainer.removeView(f.mView); |
916 | } |
917 | f.mContainer = null; |
918 | f.mView = null; |
919 | f.mInnerView = null; |
920 | } |
921 | case Fragment.CREATED: |
922 | if (newState < Fragment.CREATED) { |
923 | if (mDestroyed) { |
924 | if (f.mAnimatingAway != null) { |
925 | // The fragment's containing activity is |
926 | // being destroyed, but this fragment is |
927 | // currently animating away. Stop the |
928 | // animation right now -- it is not needed, |
929 | // and we can't wait any more on destroying |
930 | // the fragment. |
931 | View v = f.mAnimatingAway; |
932 | f.mAnimatingAway = null; |
933 | v.clearAnimation(); |
934 | } |
935 | } |
936 | if (f.mAnimatingAway != null) { |
937 | // We are waiting for the fragment's view to finish |
938 | // animating away. Just make a note of the state |
939 | // the fragment now should move to once the animation |
940 | // is done. |
941 | f.mStateAfterAnimating = newState; |
942 | } else { |
943 | if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); |
944 | if (!f.mRetaining) { |
945 | f.mCalled = false; |
946 | f.onDestroy(); |
947 | if (!f.mCalled) { |
948 | throw new SuperNotCalledException("Fragment " + f |
949 | + " did not call through to super.onDestroy()"); |
950 | } |
951 | } |
952 | |
953 | f.mCalled = false; |
954 | f.onDetach(); |
955 | if (!f.mCalled) { |
956 | throw new SuperNotCalledException("Fragment " + f |
957 | + " did not call through to super.onDetach()"); |
958 | } |
959 | f.mImmediateActivity = null; |
960 | f.mActivity = null; |
961 | f.mFragmentManager = null; |
962 | } |
963 | } |
964 | } |
965 | } |
966 | |
967 | f.mState = newState; |
968 | } |
969 | |
970 | void moveToState(Fragment f) { |
971 | moveToState(f, mCurState, 0, 0); |
972 | } |
973 | |
974 | void moveToState(int newState, boolean always) { |
975 | moveToState(newState, 0, 0, always); |
976 | } |
977 | |
978 | void moveToState(int newState, int transit, int transitStyle, boolean always) { |
979 | if (mActivity == null && newState != Fragment.INITIALIZING) { |
980 | throw new IllegalStateException("No activity"); |
981 | } |
982 | |
983 | if (!always && mCurState == newState) { |
984 | return; |
985 | } |
986 | |
987 | mCurState = newState; |
988 | if (mActive != null) { |
989 | for (int i=0; i<mActive.size(); i++) { |
990 | Fragment f = mActive.get(i); |
991 | if (f != null) { |
992 | moveToState(f, newState, transit, transitStyle); |
993 | } |
994 | } |
995 | |
996 | if (mNeedMenuInvalidate && mActivity != null) { |
997 | mActivity.supportInvalidateOptionsMenu(); |
998 | mNeedMenuInvalidate = false; |
999 | } |
1000 | } |
1001 | } |
1002 | |
1003 | void makeActive(Fragment f) { |
1004 | if (f.mIndex >= 0) { |
1005 | return; |
1006 | } |
1007 | |
1008 | if (mAvailIndices == null || mAvailIndices.size() <= 0) { |
1009 | if (mActive == null) { |
1010 | mActive = new ArrayList<Fragment>(); |
1011 | } |
1012 | f.setIndex(mActive.size()); |
1013 | mActive.add(f); |
1014 | |
1015 | } else { |
1016 | f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1)); |
1017 | mActive.set(f.mIndex, f); |
1018 | } |
1019 | } |
1020 | |
1021 | void makeInactive(Fragment f) { |
1022 | if (f.mIndex < 0) { |
1023 | return; |
1024 | } |
1025 | |
1026 | if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex); |
1027 | mActive.set(f.mIndex, null); |
1028 | if (mAvailIndices == null) { |
1029 | mAvailIndices = new ArrayList<Integer>(); |
1030 | } |
1031 | mAvailIndices.add(f.mIndex); |
1032 | mActivity.invalidateSupportFragmentIndex(f.mIndex); |
1033 | f.clearIndex(); |
1034 | } |
1035 | |
1036 | public void addFragment(Fragment fragment, boolean moveToStateNow) { |
1037 | if (mAdded == null) { |
1038 | mAdded = new ArrayList<Fragment>(); |
1039 | } |
1040 | mAdded.add(fragment); |
1041 | makeActive(fragment); |
1042 | if (DEBUG) Log.v(TAG, "add: " + fragment); |
1043 | fragment.mAdded = true; |
1044 | fragment.mRemoving = false; |
1045 | if (fragment.mHasMenu) { |
1046 | mNeedMenuInvalidate = true; |
1047 | } |
1048 | if (moveToStateNow) { |
1049 | moveToState(fragment); |
1050 | } |
1051 | } |
1052 | |
1053 | public void removeFragment(Fragment fragment, int transition, int transitionStyle) { |
1054 | if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); |
1055 | mAdded.remove(fragment); |
1056 | final boolean inactive = fragment.mBackStackNesting <= 0; |
1057 | if (fragment.mHasMenu) { |
1058 | mNeedMenuInvalidate = true; |
1059 | } |
1060 | fragment.mAdded = false; |
1061 | fragment.mRemoving = true; |
1062 | moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, |
1063 | transition, transitionStyle); |
1064 | if (inactive) { |
1065 | makeInactive(fragment); |
1066 | } |
1067 | } |
1068 | |
1069 | public void hideFragment(Fragment fragment, int transition, int transitionStyle) { |
1070 | if (DEBUG) Log.v(TAG, "hide: " + fragment); |
1071 | if (!fragment.mHidden) { |
1072 | fragment.mHidden = true; |
1073 | if (fragment.mView != null) { |
1074 | Animation anim = loadAnimation(fragment, transition, true, |
1075 | transitionStyle); |
1076 | if (anim != null) { |
1077 | fragment.mView.startAnimation(anim); |
1078 | } |
1079 | fragment.mView.setVisibility(View.GONE); |
1080 | } |
1081 | if (fragment.mAdded && fragment.mHasMenu) { |
1082 | mNeedMenuInvalidate = true; |
1083 | } |
1084 | fragment.onHiddenChanged(true); |
1085 | } |
1086 | } |
1087 | |
1088 | public void showFragment(Fragment fragment, int transition, int transitionStyle) { |
1089 | if (DEBUG) Log.v(TAG, "show: " + fragment); |
1090 | if (fragment.mHidden) { |
1091 | fragment.mHidden = false; |
1092 | if (fragment.mView != null) { |
1093 | Animation anim = loadAnimation(fragment, transition, true, |
1094 | transitionStyle); |
1095 | if (anim != null) { |
1096 | fragment.mView.startAnimation(anim); |
1097 | } |
1098 | fragment.mView.setVisibility(View.VISIBLE); |
1099 | } |
1100 | if (fragment.mAdded && fragment.mHasMenu) { |
1101 | mNeedMenuInvalidate = true; |
1102 | } |
1103 | fragment.onHiddenChanged(false); |
1104 | } |
1105 | } |
1106 | |
1107 | public Fragment findFragmentById(int id) { |
1108 | if (mActive != null) { |
1109 | // First look through added fragments. |
1110 | for (int i=mAdded.size()-1; i>=0; i--) { |
1111 | Fragment f = mAdded.get(i); |
1112 | if (f != null && f.mFragmentId == id) { |
1113 | return f; |
1114 | } |
1115 | } |
1116 | // Now for any known fragment. |
1117 | for (int i=mActive.size()-1; i>=0; i--) { |
1118 | Fragment f = mActive.get(i); |
1119 | if (f != null && f.mFragmentId == id) { |
1120 | return f; |
1121 | } |
1122 | } |
1123 | } |
1124 | return null; |
1125 | } |
1126 | |
1127 | public Fragment findFragmentByTag(String tag) { |
1128 | if (mActive != null && tag != null) { |
1129 | // First look through added fragments. |
1130 | for (int i=mAdded.size()-1; i>=0; i--) { |
1131 | Fragment f = mAdded.get(i); |
1132 | if (f != null && tag.equals(f.mTag)) { |
1133 | return f; |
1134 | } |
1135 | } |
1136 | // Now for any known fragment. |
1137 | for (int i=mActive.size()-1; i>=0; i--) { |
1138 | Fragment f = mActive.get(i); |
1139 | if (f != null && tag.equals(f.mTag)) { |
1140 | return f; |
1141 | } |
1142 | } |
1143 | } |
1144 | return null; |
1145 | } |
1146 | |
1147 | public Fragment findFragmentByWho(String who) { |
1148 | if (mActive != null && who != null) { |
1149 | for (int i=mActive.size()-1; i>=0; i--) { |
1150 | Fragment f = mActive.get(i); |
1151 | if (f != null && who.equals(f.mWho)) { |
1152 | return f; |
1153 | } |
1154 | } |
1155 | } |
1156 | return null; |
1157 | } |
1158 | |
1159 | private void checkStateLoss() { |
1160 | if (mStateSaved) { |
1161 | throw new IllegalStateException( |
1162 | "Can not perform this action after onSaveInstanceState"); |
1163 | } |
1164 | if (mNoTransactionsBecause != null) { |
1165 | throw new IllegalStateException( |
1166 | "Can not perform this action inside of " + mNoTransactionsBecause); |
1167 | } |
1168 | } |
1169 | |
1170 | public void enqueueAction(Runnable action, boolean allowStateLoss) { |
1171 | if (!allowStateLoss) { |
1172 | checkStateLoss(); |
1173 | } |
1174 | synchronized (this) { |
1175 | if (mActivity == null) { |
1176 | throw new IllegalStateException("Activity has been destroyed"); |
1177 | } |
1178 | if (mPendingActions == null) { |
1179 | mPendingActions = new ArrayList<Runnable>(); |
1180 | } |
1181 | mPendingActions.add(action); |
1182 | if (mPendingActions.size() == 1) { |
1183 | mActivity.mHandler.removeCallbacks(mExecCommit); |
1184 | mActivity.mHandler.post(mExecCommit); |
1185 | } |
1186 | } |
1187 | } |
1188 | |
1189 | public int allocBackStackIndex(BackStackRecord bse) { |
1190 | synchronized (this) { |
1191 | if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { |
1192 | if (mBackStackIndices == null) { |
1193 | mBackStackIndices = new ArrayList<BackStackRecord>(); |
1194 | } |
1195 | int index = mBackStackIndices.size(); |
1196 | if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); |
1197 | mBackStackIndices.add(bse); |
1198 | return index; |
1199 | |
1200 | } else { |
1201 | int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); |
1202 | if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); |
1203 | mBackStackIndices.set(index, bse); |
1204 | return index; |
1205 | } |
1206 | } |
1207 | } |
1208 | |
1209 | public void setBackStackIndex(int index, BackStackRecord bse) { |
1210 | synchronized (this) { |
1211 | if (mBackStackIndices == null) { |
1212 | mBackStackIndices = new ArrayList<BackStackRecord>(); |
1213 | } |
1214 | int N = mBackStackIndices.size(); |
1215 | if (index < N) { |
1216 | if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); |
1217 | mBackStackIndices.set(index, bse); |
1218 | } else { |
1219 | while (N < index) { |
1220 | mBackStackIndices.add(null); |
1221 | if (mAvailBackStackIndices == null) { |
1222 | mAvailBackStackIndices = new ArrayList<Integer>(); |
1223 | } |
1224 | if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); |
1225 | mAvailBackStackIndices.add(N); |
1226 | N++; |
1227 | } |
1228 | if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); |
1229 | mBackStackIndices.add(bse); |
1230 | } |
1231 | } |
1232 | } |
1233 | |
1234 | public void freeBackStackIndex(int index) { |
1235 | synchronized (this) { |
1236 | mBackStackIndices.set(index, null); |
1237 | if (mAvailBackStackIndices == null) { |
1238 | mAvailBackStackIndices = new ArrayList<Integer>(); |
1239 | } |
1240 | if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); |
1241 | mAvailBackStackIndices.add(index); |
1242 | } |
1243 | } |
1244 | |
1245 | /** |
1246 | * Only call from main thread! |
1247 | */ |
1248 | public boolean execPendingActions() { |
1249 | if (mExecutingActions) { |
1250 | throw new IllegalStateException("Recursive entry to executePendingTransactions"); |
1251 | } |
1252 | |
1253 | if (Looper.myLooper() != mActivity.mHandler.getLooper()) { |
1254 | throw new IllegalStateException("Must be called from main thread of process"); |
1255 | } |
1256 | |
1257 | boolean didSomething = false; |
1258 | |
1259 | while (true) { |
1260 | int numActions; |
1261 | |
1262 | synchronized (this) { |
1263 | if (mPendingActions == null || mPendingActions.size() == 0) { |
1264 | return didSomething; |
1265 | } |
1266 | |
1267 | numActions = mPendingActions.size(); |
1268 | if (mTmpActions == null || mTmpActions.length < numActions) { |
1269 | mTmpActions = new Runnable[numActions]; |
1270 | } |
1271 | mPendingActions.toArray(mTmpActions); |
1272 | mPendingActions.clear(); |
1273 | mActivity.mHandler.removeCallbacks(mExecCommit); |
1274 | } |
1275 | |
1276 | mExecutingActions = true; |
1277 | for (int i=0; i<numActions; i++) { |
1278 | mTmpActions[i].run(); |
1279 | } |
1280 | mExecutingActions = false; |
1281 | didSomething = true; |
1282 | } |
1283 | } |
1284 | |
1285 | void reportBackStackChanged() { |
1286 | if (mBackStackChangeListeners != null) { |
1287 | for (int i=0; i<mBackStackChangeListeners.size(); i++) { |
1288 | mBackStackChangeListeners.get(i).onBackStackChanged(); |
1289 | } |
1290 | } |
1291 | } |
1292 | |
1293 | void addBackStackState(BackStackRecord state) { |
1294 | if (mBackStack == null) { |
1295 | mBackStack = new ArrayList<BackStackRecord>(); |
1296 | } |
1297 | mBackStack.add(state); |
1298 | reportBackStackChanged(); |
1299 | } |
1300 | |
1301 | boolean popBackStackState(Handler handler, String name, int id, int flags) { |
1302 | if (mBackStack == null) { |
1303 | return false; |
1304 | } |
1305 | if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { |
1306 | int last = mBackStack.size()-1; |
1307 | if (last < 0) { |
1308 | return false; |
1309 | } |
1310 | final BackStackRecord bss = mBackStack.remove(last); |
1311 | bss.popFromBackStack(true); |
1312 | reportBackStackChanged(); |
1313 | } else { |
1314 | int index = -1; |
1315 | if (name != null || id >= 0) { |
1316 | // If a name or ID is specified, look for that place in |
1317 | // the stack. |
1318 | index = mBackStack.size()-1; |
1319 | while (index >= 0) { |
1320 | BackStackRecord bss = mBackStack.get(index); |
1321 | if (name != null && name.equals(bss.getName())) { |
1322 | break; |
1323 | } |
1324 | if (id >= 0 && id == bss.mIndex) { |
1325 | break; |
1326 | } |
1327 | index--; |
1328 | } |
1329 | if (index < 0) { |
1330 | return false; |
1331 | } |
1332 | if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { |
1333 | index--; |
1334 | // Consume all following entries that match. |
1335 | while (index >= 0) { |
1336 | BackStackRecord bss = mBackStack.get(index); |
1337 | if ((name != null && name.equals(bss.getName())) |
1338 | || (id >= 0 && id == bss.mIndex)) { |
1339 | index--; |
1340 | continue; |
1341 | } |
1342 | break; |
1343 | } |
1344 | } |
1345 | } |
1346 | if (index == mBackStack.size()-1) { |
1347 | return false; |
1348 | } |
1349 | final ArrayList<BackStackRecord> states |
1350 | = new ArrayList<BackStackRecord>(); |
1351 | for (int i=mBackStack.size()-1; i>index; i--) { |
1352 | states.add(mBackStack.remove(i)); |
1353 | } |
1354 | final int LAST = states.size()-1; |
1355 | for (int i=0; i<=LAST; i++) { |
1356 | if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); |
1357 | states.get(i).popFromBackStack(i == LAST); |
1358 | } |
1359 | reportBackStackChanged(); |
1360 | } |
1361 | return true; |
1362 | } |
1363 | |
1364 | ArrayList<Fragment> retainNonConfig() { |
1365 | ArrayList<Fragment> fragments = null; |
1366 | if (mActive != null) { |
1367 | for (int i=0; i<mActive.size(); i++) { |
1368 | Fragment f = mActive.get(i); |
1369 | if (f != null && f.mRetainInstance) { |
1370 | if (fragments == null) { |
1371 | fragments = new ArrayList<Fragment>(); |
1372 | } |
1373 | fragments.add(f); |
1374 | f.mRetaining = true; |
1375 | } |
1376 | } |
1377 | } |
1378 | return fragments; |
1379 | } |
1380 | |
1381 | void saveFragmentViewState(Fragment f) { |
1382 | if (f.mInnerView == null) { |
1383 | return; |
1384 | } |
1385 | if (mStateArray == null) { |
1386 | mStateArray = new SparseArray<Parcelable>(); |
1387 | } |
1388 | f.mInnerView.saveHierarchyState(mStateArray); |
1389 | if (mStateArray.size() > 0) { |
1390 | f.mSavedViewState = mStateArray; |
1391 | mStateArray = null; |
1392 | } |
1393 | } |
1394 | |
1395 | Parcelable saveAllState() { |
1396 | // Make sure all pending operations have now been executed to get |
1397 | // our state update-to-date. |
1398 | execPendingActions(); |
1399 | |
1400 | if (HONEYCOMB) { |
1401 | // As of Honeycomb, we save state after pausing. Prior to that |
1402 | // it is before pausing. With fragments this is an issue, since |
1403 | // there are many things you may do after pausing but before |
1404 | // stopping that change the fragment state. For those older |
1405 | // devices, we will not at this point say that we have saved |
1406 | // the state, so we will allow them to continue doing fragment |
1407 | // transactions. This retains the same semantics as Honeycomb, |
1408 | // though you do have the risk of losing the very most recent state |
1409 | // if the process is killed... we'll live with that. |
1410 | mStateSaved = true; |
1411 | } |
1412 | |
1413 | if (mActive == null || mActive.size() <= 0) { |
1414 | return null; |
1415 | } |
1416 | |
1417 | // First collect all active fragments. |
1418 | int N = mActive.size(); |
1419 | FragmentState[] active = new FragmentState[N]; |
1420 | boolean haveFragments = false; |
1421 | for (int i=0; i<N; i++) { |
1422 | Fragment f = mActive.get(i); |
1423 | if (f != null) { |
1424 | haveFragments = true; |
1425 | |
1426 | FragmentState fs = new FragmentState(f); |
1427 | active[i] = fs; |
1428 | |
1429 | if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { |
1430 | if (mStateBundle == null) { |
1431 | mStateBundle = new Bundle(); |
1432 | } |
1433 | f.onSaveInstanceState(mStateBundle); |
1434 | if (!mStateBundle.isEmpty()) { |
1435 | fs.mSavedFragmentState = mStateBundle; |
1436 | mStateBundle = null; |
1437 | } |
1438 | |
1439 | if (f.mView != null) { |
1440 | saveFragmentViewState(f); |
1441 | if (f.mSavedViewState != null) { |
1442 | if (fs.mSavedFragmentState == null) { |
1443 | fs.mSavedFragmentState = new Bundle(); |
1444 | } |
1445 | fs.mSavedFragmentState.putSparseParcelableArray( |
1446 | FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); |
1447 | } |
1448 | } |
1449 | |
1450 | if (f.mTarget != null) { |
1451 | if (f.mTarget.mIndex < 0) { |
1452 | String msg = "Failure saving state: " + f |
1453 | + " has target not in fragment manager: " + f.mTarget; |
1454 | Log.e(TAG, msg); |
1455 | dump(" ", null, new PrintWriter(new LogWriter(TAG)), new String[] { }); |
1456 | throw new IllegalStateException(msg); |
1457 | } |
1458 | if (fs.mSavedFragmentState == null) { |
1459 | fs.mSavedFragmentState = new Bundle(); |
1460 | } |
1461 | putFragment(fs.mSavedFragmentState, |
1462 | FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); |
1463 | if (f.mTargetRequestCode != 0) { |
1464 | fs.mSavedFragmentState.putInt( |
1465 | FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, |
1466 | f.mTargetRequestCode); |
1467 | } |
1468 | } |
1469 | |
1470 | } else { |
1471 | fs.mSavedFragmentState = f.mSavedFragmentState; |
1472 | } |
1473 | |
1474 | if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " |
1475 | + fs.mSavedFragmentState); |
1476 | } |
1477 | } |
1478 | |
1479 | if (!haveFragments) { |
1480 | if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); |
1481 | return null; |
1482 | } |
1483 | |
1484 | int[] added = null; |
1485 | BackStackState[] backStack = null; |
1486 | |
1487 | // Build list of currently added fragments. |
1488 | if (mAdded != null) { |
1489 | N = mAdded.size(); |
1490 | if (N > 0) { |
1491 | added = new int[N]; |
1492 | for (int i=0; i<N; i++) { |
1493 | added[i] = mAdded.get(i).mIndex; |
1494 | if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i |
1495 | + ": " + mAdded.get(i)); |
1496 | } |
1497 | } |
1498 | } |
1499 | |
1500 | // Now save back stack. |
1501 | if (mBackStack != null) { |
1502 | N = mBackStack.size(); |
1503 | if (N > 0) { |
1504 | backStack = new BackStackState[N]; |
1505 | for (int i=0; i<N; i++) { |
1506 | backStack[i] = new BackStackState(this, mBackStack.get(i)); |
1507 | if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i |
1508 | + ": " + mBackStack.get(i)); |
1509 | } |
1510 | } |
1511 | } |
1512 | |
1513 | FragmentManagerState fms = new FragmentManagerState(); |
1514 | fms.mActive = active; |
1515 | fms.mAdded = added; |
1516 | fms.mBackStack = backStack; |
1517 | return fms; |
1518 | } |
1519 | |
1520 | void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { |
1521 | // If there is no saved state at all, then there can not be |
1522 | // any nonConfig fragments either, so that is that. |
1523 | if (state == null) return; |
1524 | FragmentManagerState fms = (FragmentManagerState)state; |
1525 | if (fms.mActive == null) return; |
1526 | |
1527 | // First re-attach any non-config instances we are retaining back |
1528 | // to their saved state, so we don't try to instantiate them again. |
1529 | if (nonConfig != null) { |
1530 | for (int i=0; i<nonConfig.size(); i++) { |
1531 | Fragment f = nonConfig.get(i); |
1532 | if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); |
1533 | FragmentState fs = fms.mActive[f.mIndex]; |
1534 | fs.mInstance = f; |
1535 | f.mSavedViewState = null; |
1536 | f.mBackStackNesting = 0; |
1537 | f.mInLayout = false; |
1538 | f.mAdded = false; |
1539 | if (fs.mSavedFragmentState != null) { |
1540 | fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); |
1541 | f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( |
1542 | FragmentManagerImpl.VIEW_STATE_TAG); |
1543 | } |
1544 | } |
1545 | } |
1546 | |
1547 | // Build the full list of active fragments, instantiating them from |
1548 | // their saved state. |
1549 | mActive = new ArrayList<Fragment>(fms.mActive.length); |
1550 | if (mAvailIndices != null) { |
1551 | mAvailIndices.clear(); |
1552 | } |
1553 | for (int i=0; i<fms.mActive.length; i++) { |
1554 | FragmentState fs = fms.mActive[i]; |
1555 | if (fs != null) { |
1556 | Fragment f = fs.instantiate(mActivity); |
1557 | if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f); |
1558 | mActive.add(f); |
1559 | // Now that the fragment is instantiated (or came from being |
1560 | // retained above), clear mInstance in case we end up re-restoring |
1561 | // from this FragmentState again. |
1562 | fs.mInstance = null; |
1563 | } else { |
1564 | if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)"); |
1565 | mActive.add(null); |
1566 | if (mAvailIndices == null) { |
1567 | mAvailIndices = new ArrayList<Integer>(); |
1568 | } |
1569 | if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i); |
1570 | mAvailIndices.add(i); |
1571 | } |
1572 | } |
1573 | |
1574 | // Update the target of all retained fragments. |
1575 | if (nonConfig != null) { |
1576 | for (int i=0; i<nonConfig.size(); i++) { |
1577 | Fragment f = nonConfig.get(i); |
1578 | if (f.mTarget != null) { |
1579 | if (f.mTarget.mIndex < mActive.size()) { |
1580 | f.mTarget = mActive.get(f.mTarget.mIndex); |
1581 | } else { |
1582 | Log.w(TAG, "Re-attaching retained fragment " + f |
1583 | + " target no longer exists: " + f.mTarget); |
1584 | f.mTarget = null; |
1585 | } |
1586 | } |
1587 | } |
1588 | } |
1589 | |
1590 | // Build the list of currently added fragments. |
1591 | if (fms.mAdded != null) { |
1592 | mAdded = new ArrayList<Fragment>(fms.mAdded.length); |
1593 | for (int i=0; i<fms.mAdded.length; i++) { |
1594 | Fragment f = mActive.get(fms.mAdded[i]); |
1595 | if (f == null) { |
1596 | throw new IllegalStateException( |
1597 | "No instantiated fragment for index #" + fms.mAdded[i]); |
1598 | } |
1599 | f.mAdded = true; |
1600 | f.mImmediateActivity = mActivity; |
1601 | if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f); |
1602 | mAdded.add(f); |
1603 | } |
1604 | } else { |
1605 | mAdded = null; |
1606 | } |
1607 | |
1608 | // Build the back stack. |
1609 | if (fms.mBackStack != null) { |
1610 | mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); |
1611 | for (int i=0; i<fms.mBackStack.length; i++) { |
1612 | BackStackRecord bse = fms.mBackStack[i].instantiate(this); |
1613 | if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i |
1614 | + " (index " + bse.mIndex + "): " + bse); |
1615 | mBackStack.add(bse); |
1616 | if (bse.mIndex >= 0) { |
1617 | setBackStackIndex(bse.mIndex, bse); |
1618 | } |
1619 | } |
1620 | } else { |
1621 | mBackStack = null; |
1622 | } |
1623 | } |
1624 | |
1625 | public void attachActivity(FragmentActivity activity) { |
1626 | if (mActivity != null) throw new IllegalStateException(); |
1627 | mActivity = activity; |
1628 | } |
1629 | |
1630 | public void noteStateNotSaved() { |
1631 | mStateSaved = false; |
1632 | } |
1633 | |
1634 | public void dispatchCreate() { |
1635 | mStateSaved = false; |
1636 | moveToState(Fragment.CREATED, false); |
1637 | } |
1638 | |
1639 | public void dispatchActivityCreated() { |
1640 | mStateSaved = false; |
1641 | moveToState(Fragment.ACTIVITY_CREATED, false); |
1642 | } |
1643 | |
1644 | public void dispatchStart() { |
1645 | mStateSaved = false; |
1646 | moveToState(Fragment.STARTED, false); |
1647 | } |
1648 | |
1649 | public void dispatchResume() { |
1650 | mStateSaved = false; |
1651 | moveToState(Fragment.RESUMED, false); |
1652 | } |
1653 | |
1654 | public void dispatchPause() { |
1655 | moveToState(Fragment.STARTED, false); |
1656 | } |
1657 | |
1658 | public void dispatchStop() { |
1659 | // See saveAllState() for the explanation of this. We do this for |
1660 | // all platform versions, to keep our behavior more consistent between |
1661 | // them. |
1662 | mStateSaved = true; |
1663 | |
1664 | moveToState(Fragment.ACTIVITY_CREATED, false); |
1665 | } |
1666 | |
1667 | public void dispatchReallyStop(boolean retaining) { |
1668 | if (mActive != null) { |
1669 | for (int i=0; i<mAdded.size(); i++) { |
1670 | Fragment f = mAdded.get(i); |
1671 | if (f != null) { |
1672 | f.performReallyStop(retaining); |
1673 | } |
1674 | } |
1675 | } |
1676 | } |
1677 | |
1678 | public void dispatchDestroy() { |
1679 | mDestroyed = true; |
1680 | moveToState(Fragment.INITIALIZING, false); |
1681 | mActivity = null; |
1682 | } |
1683 | |
1684 | public void dispatchConfigurationChanged(Configuration newConfig) { |
1685 | if (mActive != null) { |
1686 | for (int i=0; i<mAdded.size(); i++) { |
1687 | Fragment f = mAdded.get(i); |
1688 | if (f != null) { |
1689 | f.onConfigurationChanged(newConfig); |
1690 | } |
1691 | } |
1692 | } |
1693 | } |
1694 | |
1695 | public void dispatchLowMemory() { |
1696 | if (mActive != null) { |
1697 | for (int i=0; i<mAdded.size(); i++) { |
1698 | Fragment f = mAdded.get(i); |
1699 | if (f != null) { |
1700 | f.onLowMemory(); |
1701 | } |
1702 | } |
1703 | } |
1704 | } |
1705 | |
1706 | public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { |
1707 | boolean show = false; |
1708 | ArrayList<Fragment> newMenus = null; |
1709 | if (mActive != null) { |
1710 | for (int i=0; i<mAdded.size(); i++) { |
1711 | Fragment f = mAdded.get(i); |
1712 | if (f != null && !f.mHidden && f.mHasMenu) { |
1713 | show = true; |
1714 | f.onCreateOptionsMenu(menu, inflater); |
1715 | if (newMenus == null) { |
1716 | newMenus = new ArrayList<Fragment>(); |
1717 | } |
1718 | newMenus.add(f); |
1719 | } |
1720 | } |
1721 | } |
1722 | |
1723 | if (mCreatedMenus != null) { |
1724 | for (int i=0; i<mCreatedMenus.size(); i++) { |
1725 | Fragment f = mCreatedMenus.get(i); |
1726 | if (newMenus == null || !newMenus.contains(f)) { |
1727 | f.onDestroyOptionsMenu(); |
1728 | } |
1729 | } |
1730 | } |
1731 | |
1732 | mCreatedMenus = newMenus; |
1733 | |
1734 | return show; |
1735 | } |
1736 | |
1737 | public boolean dispatchPrepareOptionsMenu(Menu menu) { |
1738 | boolean show = false; |
1739 | if (mActive != null) { |
1740 | for (int i=0; i<mAdded.size(); i++) { |
1741 | Fragment f = mAdded.get(i); |
1742 | if (f != null && !f.mHidden && f.mHasMenu) { |
1743 | show = true; |
1744 | f.onPrepareOptionsMenu(menu); |
1745 | } |
1746 | } |
1747 | } |
1748 | return show; |
1749 | } |
1750 | |
1751 | public boolean dispatchOptionsItemSelected(MenuItem item) { |
1752 | if (mActive != null) { |
1753 | for (int i=0; i<mAdded.size(); i++) { |
1754 | Fragment f = mAdded.get(i); |
1755 | if (f != null && !f.mHidden && f.mHasMenu) { |
1756 | if (f.onOptionsItemSelected(item)) { |
1757 | return true; |
1758 | } |
1759 | } |
1760 | } |
1761 | } |
1762 | return false; |
1763 | } |
1764 | |
1765 | public boolean dispatchContextItemSelected(MenuItem item) { |
1766 | if (mActive != null) { |
1767 | for (int i=0; i<mAdded.size(); i++) { |
1768 | Fragment f = mAdded.get(i); |
1769 | if (f != null && !f.mHidden) { |
1770 | if (f.onContextItemSelected(item)) { |
1771 | return true; |
1772 | } |
1773 | } |
1774 | } |
1775 | } |
1776 | return false; |
1777 | } |
1778 | |
1779 | public void dispatchOptionsMenuClosed(Menu menu) { |
1780 | if (mActive != null) { |
1781 | for (int i=0; i<mAdded.size(); i++) { |
1782 | Fragment f = mAdded.get(i); |
1783 | if (f != null && !f.mHidden && f.mHasMenu) { |
1784 | f.onOptionsMenuClosed(menu); |
1785 | } |
1786 | } |
1787 | } |
1788 | } |
1789 | |
1790 | public static int reverseTransit(int transit) { |
1791 | int rev = 0; |
1792 | switch (transit) { |
1793 | case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: |
1794 | rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; |
1795 | break; |
1796 | case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: |
1797 | rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; |
1798 | break; |
1799 | case FragmentTransaction.TRANSIT_FRAGMENT_FADE: |
1800 | rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; |
1801 | break; |
1802 | } |
1803 | return rev; |
1804 | |
1805 | } |
1806 | |
1807 | public static final int ANIM_STYLE_OPEN_ENTER = 1; |
1808 | public static final int ANIM_STYLE_OPEN_EXIT = 2; |
1809 | public static final int ANIM_STYLE_CLOSE_ENTER = 3; |
1810 | public static final int ANIM_STYLE_CLOSE_EXIT = 4; |
1811 | public static final int ANIM_STYLE_FADE_ENTER = 5; |
1812 | public static final int ANIM_STYLE_FADE_EXIT = 6; |
1813 | |
1814 | public static int transitToStyleIndex(int transit, boolean enter) { |
1815 | int animAttr = -1; |
1816 | switch (transit) { |
1817 | case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: |
1818 | animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT; |
1819 | break; |
1820 | case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: |
1821 | animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT; |
1822 | break; |
1823 | case FragmentTransaction.TRANSIT_FRAGMENT_FADE: |
1824 | animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT; |
1825 | break; |
1826 | } |
1827 | return animAttr; |
1828 | } |
1829 | } |