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