FragmentManager.java revision fb3cffeb35368da22f99b85d45039c4e6e471c06
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 android.animation.Animator; 20import android.animation.AnimatorInflater; 21import android.animation.AnimatorListenerAdapter; 22import android.content.res.TypedArray; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.Parcel; 26import android.os.Parcelable; 27import android.util.Log; 28import android.util.SparseArray; 29import android.view.Menu; 30import android.view.MenuInflater; 31import android.view.MenuItem; 32import android.view.View; 33import android.view.ViewGroup; 34 35import java.io.FileDescriptor; 36import java.io.PrintWriter; 37import java.util.ArrayList; 38 39/** 40 * Interface for interacting with {@link Fragment} objects inside of an 41 * {@link Activity} 42 */ 43public interface FragmentManager { 44 /** 45 * Representation of an entry on the fragment back stack, as created 46 * with {@link FragmentTransaction#addToBackStack(String) 47 * FragmentTransaction.addToBackStack()}. Entries can later be 48 * retrieved with {@link FragmentManager#getBackStackEntry(int) 49 * FragmentManager.getBackStackEntry()}. 50 * 51 * <p>Note that you should never hold on to a BackStackEntry object; 52 * the identifier as returned by {@link #getId} is the only thing that 53 * will be persisted across activity instances. 54 */ 55 public interface BackStackEntry { 56 /** 57 * Return the unique identifier for the entry. This is the only 58 * representation of the entry that will persist across activity 59 * instances. 60 */ 61 public int getId(); 62 63 /** 64 * Return the full bread crumb title for the entry, or null if it 65 * does not have one. 66 */ 67 public CharSequence getBreadCrumbTitle(); 68 69 /** 70 * Return the short bread crumb title for the entry, or null if it 71 * does not have one. 72 */ 73 public CharSequence getBreadCrumbShortTitle(); 74 } 75 76 /** 77 * Interface to watch for changes to the back stack. 78 */ 79 public interface OnBackStackChangedListener { 80 /** 81 * Called whenever the contents of the back stack change. 82 */ 83 public void onBackStackChanged(); 84 } 85 86 /** 87 * Start a series of edit operations on the Fragments associated with 88 * this FragmentManager. 89 * 90 * <p>Note: A fragment transaction can only be created/committed prior 91 * to an activity saving its state. If you try to commit a transaction 92 * after {@link Activity#onSaveInstanceState Activity.onSaveInstanceState()} 93 * (and prior to a following {@link Activity#onStart Activity.onStart} 94 * or {@link Activity#onResume Activity.onResume()}, you will get an error. 95 * This is because the framework takes care of saving your current fragments 96 * in the state, and if changes are made after the state is saved then they 97 * will be lost.</p> 98 */ 99 public FragmentTransaction openTransaction(); 100 101 /** 102 * Finds a fragment that was identified by the given id either when inflated 103 * from XML or as the container ID when added in a transaction. This first 104 * searches through fragments that are currently added to the manager's 105 * activity; if no such fragment is found, then all fragments currently 106 * on the back stack associated with this ID are searched. 107 * @return The fragment if found or null otherwise. 108 */ 109 public Fragment findFragmentById(int id); 110 111 /** 112 * Finds a fragment that was identified by the given tag either when inflated 113 * from XML or as supplied when added in a transaction. This first 114 * searches through fragments that are currently added to the manager's 115 * activity; if no such fragment is found, then all fragments currently 116 * on the back stack are searched. 117 * @return The fragment if found or null otherwise. 118 */ 119 public Fragment findFragmentByTag(String tag); 120 121 /** 122 * Flag for {@link #popBackStack(String, int)} 123 * and {@link #popBackStack(int, int)}: If set, and the name or ID of 124 * a back stack entry has been supplied, then all matching entries will 125 * be consumed until one that doesn't match is found or the bottom of 126 * the stack is reached. Otherwise, all entries up to but not including that entry 127 * will be removed. 128 */ 129 public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; 130 131 /** 132 * Pop the top state off the back stack. Returns true if there was one 133 * to pop, else false. 134 */ 135 public boolean popBackStack(); 136 137 /** 138 * Pop the last fragment transition from the manager's fragment 139 * back stack. If there is nothing to pop, false is returned. 140 * @param name If non-null, this is the name of a previous back state 141 * to look for; if found, all states up to that state will be popped. The 142 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 143 * the named state itself is popped. If null, only the top state is popped. 144 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 145 */ 146 public boolean popBackStack(String name, int flags); 147 148 /** 149 * Pop all back stack states up to the one with the given identifier. 150 * @param id Identifier of the stated to be popped. If no identifier exists, 151 * false is returned. 152 * The identifier is the number returned by 153 * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The 154 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 155 * the named state itself is popped. 156 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 157 */ 158 public boolean popBackStack(int id, int flags); 159 160 /** 161 * Return the number of entries currently in the back stack. 162 */ 163 public int countBackStackEntries(); 164 165 /** 166 * Return the BackStackEntry at index <var>index</var> in the back stack; 167 * entries start index 0 being the bottom of the stack. 168 */ 169 public BackStackEntry getBackStackEntry(int index); 170 171 /** 172 * Add a new listener for changes to the fragment back stack. 173 */ 174 public void addOnBackStackChangedListener(OnBackStackChangedListener listener); 175 176 /** 177 * Remove a listener that was previously added with 178 * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. 179 */ 180 public void removeOnBackStackChangedListener(OnBackStackChangedListener listener); 181 182 /** 183 * Put a reference to a fragment in a Bundle. This Bundle can be 184 * persisted as saved state, and when later restoring 185 * {@link #getFragment(Bundle, String)} will return the current 186 * instance of the same fragment. 187 * 188 * @param bundle The bundle in which to put the fragment reference. 189 * @param key The name of the entry in the bundle. 190 * @param fragment The Fragment whose reference is to be stored. 191 */ 192 public void putFragment(Bundle bundle, String key, Fragment fragment); 193 194 /** 195 * Retrieve the current Fragment instance for a reference previously 196 * placed with {@link #putFragment(Bundle, String, Fragment)}. 197 * 198 * @param bundle The bundle from which to retrieve the fragment reference. 199 * @param key The name of the entry in the bundle. 200 * @return Returns the current Fragment instance that is associated with 201 * the given reference. 202 */ 203 public Fragment getFragment(Bundle bundle, String key); 204 205 /** 206 * Print the FragmentManager's state into the given stream. 207 * 208 * @param prefix Text to print at the front of each line. 209 * @param fd The raw file descriptor that the dump is being sent to. 210 * @param writer A PrintWriter to which the dump is to be set. 211 * @param args additional arguments to the dump request. 212 */ 213 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); 214} 215 216final class FragmentManagerState implements Parcelable { 217 FragmentState[] mActive; 218 int[] mAdded; 219 BackStackState[] mBackStack; 220 221 public FragmentManagerState() { 222 } 223 224 public FragmentManagerState(Parcel in) { 225 mActive = in.createTypedArray(FragmentState.CREATOR); 226 mAdded = in.createIntArray(); 227 mBackStack = in.createTypedArray(BackStackState.CREATOR); 228 } 229 230 public int describeContents() { 231 return 0; 232 } 233 234 public void writeToParcel(Parcel dest, int flags) { 235 dest.writeTypedArray(mActive, flags); 236 dest.writeIntArray(mAdded); 237 dest.writeTypedArray(mBackStack, flags); 238 } 239 240 public static final Parcelable.Creator<FragmentManagerState> CREATOR 241 = new Parcelable.Creator<FragmentManagerState>() { 242 public FragmentManagerState createFromParcel(Parcel in) { 243 return new FragmentManagerState(in); 244 } 245 246 public FragmentManagerState[] newArray(int size) { 247 return new FragmentManagerState[size]; 248 } 249 }; 250} 251 252/** 253 * Container for fragments associated with an activity. 254 */ 255final class FragmentManagerImpl implements FragmentManager { 256 static final boolean DEBUG = true; 257 static final String TAG = "FragmentManager"; 258 259 static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; 260 static final String TARGET_STATE_TAG = "android:target_state"; 261 static final String VIEW_STATE_TAG = "android:view_state"; 262 263 ArrayList<Runnable> mPendingActions; 264 Runnable[] mTmpActions; 265 boolean mExecutingActions; 266 267 ArrayList<Fragment> mActive; 268 ArrayList<Fragment> mAdded; 269 ArrayList<Integer> mAvailIndices; 270 ArrayList<BackStackRecord> mBackStack; 271 272 // Must be accessed while locked. 273 ArrayList<BackStackRecord> mBackStackIndices; 274 ArrayList<Integer> mAvailBackStackIndices; 275 276 ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; 277 278 int mCurState = Fragment.INITIALIZING; 279 Activity mActivity; 280 281 boolean mNeedMenuInvalidate; 282 boolean mStateSaved; 283 String mNoTransactionsBecause; 284 285 // Temporary vars for state save and restore. 286 Bundle mStateBundle = null; 287 SparseArray<Parcelable> mStateArray = null; 288 289 Runnable mExecCommit = new Runnable() { 290 @Override 291 public void run() { 292 execPendingActions(); 293 } 294 }; 295 296 @Override 297 public FragmentTransaction openTransaction() { 298 return new BackStackRecord(this); 299 } 300 301 @Override 302 public boolean popBackStack() { 303 return popBackStackState(mActivity.mHandler, null, -1, 0); 304 } 305 306 @Override 307 public boolean popBackStack(String name, int flags) { 308 return popBackStackState(mActivity.mHandler, name, -1, flags); 309 } 310 311 @Override 312 public boolean popBackStack(int id, int flags) { 313 if (id < 0) { 314 throw new IllegalArgumentException("Bad id: " + id); 315 } 316 return popBackStackState(mActivity.mHandler, null, id, flags); 317 } 318 319 @Override 320 public int countBackStackEntries() { 321 return mBackStack != null ? mBackStack.size() : 0; 322 } 323 324 @Override 325 public BackStackEntry getBackStackEntry(int index) { 326 return mBackStack.get(index); 327 } 328 329 @Override 330 public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { 331 if (mBackStackChangeListeners == null) { 332 mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>(); 333 } 334 mBackStackChangeListeners.add(listener); 335 } 336 337 @Override 338 public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { 339 if (mBackStackChangeListeners != null) { 340 mBackStackChangeListeners.remove(listener); 341 } 342 } 343 344 @Override 345 public void putFragment(Bundle bundle, String key, Fragment fragment) { 346 if (fragment.mIndex < 0) { 347 throw new IllegalStateException("Fragment " + fragment 348 + " is not currently in the FragmentManager"); 349 } 350 bundle.putInt(key, fragment.mIndex); 351 } 352 353 @Override 354 public Fragment getFragment(Bundle bundle, String key) { 355 int index = bundle.getInt(key, -1); 356 if (index == -1) { 357 return null; 358 } 359 if (index >= mActive.size()) { 360 throw new IllegalStateException("Fragement no longer exists for key " 361 + key + ": index " + index); 362 } 363 Fragment f = mActive.get(index); 364 if (f == null) { 365 throw new IllegalStateException("Fragement no longer exists for key " 366 + key + ": index " + index); 367 } 368 return f; 369 } 370 371 @Override 372 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 373 if (mActive == null || mActive.size() <= 0) { 374 return; 375 } 376 377 writer.print(prefix); writer.println("Active Fragments:"); 378 379 String innerPrefix = prefix + " "; 380 381 int N = mActive.size(); 382 for (int i=0; i<N; i++) { 383 Fragment f = mActive.get(i); 384 if (f != null) { 385 writer.print(prefix); writer.print(" #"); writer.print(i); 386 writer.print(": "); writer.println(f.toString()); 387 f.dump(innerPrefix, fd, writer, args); 388 } 389 } 390 391 if (mAdded != null) { 392 N = mAdded.size(); 393 if (N > 0) { 394 writer.print(prefix); writer.println("Added Fragments:"); 395 for (int i=0; i<N; i++) { 396 Fragment f = mAdded.get(i); 397 writer.print(prefix); writer.print(" #"); writer.print(i); 398 writer.print(": "); writer.println(f.toString()); 399 } 400 } 401 } 402 403 if (mBackStack != null) { 404 N = mBackStack.size(); 405 if (N > 0) { 406 writer.print(prefix); writer.println("Back Stack:"); 407 for (int i=0; i<N; i++) { 408 BackStackRecord bs = mBackStack.get(i); 409 writer.print(prefix); writer.print(" #"); writer.print(i); 410 writer.print(": "); writer.println(bs.toString()); 411 } 412 } 413 } 414 } 415 416 Animator loadAnimator(Fragment fragment, int transit, boolean enter, 417 int transitionStyle) { 418 Animator animObj = fragment.onCreateAnimator(transit, enter, 419 fragment.mNextAnim); 420 if (animObj != null) { 421 return animObj; 422 } 423 424 if (fragment.mNextAnim != 0) { 425 Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim); 426 if (anim != null) { 427 return anim; 428 } 429 } 430 431 if (transit == 0) { 432 return null; 433 } 434 435 int styleIndex = transitToStyleIndex(transit, enter); 436 if (styleIndex < 0) { 437 return null; 438 } 439 440 if (transitionStyle == 0 && mActivity.getWindow() != null) { 441 transitionStyle = mActivity.getWindow().getAttributes().windowAnimations; 442 } 443 if (transitionStyle == 0) { 444 return null; 445 } 446 447 TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, 448 com.android.internal.R.styleable.FragmentAnimation); 449 int anim = attrs.getResourceId(styleIndex, 0); 450 attrs.recycle(); 451 452 if (anim == 0) { 453 return null; 454 } 455 456 return AnimatorInflater.loadAnimator(mActivity, anim); 457 } 458 459 void moveToState(Fragment f, int newState, int transit, int transitionStyle) { 460 // Fragments that are not currently added will sit in the onCreate() state. 461 if (!f.mAdded && newState > Fragment.CREATED) { 462 newState = Fragment.CREATED; 463 } 464 465 if (f.mState < newState) { 466 switch (f.mState) { 467 case Fragment.INITIALIZING: 468 if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); 469 if (f.mSavedFragmentState != null) { 470 f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( 471 FragmentManagerImpl.VIEW_STATE_TAG); 472 f.mTarget = getFragment(f.mSavedFragmentState, 473 FragmentManagerImpl.TARGET_STATE_TAG); 474 if (f.mTarget != null) { 475 f.mTargetRequestCode = f.mSavedFragmentState.getInt( 476 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); 477 } 478 } 479 f.mActivity = mActivity; 480 f.mCalled = false; 481 f.onAttach(mActivity); 482 if (!f.mCalled) { 483 throw new SuperNotCalledException("Fragment " + f 484 + " did not call through to super.onAttach()"); 485 } 486 mActivity.onAttachFragment(f); 487 488 if (!f.mRetaining) { 489 f.mCalled = false; 490 f.onCreate(f.mSavedFragmentState); 491 if (!f.mCalled) { 492 throw new SuperNotCalledException("Fragment " + f 493 + " did not call through to super.onCreate()"); 494 } 495 } 496 f.mRetaining = false; 497 if (f.mFromLayout) { 498 // For fragments that are part of the content view 499 // layout, we need to instantiate the view immediately 500 // and the inflater will take care of adding it. 501 f.mView = f.onCreateView(mActivity.getLayoutInflater(), 502 null, f.mSavedFragmentState); 503 if (f.mView != null) { 504 f.mView.setSaveFromParentEnabled(false); 505 f.restoreViewState(); 506 if (f.mHidden) f.mView.setVisibility(View.GONE); 507 } 508 } 509 case Fragment.CREATED: 510 if (newState > Fragment.CREATED) { 511 if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f); 512 if (!f.mFromLayout) { 513 ViewGroup container = null; 514 if (f.mContainerId != 0) { 515 container = (ViewGroup)mActivity.findViewById(f.mContainerId); 516 if (container == null) { 517 throw new IllegalArgumentException("No view found for id 0x" 518 + Integer.toHexString(f.mContainerId) 519 + " for fragment " + f); 520 } 521 } 522 f.mContainer = container; 523 f.mView = f.onCreateView(mActivity.getLayoutInflater(), 524 container, f.mSavedFragmentState); 525 if (f.mView != null) { 526 f.mView.setSaveFromParentEnabled(false); 527 if (container != null) { 528 Animator anim = loadAnimator(f, transit, true, 529 transitionStyle); 530 if (anim != null) { 531 anim.setTarget(f.mView); 532 anim.start(); 533 } 534 container.addView(f.mView); 535 f.restoreViewState(); 536 } 537 if (f.mHidden) f.mView.setVisibility(View.GONE); 538 } 539 } 540 541 f.mCalled = false; 542 f.onActivityCreated(f.mSavedFragmentState); 543 if (!f.mCalled) { 544 throw new SuperNotCalledException("Fragment " + f 545 + " did not call through to super.onReady()"); 546 } 547 f.mSavedFragmentState = null; 548 } 549 case Fragment.ACTIVITY_CREATED: 550 if (newState > Fragment.ACTIVITY_CREATED) { 551 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 552 f.mCalled = false; 553 f.onStart(); 554 if (!f.mCalled) { 555 throw new SuperNotCalledException("Fragment " + f 556 + " did not call through to super.onStart()"); 557 } 558 } 559 case Fragment.STARTED: 560 if (newState > Fragment.STARTED) { 561 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 562 f.mCalled = false; 563 f.mResumed = true; 564 f.onResume(); 565 if (!f.mCalled) { 566 throw new SuperNotCalledException("Fragment " + f 567 + " did not call through to super.onResume()"); 568 } 569 } 570 } 571 } else if (f.mState > newState) { 572 switch (f.mState) { 573 case Fragment.RESUMED: 574 if (newState < Fragment.RESUMED) { 575 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 576 f.mCalled = false; 577 f.onPause(); 578 if (!f.mCalled) { 579 throw new SuperNotCalledException("Fragment " + f 580 + " did not call through to super.onPause()"); 581 } 582 f.mResumed = false; 583 } 584 case Fragment.STARTED: 585 if (newState < Fragment.STARTED) { 586 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 587 f.mCalled = false; 588 f.performStop(); 589 if (!f.mCalled) { 590 throw new SuperNotCalledException("Fragment " + f 591 + " did not call through to super.onStop()"); 592 } 593 } 594 case Fragment.ACTIVITY_CREATED: 595 if (newState < Fragment.ACTIVITY_CREATED) { 596 if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f); 597 if (f.mView != null) { 598 // Need to save the current view state if not 599 // done already. 600 if (!mActivity.isFinishing() && f.mSavedViewState == null) { 601 saveFragmentViewState(f); 602 } 603 } 604 f.mCalled = false; 605 f.onDestroyView(); 606 if (!f.mCalled) { 607 throw new SuperNotCalledException("Fragment " + f 608 + " did not call through to super.onDestroyedView()"); 609 } 610 if (f.mView != null && f.mContainer != null) { 611 Animator anim = null; 612 if (mCurState > Fragment.INITIALIZING) { 613 anim = loadAnimator(f, transit, false, 614 transitionStyle); 615 } 616 if (anim != null) { 617 final ViewGroup container = f.mContainer; 618 final View view = f.mView; 619 container.startViewTransition(view); 620 anim.addListener(new AnimatorListenerAdapter() { 621 @Override 622 public void onAnimationEnd(Animator anim) { 623 container.endViewTransition(view); 624 } 625 }); 626 anim.setTarget(f.mView); 627 anim.start(); 628 629 } 630 f.mContainer.removeView(f.mView); 631 } 632 f.mContainer = null; 633 f.mView = null; 634 } 635 case Fragment.CREATED: 636 if (newState < Fragment.CREATED) { 637 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 638 if (!f.mRetaining) { 639 f.mCalled = false; 640 f.onDestroy(); 641 if (!f.mCalled) { 642 throw new SuperNotCalledException("Fragment " + f 643 + " did not call through to super.onDestroy()"); 644 } 645 } 646 647 f.mCalled = false; 648 f.onDetach(); 649 if (!f.mCalled) { 650 throw new SuperNotCalledException("Fragment " + f 651 + " did not call through to super.onDetach()"); 652 } 653 f.mImmediateActivity = null; 654 f.mActivity = null; 655 } 656 } 657 } 658 659 f.mState = newState; 660 } 661 662 void moveToState(Fragment f) { 663 moveToState(f, mCurState, 0, 0); 664 } 665 666 void moveToState(int newState, boolean always) { 667 moveToState(newState, 0, 0, always); 668 } 669 670 void moveToState(int newState, int transit, int transitStyle, boolean always) { 671 if (mActivity == null && newState != Fragment.INITIALIZING) { 672 throw new IllegalStateException("No activity"); 673 } 674 675 if (!always && mCurState == newState) { 676 return; 677 } 678 679 mCurState = newState; 680 if (mActive != null) { 681 for (int i=0; i<mActive.size(); i++) { 682 Fragment f = mActive.get(i); 683 if (f != null) { 684 moveToState(f, newState, transit, transitStyle); 685 } 686 } 687 688 if (mNeedMenuInvalidate && mActivity != null) { 689 mActivity.invalidateOptionsMenu(); 690 mNeedMenuInvalidate = false; 691 } 692 } 693 } 694 695 void makeActive(Fragment f) { 696 if (f.mIndex >= 0) { 697 return; 698 } 699 700 if (mAvailIndices == null || mAvailIndices.size() <= 0) { 701 if (mActive == null) { 702 mActive = new ArrayList<Fragment>(); 703 } 704 f.setIndex(mActive.size()); 705 mActive.add(f); 706 707 } else { 708 f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1)); 709 mActive.set(f.mIndex, f); 710 } 711 } 712 713 void makeInactive(Fragment f) { 714 if (f.mIndex < 0) { 715 return; 716 } 717 718 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex); 719 mActive.set(f.mIndex, null); 720 if (mAvailIndices == null) { 721 mAvailIndices = new ArrayList<Integer>(); 722 } 723 mAvailIndices.add(f.mIndex); 724 mActivity.invalidateFragmentIndex(f.mIndex); 725 f.clearIndex(); 726 } 727 728 public void addFragment(Fragment fragment, boolean moveToStateNow) { 729 if (mAdded == null) { 730 mAdded = new ArrayList<Fragment>(); 731 } 732 mAdded.add(fragment); 733 makeActive(fragment); 734 if (DEBUG) Log.v(TAG, "add: " + fragment); 735 fragment.mAdded = true; 736 if (fragment.mHasMenu) { 737 mNeedMenuInvalidate = true; 738 } 739 if (moveToStateNow) { 740 moveToState(fragment); 741 } 742 } 743 744 public void removeFragment(Fragment fragment, int transition, int transitionStyle) { 745 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 746 mAdded.remove(fragment); 747 final boolean inactive = fragment.mBackStackNesting <= 0; 748 if (fragment.mHasMenu) { 749 mNeedMenuInvalidate = true; 750 } 751 fragment.mAdded = false; 752 moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, 753 transition, transitionStyle); 754 if (inactive) { 755 makeInactive(fragment); 756 } 757 } 758 759 public void hideFragment(Fragment fragment, int transition, int transitionStyle) { 760 if (DEBUG) Log.v(TAG, "hide: " + fragment); 761 if (!fragment.mHidden) { 762 fragment.mHidden = true; 763 if (fragment.mView != null) { 764 Animator anim = loadAnimator(fragment, transition, true, 765 transitionStyle); 766 if (anim != null) { 767 anim.setTarget(fragment.mView); 768 anim.start(); 769 } 770 fragment.mView.setVisibility(View.GONE); 771 } 772 if (fragment.mAdded && fragment.mHasMenu) { 773 mNeedMenuInvalidate = true; 774 } 775 fragment.onHiddenChanged(true); 776 } 777 } 778 779 public void showFragment(Fragment fragment, int transition, int transitionStyle) { 780 if (DEBUG) Log.v(TAG, "show: " + fragment); 781 if (fragment.mHidden) { 782 fragment.mHidden = false; 783 if (fragment.mView != null) { 784 Animator anim = loadAnimator(fragment, transition, true, 785 transitionStyle); 786 if (anim != null) { 787 anim.setTarget(fragment.mView); 788 anim.start(); 789 } 790 fragment.mView.setVisibility(View.VISIBLE); 791 } 792 if (fragment.mAdded && fragment.mHasMenu) { 793 mNeedMenuInvalidate = true; 794 } 795 fragment.onHiddenChanged(false); 796 } 797 } 798 799 public Fragment findFragmentById(int id) { 800 if (mActive != null) { 801 // First look through added fragments. 802 for (int i=mAdded.size()-1; i>=0; i--) { 803 Fragment f = mAdded.get(i); 804 if (f != null && f.mFragmentId == id) { 805 return f; 806 } 807 } 808 // Now for any known fragment. 809 for (int i=mActive.size()-1; i>=0; i--) { 810 Fragment f = mActive.get(i); 811 if (f != null && f.mFragmentId == id) { 812 return f; 813 } 814 } 815 } 816 return null; 817 } 818 819 public Fragment findFragmentByTag(String tag) { 820 if (mActive != null && tag != null) { 821 // First look through added fragments. 822 for (int i=mAdded.size()-1; i>=0; i--) { 823 Fragment f = mAdded.get(i); 824 if (f != null && tag.equals(f.mTag)) { 825 return f; 826 } 827 } 828 // Now for any known fragment. 829 for (int i=mActive.size()-1; i>=0; i--) { 830 Fragment f = mActive.get(i); 831 if (f != null && tag.equals(f.mTag)) { 832 return f; 833 } 834 } 835 } 836 return null; 837 } 838 839 public Fragment findFragmentByWho(String who) { 840 if (mActive != null && who != null) { 841 for (int i=mActive.size()-1; i>=0; i--) { 842 Fragment f = mActive.get(i); 843 if (f != null && who.equals(f.mWho)) { 844 return f; 845 } 846 } 847 } 848 return null; 849 } 850 851 public void enqueueAction(Runnable action) { 852 if (mStateSaved) { 853 throw new IllegalStateException( 854 "Can not perform this action after onSaveInstanceState"); 855 } 856 if (mNoTransactionsBecause != null) { 857 throw new IllegalStateException( 858 "Can not perform this action inside of " + mNoTransactionsBecause); 859 } 860 synchronized (this) { 861 if (mPendingActions == null) { 862 mPendingActions = new ArrayList<Runnable>(); 863 } 864 mPendingActions.add(action); 865 if (mPendingActions.size() == 1) { 866 mActivity.mHandler.removeCallbacks(mExecCommit); 867 mActivity.mHandler.post(mExecCommit); 868 } 869 } 870 } 871 872 public int allocBackStackIndex(BackStackRecord bse) { 873 synchronized (this) { 874 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 875 if (mBackStackIndices == null) { 876 mBackStackIndices = new ArrayList<BackStackRecord>(); 877 } 878 int index = mBackStackIndices.size(); 879 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 880 mBackStackIndices.add(bse); 881 return index; 882 883 } else { 884 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 885 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 886 mBackStackIndices.set(index, bse); 887 return index; 888 } 889 } 890 } 891 892 public void setBackStackIndex(int index, BackStackRecord bse) { 893 synchronized (this) { 894 if (mBackStackIndices == null) { 895 mBackStackIndices = new ArrayList<BackStackRecord>(); 896 } 897 int N = mBackStackIndices.size(); 898 if (index < N) { 899 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 900 mBackStackIndices.set(index, bse); 901 } else { 902 while (N < index) { 903 mBackStackIndices.add(null); 904 if (mAvailBackStackIndices == null) { 905 mAvailBackStackIndices = new ArrayList<Integer>(); 906 } 907 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 908 mAvailBackStackIndices.add(N); 909 N++; 910 } 911 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 912 mBackStackIndices.add(bse); 913 } 914 } 915 } 916 917 public void freeBackStackIndex(int index) { 918 synchronized (this) { 919 mBackStackIndices.set(index, null); 920 if (mAvailBackStackIndices == null) { 921 mAvailBackStackIndices = new ArrayList<Integer>(); 922 } 923 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 924 mAvailBackStackIndices.add(index); 925 } 926 } 927 928 /** 929 * Only call from main thread! 930 */ 931 public void execPendingActions() { 932 if (mExecutingActions) { 933 throw new IllegalStateException("Recursive entry to execPendingActions"); 934 } 935 936 while (true) { 937 int numActions; 938 939 synchronized (this) { 940 if (mPendingActions == null || mPendingActions.size() == 0) { 941 return; 942 } 943 944 numActions = mPendingActions.size(); 945 if (mTmpActions == null || mTmpActions.length < numActions) { 946 mTmpActions = new Runnable[numActions]; 947 } 948 mPendingActions.toArray(mTmpActions); 949 mPendingActions.clear(); 950 mActivity.mHandler.removeCallbacks(mExecCommit); 951 } 952 953 mExecutingActions = true; 954 for (int i=0; i<numActions; i++) { 955 mTmpActions[i].run(); 956 } 957 mExecutingActions = false; 958 } 959 } 960 961 void reportBackStackChanged() { 962 if (mBackStackChangeListeners != null) { 963 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 964 mBackStackChangeListeners.get(i).onBackStackChanged(); 965 } 966 } 967 } 968 969 void addBackStackState(BackStackRecord state) { 970 if (mBackStack == null) { 971 mBackStack = new ArrayList<BackStackRecord>(); 972 } 973 mBackStack.add(state); 974 reportBackStackChanged(); 975 } 976 977 boolean popBackStackState(Handler handler, String name, int id, int flags) { 978 if (mBackStack == null) { 979 return false; 980 } 981 if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) { 982 int last = mBackStack.size()-1; 983 if (last < 0) { 984 return false; 985 } 986 final BackStackRecord bss = mBackStack.remove(last); 987 enqueueAction(new Runnable() { 988 public void run() { 989 if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss); 990 bss.popFromBackStack(true); 991 reportBackStackChanged(); 992 } 993 }); 994 } else { 995 int index = -1; 996 if (name != null || id >= 0) { 997 // If a name or ID is specified, look for that place in 998 // the stack. 999 index = mBackStack.size()-1; 1000 while (index >= 0) { 1001 BackStackRecord bss = mBackStack.get(index); 1002 if (name != null && name.equals(bss.getName())) { 1003 break; 1004 } 1005 if (id >= 0 && id == bss.mIndex) { 1006 break; 1007 } 1008 index--; 1009 } 1010 if (index < 0) { 1011 return false; 1012 } 1013 if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) { 1014 index--; 1015 // Consume all following entries that match. 1016 while (index >= 0) { 1017 BackStackRecord bss = mBackStack.get(index); 1018 if ((name != null && name.equals(bss.getName())) 1019 || (id >= 0 && id == bss.mIndex)) { 1020 index--; 1021 continue; 1022 } 1023 break; 1024 } 1025 } 1026 } 1027 if (index == mBackStack.size()-1) { 1028 return false; 1029 } 1030 final ArrayList<BackStackRecord> states 1031 = new ArrayList<BackStackRecord>(); 1032 for (int i=mBackStack.size()-1; i>index; i--) { 1033 states.add(mBackStack.remove(i)); 1034 } 1035 enqueueAction(new Runnable() { 1036 public void run() { 1037 final int LAST = states.size()-1; 1038 for (int i=0; i<=LAST; i++) { 1039 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); 1040 states.get(i).popFromBackStack(i == LAST); 1041 } 1042 reportBackStackChanged(); 1043 } 1044 }); 1045 } 1046 return true; 1047 } 1048 1049 ArrayList<Fragment> retainNonConfig() { 1050 ArrayList<Fragment> fragments = null; 1051 if (mActive != null) { 1052 for (int i=0; i<mActive.size(); i++) { 1053 Fragment f = mActive.get(i); 1054 if (f != null && f.mRetainInstance) { 1055 if (fragments == null) { 1056 fragments = new ArrayList<Fragment>(); 1057 } 1058 fragments.add(f); 1059 f.mRetaining = true; 1060 } 1061 } 1062 } 1063 return fragments; 1064 } 1065 1066 void saveFragmentViewState(Fragment f) { 1067 if (f.mView == null) { 1068 return; 1069 } 1070 if (mStateArray == null) { 1071 mStateArray = new SparseArray<Parcelable>(); 1072 } 1073 f.mView.saveHierarchyState(mStateArray); 1074 if (mStateArray.size() > 0) { 1075 f.mSavedViewState = mStateArray; 1076 mStateArray = null; 1077 } 1078 } 1079 1080 Parcelable saveAllState() { 1081 mStateSaved = true; 1082 1083 if (mActive == null || mActive.size() <= 0) { 1084 return null; 1085 } 1086 1087 // First collect all active fragments. 1088 int N = mActive.size(); 1089 FragmentState[] active = new FragmentState[N]; 1090 boolean haveFragments = false; 1091 for (int i=0; i<N; i++) { 1092 Fragment f = mActive.get(i); 1093 if (f != null) { 1094 haveFragments = true; 1095 1096 FragmentState fs = new FragmentState(f); 1097 active[i] = fs; 1098 1099 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 1100 if (mStateBundle == null) { 1101 mStateBundle = new Bundle(); 1102 } 1103 f.onSaveInstanceState(mStateBundle); 1104 if (!mStateBundle.isEmpty()) { 1105 fs.mSavedFragmentState = mStateBundle; 1106 mStateBundle = null; 1107 } 1108 1109 if (f.mView != null) { 1110 saveFragmentViewState(f); 1111 if (f.mSavedViewState != null) { 1112 if (fs.mSavedFragmentState == null) { 1113 fs.mSavedFragmentState = new Bundle(); 1114 } 1115 fs.mSavedFragmentState.putSparseParcelableArray( 1116 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 1117 } 1118 } 1119 1120 if (f.mTarget != null) { 1121 if (fs.mSavedFragmentState == null) { 1122 fs.mSavedFragmentState = new Bundle(); 1123 } 1124 putFragment(fs.mSavedFragmentState, 1125 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 1126 if (f.mTargetRequestCode != 0) { 1127 fs.mSavedFragmentState.putInt( 1128 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 1129 f.mTargetRequestCode); 1130 } 1131 } 1132 1133 } else { 1134 fs.mSavedFragmentState = f.mSavedFragmentState; 1135 } 1136 1137 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 1138 + fs.mSavedFragmentState); 1139 } 1140 } 1141 1142 if (!haveFragments) { 1143 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 1144 return null; 1145 } 1146 1147 int[] added = null; 1148 BackStackState[] backStack = null; 1149 1150 // Build list of currently added fragments. 1151 if (mAdded != null) { 1152 N = mAdded.size(); 1153 if (N > 0) { 1154 added = new int[N]; 1155 for (int i=0; i<N; i++) { 1156 added[i] = mAdded.get(i).mIndex; 1157 if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i 1158 + ": " + mAdded.get(i)); 1159 } 1160 } 1161 } 1162 1163 // Now save back stack. 1164 if (mBackStack != null) { 1165 N = mBackStack.size(); 1166 if (N > 0) { 1167 backStack = new BackStackState[N]; 1168 for (int i=0; i<N; i++) { 1169 backStack[i] = new BackStackState(this, mBackStack.get(i)); 1170 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 1171 + ": " + mBackStack.get(i)); 1172 } 1173 } 1174 } 1175 1176 FragmentManagerState fms = new FragmentManagerState(); 1177 fms.mActive = active; 1178 fms.mAdded = added; 1179 fms.mBackStack = backStack; 1180 return fms; 1181 } 1182 1183 void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 1184 // If there is no saved state at all, then there can not be 1185 // any nonConfig fragments either, so that is that. 1186 if (state == null) return; 1187 FragmentManagerState fms = (FragmentManagerState)state; 1188 if (fms.mActive == null) return; 1189 1190 // First re-attach any non-config instances we are retaining back 1191 // to their saved state, so we don't try to instantiate them again. 1192 if (nonConfig != null) { 1193 for (int i=0; i<nonConfig.size(); i++) { 1194 Fragment f = nonConfig.get(i); 1195 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 1196 FragmentState fs = fms.mActive[f.mIndex]; 1197 fs.mInstance = f; 1198 f.mSavedViewState = null; 1199 f.mBackStackNesting = 0; 1200 f.mInLayout = false; 1201 f.mAdded = false; 1202 if (fs.mSavedFragmentState != null) { 1203 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 1204 FragmentManagerImpl.VIEW_STATE_TAG); 1205 } 1206 } 1207 } 1208 1209 // Build the full list of active fragments, instantiating them from 1210 // their saved state. 1211 mActive = new ArrayList<Fragment>(fms.mActive.length); 1212 if (mAvailIndices != null) { 1213 mAvailIndices.clear(); 1214 } 1215 for (int i=0; i<fms.mActive.length; i++) { 1216 FragmentState fs = fms.mActive[i]; 1217 if (fs != null) { 1218 Fragment f = fs.instantiate(mActivity); 1219 if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f); 1220 mActive.add(f); 1221 } else { 1222 if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)"); 1223 mActive.add(null); 1224 if (mAvailIndices == null) { 1225 mAvailIndices = new ArrayList<Integer>(); 1226 } 1227 if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i); 1228 mAvailIndices.add(i); 1229 } 1230 } 1231 1232 // Update the target of all retained fragments. 1233 if (nonConfig != null) { 1234 for (int i=0; i<nonConfig.size(); i++) { 1235 Fragment f = nonConfig.get(i); 1236 if (f.mTarget != null) { 1237 if (f.mTarget.mIndex < mActive.size()) { 1238 f.mTarget = mActive.get(f.mTarget.mIndex); 1239 } else { 1240 Log.w(TAG, "Re-attaching retained fragment " + f 1241 + " target no longer exists: " + f.mTarget); 1242 f.mTarget = null; 1243 } 1244 } 1245 } 1246 } 1247 1248 // Build the list of currently added fragments. 1249 if (fms.mAdded != null) { 1250 mAdded = new ArrayList<Fragment>(fms.mAdded.length); 1251 for (int i=0; i<fms.mAdded.length; i++) { 1252 Fragment f = mActive.get(fms.mAdded[i]); 1253 if (f == null) { 1254 throw new IllegalStateException( 1255 "No instantiated fragment for index #" + fms.mAdded[i]); 1256 } 1257 f.mAdded = true; 1258 f.mImmediateActivity = mActivity; 1259 if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f); 1260 mAdded.add(f); 1261 } 1262 } else { 1263 mAdded = null; 1264 } 1265 1266 // Build the back stack. 1267 if (fms.mBackStack != null) { 1268 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 1269 for (int i=0; i<fms.mBackStack.length; i++) { 1270 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 1271 if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i 1272 + " (index " + bse.mIndex + "): " + bse); 1273 mBackStack.add(bse); 1274 if (bse.mIndex >= 0) { 1275 setBackStackIndex(bse.mIndex, bse); 1276 } 1277 } 1278 } else { 1279 mBackStack = null; 1280 } 1281 } 1282 1283 public void attachActivity(Activity activity) { 1284 if (mActivity != null) throw new IllegalStateException(); 1285 mActivity = activity; 1286 } 1287 1288 public void noteStateNotSaved() { 1289 mStateSaved = false; 1290 } 1291 1292 public void dispatchCreate() { 1293 mStateSaved = false; 1294 moveToState(Fragment.CREATED, false); 1295 } 1296 1297 public void dispatchActivityCreated() { 1298 mStateSaved = false; 1299 moveToState(Fragment.ACTIVITY_CREATED, false); 1300 } 1301 1302 public void dispatchStart() { 1303 mStateSaved = false; 1304 moveToState(Fragment.STARTED, false); 1305 } 1306 1307 public void dispatchResume() { 1308 mStateSaved = false; 1309 moveToState(Fragment.RESUMED, false); 1310 } 1311 1312 public void dispatchPause() { 1313 moveToState(Fragment.STARTED, false); 1314 } 1315 1316 public void dispatchStop() { 1317 moveToState(Fragment.ACTIVITY_CREATED, false); 1318 } 1319 1320 public void dispatchDestroy() { 1321 moveToState(Fragment.INITIALIZING, false); 1322 mActivity = null; 1323 } 1324 1325 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1326 boolean show = false; 1327 if (mActive != null) { 1328 for (int i=0; i<mAdded.size(); i++) { 1329 Fragment f = mAdded.get(i); 1330 if (f != null && !f.mHidden && f.mHasMenu) { 1331 show = true; 1332 f.onCreateOptionsMenu(menu, inflater); 1333 } 1334 } 1335 } 1336 return show; 1337 } 1338 1339 public boolean dispatchPrepareOptionsMenu(Menu menu) { 1340 boolean show = false; 1341 if (mActive != null) { 1342 for (int i=0; i<mAdded.size(); i++) { 1343 Fragment f = mAdded.get(i); 1344 if (f != null && !f.mHidden && f.mHasMenu) { 1345 show = true; 1346 f.onPrepareOptionsMenu(menu); 1347 } 1348 } 1349 } 1350 return show; 1351 } 1352 1353 public boolean dispatchOptionsItemSelected(MenuItem item) { 1354 if (mActive != null) { 1355 for (int i=0; i<mAdded.size(); i++) { 1356 Fragment f = mAdded.get(i); 1357 if (f != null && !f.mHidden && f.mHasMenu) { 1358 if (f.onOptionsItemSelected(item)) { 1359 return true; 1360 } 1361 } 1362 } 1363 } 1364 return false; 1365 } 1366 1367 public boolean dispatchContextItemSelected(MenuItem item) { 1368 if (mActive != null) { 1369 for (int i=0; i<mAdded.size(); i++) { 1370 Fragment f = mAdded.get(i); 1371 if (f != null && !f.mHidden) { 1372 if (f.onContextItemSelected(item)) { 1373 return true; 1374 } 1375 } 1376 } 1377 } 1378 return false; 1379 } 1380 1381 public void dispatchOptionsMenuClosed(Menu menu) { 1382 if (mActive != null) { 1383 for (int i=0; i<mAdded.size(); i++) { 1384 Fragment f = mAdded.get(i); 1385 if (f != null && !f.mHidden && f.mHasMenu) { 1386 f.onOptionsMenuClosed(menu); 1387 } 1388 } 1389 } 1390 } 1391 1392 public static int reverseTransit(int transit) { 1393 int rev = 0; 1394 switch (transit) { 1395 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1396 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 1397 break; 1398 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1399 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 1400 break; 1401 case FragmentTransaction.TRANSIT_FRAGMENT_NEXT: 1402 rev = FragmentTransaction.TRANSIT_FRAGMENT_PREV; 1403 break; 1404 case FragmentTransaction.TRANSIT_FRAGMENT_PREV: 1405 rev = FragmentTransaction.TRANSIT_FRAGMENT_NEXT; 1406 break; 1407 } 1408 return rev; 1409 1410 } 1411 1412 public static int transitToStyleIndex(int transit, boolean enter) { 1413 int animAttr = -1; 1414 switch (transit) { 1415 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1416 animAttr = enter 1417 ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation 1418 : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation; 1419 break; 1420 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1421 animAttr = enter 1422 ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation 1423 : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation; 1424 break; 1425 case FragmentTransaction.TRANSIT_FRAGMENT_NEXT: 1426 animAttr = enter 1427 ? com.android.internal.R.styleable.FragmentAnimation_fragmentNextEnterAnimation 1428 : com.android.internal.R.styleable.FragmentAnimation_fragmentNextExitAnimation; 1429 break; 1430 case FragmentTransaction.TRANSIT_FRAGMENT_PREV: 1431 animAttr = enter 1432 ? com.android.internal.R.styleable.FragmentAnimation_fragmentPrevEnterAnimation 1433 : com.android.internal.R.styleable.FragmentAnimation_fragmentPrevExitAnimation; 1434 break; 1435 } 1436 return animAttr; 1437 } 1438} 1439