FragmentManager.java revision 811ed1065f39469cf2cf6adba22cab397ed88d5e
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.Animatable; 20import android.animation.PropertyAnimator; 21import android.animation.Sequencer; 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; 34import android.view.animation.AnimationUtils; 35 36import java.util.ArrayList; 37 38final class FragmentManagerState implements Parcelable { 39 FragmentState[] mActive; 40 int[] mAdded; 41 BackStackState[] mBackStack; 42 43 public FragmentManagerState() { 44 } 45 46 public FragmentManagerState(Parcel in) { 47 mActive = in.createTypedArray(FragmentState.CREATOR); 48 mAdded = in.createIntArray(); 49 mBackStack = in.createTypedArray(BackStackState.CREATOR); 50 } 51 52 public int describeContents() { 53 return 0; 54 } 55 56 public void writeToParcel(Parcel dest, int flags) { 57 dest.writeTypedArray(mActive, flags); 58 dest.writeIntArray(mAdded); 59 dest.writeTypedArray(mBackStack, flags); 60 } 61 62 public static final Parcelable.Creator<FragmentManagerState> CREATOR 63 = new Parcelable.Creator<FragmentManagerState>() { 64 public FragmentManagerState createFromParcel(Parcel in) { 65 return new FragmentManagerState(in); 66 } 67 68 public FragmentManagerState[] newArray(int size) { 69 return new FragmentManagerState[size]; 70 } 71 }; 72} 73 74/** 75 * @hide 76 * Container for fragments associated with an activity. 77 */ 78public class FragmentManager { 79 static final boolean DEBUG = true; 80 static final String TAG = "FragmentManager"; 81 82 ArrayList<Runnable> mPendingActions; 83 Runnable[] mTmpActions; 84 boolean mExecutingActions; 85 86 ArrayList<Fragment> mActive; 87 ArrayList<Fragment> mAdded; 88 ArrayList<Integer> mAvailIndices; 89 ArrayList<BackStackEntry> mBackStack; 90 91 // Must be accessed while locked. 92 ArrayList<BackStackEntry> mBackStackIndices; 93 ArrayList<Integer> mAvailBackStackIndices; 94 95 int mCurState = Fragment.INITIALIZING; 96 Activity mActivity; 97 98 boolean mNeedMenuInvalidate; 99 100 // Temporary vars for state save and restore. 101 Bundle mStateBundle = null; 102 SparseArray<Parcelable> mStateArray = null; 103 104 Runnable mExecCommit = new Runnable() { 105 @Override 106 public void run() { 107 execPendingActions(); 108 } 109 }; 110 111 Animatable loadAnimatable(Fragment fragment, int transit, boolean enter, 112 int transitionStyle) { 113 Animatable animObj = fragment.onCreateAnimatable(transit, enter, 114 fragment.mNextAnim); 115 if (animObj != null) { 116 return animObj; 117 } 118 119 if (fragment.mNextAnim != 0) { 120 Animatable anim = AnimationUtils.loadAnimator(mActivity, fragment.mNextAnim); 121 if (anim != null) { 122 return anim; 123 } 124 } 125 126 if (transit == 0) { 127 return null; 128 } 129 130 int styleIndex = transitToStyleIndex(transit, enter); 131 if (styleIndex < 0) { 132 return null; 133 } 134 135 if (transitionStyle == 0 && mActivity.getWindow() != null) { 136 transitionStyle = mActivity.getWindow().getAttributes().windowAnimations; 137 } 138 if (transitionStyle == 0) { 139 return null; 140 } 141 142 TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, 143 com.android.internal.R.styleable.FragmentAnimation); 144 int anim = attrs.getResourceId(styleIndex, 0); 145 attrs.recycle(); 146 147 if (anim == 0) { 148 return null; 149 } 150 151 return AnimationUtils.loadAnimator(mActivity, anim); 152 } 153 154 void moveToState(Fragment f, int newState, int transit, int transitionStyle) { 155 // Fragments that are not currently added will sit in the onCreate() state. 156 if (!f.mAdded && newState > Fragment.CREATED) { 157 newState = Fragment.CREATED; 158 } 159 160 if (f.mState < newState) { 161 switch (f.mState) { 162 case Fragment.INITIALIZING: 163 if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); 164 f.mActivity = mActivity; 165 f.mCalled = false; 166 f.onAttach(mActivity); 167 if (!f.mCalled) { 168 throw new SuperNotCalledException("Fragment " + f 169 + " did not call through to super.onAttach()"); 170 } 171 mActivity.onAttachFragment(f); 172 173 if (!f.mRetaining) { 174 f.mCalled = false; 175 f.onCreate(f.mSavedFragmentState); 176 if (!f.mCalled) { 177 throw new SuperNotCalledException("Fragment " + f 178 + " did not call through to super.onCreate()"); 179 } 180 } 181 f.mRetaining = false; 182 if (f.mFromLayout) { 183 // For fragments that are part of the content view 184 // layout, we need to instantiate the view immediately 185 // and the inflater will take care of adding it. 186 f.mView = f.onCreateView(mActivity.getLayoutInflater(), 187 null, f.mSavedFragmentState); 188 if (f.mView != null) { 189 f.mView.setSaveFromParentEnabled(false); 190 f.restoreViewState(); 191 if (f.mHidden) f.mView.setVisibility(View.GONE); 192 } 193 } 194 case Fragment.CREATED: 195 if (newState > Fragment.CREATED) { 196 if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f); 197 if (!f.mFromLayout) { 198 ViewGroup container = null; 199 if (f.mContainerId != 0) { 200 container = (ViewGroup)mActivity.findViewById(f.mContainerId); 201 if (container == null) { 202 throw new IllegalArgumentException("New view found for id 0x" 203 + Integer.toHexString(f.mContainerId) 204 + " for fragment " + f); 205 } 206 } 207 f.mContainer = container; 208 f.mView = f.onCreateView(mActivity.getLayoutInflater(), 209 container, f.mSavedFragmentState); 210 if (f.mView != null) { 211 f.mView.setSaveFromParentEnabled(false); 212 if (container != null) { 213 Animatable anim = loadAnimatable(f, transit, true, 214 transitionStyle); 215 if (anim != null) { 216 if (anim instanceof Sequencer) { 217 ((Sequencer)anim).setTarget(f.mView); 218 } else if (anim instanceof PropertyAnimator) { 219 ((PropertyAnimator)anim).setTarget(f.mView); 220 } 221 anim.start(); 222 } 223 container.addView(f.mView); 224 f.restoreViewState(); 225 } 226 if (f.mHidden) f.mView.setVisibility(View.GONE); 227 } 228 } 229 230 f.mCalled = false; 231 f.onActivityCreated(f.mSavedFragmentState); 232 if (!f.mCalled) { 233 throw new SuperNotCalledException("Fragment " + f 234 + " did not call through to super.onReady()"); 235 } 236 f.mSavedFragmentState = null; 237 } 238 case Fragment.ACTIVITY_CREATED: 239 if (newState > Fragment.ACTIVITY_CREATED) { 240 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 241 f.mCalled = false; 242 f.onStart(); 243 if (!f.mCalled) { 244 throw new SuperNotCalledException("Fragment " + f 245 + " did not call through to super.onStart()"); 246 } 247 } 248 case Fragment.STARTED: 249 if (newState > Fragment.STARTED) { 250 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 251 f.mCalled = false; 252 f.mResumed = true; 253 f.onResume(); 254 if (!f.mCalled) { 255 throw new SuperNotCalledException("Fragment " + f 256 + " did not call through to super.onResume()"); 257 } 258 } 259 } 260 } else if (f.mState > newState) { 261 switch (f.mState) { 262 case Fragment.RESUMED: 263 if (newState < Fragment.RESUMED) { 264 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 265 f.mCalled = false; 266 f.onPause(); 267 if (!f.mCalled) { 268 throw new SuperNotCalledException("Fragment " + f 269 + " did not call through to super.onPause()"); 270 } 271 f.mResumed = false; 272 } 273 case Fragment.STARTED: 274 if (newState < Fragment.STARTED) { 275 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 276 f.mCalled = false; 277 f.performStop(); 278 if (!f.mCalled) { 279 throw new SuperNotCalledException("Fragment " + f 280 + " did not call through to super.onStop()"); 281 } 282 } 283 case Fragment.ACTIVITY_CREATED: 284 if (newState < Fragment.ACTIVITY_CREATED) { 285 if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f); 286 if (f.mView != null) { 287 f.mCalled = false; 288 f.onDestroyView(); 289 if (!f.mCalled) { 290 throw new SuperNotCalledException("Fragment " + f 291 + " did not call through to super.onDestroyedView()"); 292 } 293 // Need to save the current view state if not 294 // done already. 295 if (!mActivity.isFinishing() && f.mSavedFragmentState == null) { 296 saveFragmentViewState(f); 297 } 298 if (f.mContainer != null) { 299 if (mCurState > Fragment.INITIALIZING) { 300 Animatable anim = loadAnimatable(f, transit, true, 301 transitionStyle); 302 if (anim != null) { 303 if (anim instanceof Sequencer) { 304 ((Sequencer)anim).setTarget(f.mView); 305 } else if (anim instanceof PropertyAnimator) { 306 ((PropertyAnimator)anim).setTarget(f.mView); 307 } 308 anim.start(); 309 } 310 } 311 f.mContainer.removeView(f.mView); 312 } 313 } 314 f.mContainer = null; 315 f.mView = null; 316 } 317 case Fragment.CREATED: 318 if (newState < Fragment.CREATED) { 319 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 320 if (!f.mRetaining) { 321 f.mCalled = false; 322 f.onDestroy(); 323 if (!f.mCalled) { 324 throw new SuperNotCalledException("Fragment " + f 325 + " did not call through to super.onDestroy()"); 326 } 327 } 328 329 f.mCalled = false; 330 f.onDetach(); 331 if (!f.mCalled) { 332 throw new SuperNotCalledException("Fragment " + f 333 + " did not call through to super.onDetach()"); 334 } 335 f.mActivity = null; 336 } 337 } 338 } 339 340 f.mState = newState; 341 } 342 343 void moveToState(int newState, boolean always) { 344 moveToState(newState, 0, 0, always); 345 } 346 347 void moveToState(int newState, int transit, int transitStyle, boolean always) { 348 if (mActivity == null && newState != Fragment.INITIALIZING) { 349 throw new IllegalStateException("No activity"); 350 } 351 352 if (!always && mCurState == newState) { 353 return; 354 } 355 356 mCurState = newState; 357 if (mActive != null) { 358 for (int i=0; i<mActive.size(); i++) { 359 Fragment f = mActive.get(i); 360 if (f != null) { 361 moveToState(f, newState, transit, transitStyle); 362 } 363 } 364 } 365 } 366 367 void makeActive(Fragment f) { 368 if (f.mIndex >= 0) { 369 return; 370 } 371 372 if (mAvailIndices == null || mAvailIndices.size() <= 0) { 373 if (mActive == null) { 374 mActive = new ArrayList<Fragment>(); 375 } 376 f.setIndex(mActive.size()); 377 mActive.add(f); 378 379 } else { 380 f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1)); 381 mActive.set(f.mIndex, f); 382 } 383 } 384 385 void makeInactive(Fragment f) { 386 if (f.mIndex < 0) { 387 return; 388 } 389 390 mActive.set(f.mIndex, null); 391 if (mAvailIndices == null) { 392 mAvailIndices = new ArrayList<Integer>(); 393 } 394 mAvailIndices.add(f.mIndex); 395 mActivity.invalidateFragmentIndex(f.mIndex); 396 f.clearIndex(); 397 } 398 399 public void addFragment(Fragment fragment, boolean moveToStateNow) { 400 if (mAdded == null) { 401 mAdded = new ArrayList<Fragment>(); 402 } 403 mAdded.add(fragment); 404 makeActive(fragment); 405 if (DEBUG) Log.v(TAG, "add: " + fragment); 406 fragment.mAdded = true; 407 if (fragment.mHasMenu) { 408 mNeedMenuInvalidate = true; 409 } 410 if (moveToStateNow) { 411 moveToState(fragment, mCurState, 0, 0); 412 } 413 } 414 415 public void removeFragment(Fragment fragment, int transition, int transitionStyle) { 416 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 417 mAdded.remove(fragment); 418 final boolean inactive = fragment.mBackStackNesting <= 0; 419 if (fragment.mHasMenu) { 420 mNeedMenuInvalidate = true; 421 } 422 fragment.mAdded = false; 423 moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, 424 transition, transitionStyle); 425 if (inactive) { 426 makeInactive(fragment); 427 } 428 } 429 430 public void hideFragment(Fragment fragment, int transition, int transitionStyle) { 431 if (DEBUG) Log.v(TAG, "hide: " + fragment); 432 if (!fragment.mHidden) { 433 fragment.mHidden = true; 434 if (fragment.mView != null) { 435 Animatable anim = loadAnimatable(fragment, transition, true, 436 transitionStyle); 437 if (anim != null) { 438 if (anim instanceof Sequencer) { 439 ((Sequencer)anim).setTarget(fragment.mView); 440 } else if (anim instanceof PropertyAnimator) { 441 ((PropertyAnimator)anim).setTarget(fragment.mView); 442 } 443 anim.start(); 444 } 445 fragment.mView.setVisibility(View.GONE); 446 } 447 if (fragment.mAdded && fragment.mHasMenu) { 448 mNeedMenuInvalidate = true; 449 } 450 fragment.onHiddenChanged(true); 451 } 452 } 453 454 public void showFragment(Fragment fragment, int transition, int transitionStyle) { 455 if (DEBUG) Log.v(TAG, "show: " + fragment); 456 if (fragment.mHidden) { 457 fragment.mHidden = false; 458 if (fragment.mView != null) { 459 Animatable anim = loadAnimatable(fragment, transition, true, 460 transitionStyle); 461 if (anim != null) { 462 if (anim instanceof Sequencer) { 463 ((Sequencer)anim).setTarget(fragment.mView); 464 } else if (anim instanceof PropertyAnimator) { 465 ((PropertyAnimator)anim).setTarget(fragment.mView); 466 } 467 anim.start(); 468 } 469 fragment.mView.setVisibility(View.VISIBLE); 470 } 471 if (fragment.mAdded && fragment.mHasMenu) { 472 mNeedMenuInvalidate = true; 473 } 474 fragment.onHiddenChanged(false); 475 } 476 } 477 478 public Fragment findFragmentById(int id) { 479 if (mActive != null) { 480 // First look through added fragments. 481 for (int i=mAdded.size()-1; i>=0; i--) { 482 Fragment f = mAdded.get(i); 483 if (f != null && f.mFragmentId == id) { 484 return f; 485 } 486 } 487 // Now for any known fragment. 488 for (int i=mActive.size()-1; i>=0; i--) { 489 Fragment f = mActive.get(i); 490 if (f != null && f.mFragmentId == id) { 491 return f; 492 } 493 } 494 } 495 return null; 496 } 497 498 public Fragment findFragmentByTag(String tag) { 499 if (mActive != null && tag != null) { 500 // First look through added fragments. 501 for (int i=mAdded.size()-1; i>=0; i--) { 502 Fragment f = mAdded.get(i); 503 if (f != null && tag.equals(f.mTag)) { 504 return f; 505 } 506 } 507 // Now for any known fragment. 508 for (int i=mActive.size()-1; i>=0; i--) { 509 Fragment f = mActive.get(i); 510 if (f != null && tag.equals(f.mTag)) { 511 return f; 512 } 513 } 514 } 515 return null; 516 } 517 518 public Fragment findFragmentByWho(String who) { 519 if (mActive != null && who != null) { 520 for (int i=mActive.size()-1; i>=0; i--) { 521 Fragment f = mActive.get(i); 522 if (f != null && who.equals(f.mWho)) { 523 return f; 524 } 525 } 526 } 527 return null; 528 } 529 530 public void enqueueAction(Runnable action) { 531 synchronized (this) { 532 if (mPendingActions == null) { 533 mPendingActions = new ArrayList<Runnable>(); 534 } 535 mPendingActions.add(action); 536 if (mPendingActions.size() == 1) { 537 mActivity.mHandler.removeCallbacks(mExecCommit); 538 mActivity.mHandler.post(mExecCommit); 539 } 540 } 541 } 542 543 public int allocBackStackIndex(BackStackEntry bse) { 544 synchronized (this) { 545 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 546 if (mBackStackIndices == null) { 547 mBackStackIndices = new ArrayList<BackStackEntry>(); 548 } 549 int index = mBackStackIndices.size(); 550 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 551 mBackStackIndices.add(bse); 552 return index; 553 554 } else { 555 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 556 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 557 mBackStackIndices.set(index, bse); 558 return index; 559 } 560 } 561 } 562 563 public void setBackStackIndex(int index, BackStackEntry bse) { 564 synchronized (this) { 565 if (mBackStackIndices == null) { 566 mBackStackIndices = new ArrayList<BackStackEntry>(); 567 } 568 int N = mBackStackIndices.size(); 569 if (index < N) { 570 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 571 mBackStackIndices.set(index, bse); 572 } else { 573 while (N < index) { 574 mBackStackIndices.add(null); 575 if (mAvailBackStackIndices == null) { 576 mAvailBackStackIndices = new ArrayList<Integer>(); 577 } 578 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 579 mAvailBackStackIndices.add(N); 580 N++; 581 } 582 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 583 mBackStackIndices.add(bse); 584 } 585 } 586 } 587 588 public void freeBackStackIndex(int index) { 589 synchronized (this) { 590 mBackStackIndices.set(index, null); 591 if (mAvailBackStackIndices == null) { 592 mAvailBackStackIndices = new ArrayList<Integer>(); 593 } 594 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 595 mAvailBackStackIndices.add(index); 596 } 597 } 598 599 /** 600 * Only call from main thread! 601 */ 602 public void execPendingActions() { 603 if (mExecutingActions) { 604 throw new IllegalStateException("Recursive entry to execPendingActions"); 605 } 606 607 while (true) { 608 int numActions; 609 610 synchronized (this) { 611 if (mPendingActions == null || mPendingActions.size() == 0) { 612 return; 613 } 614 615 numActions = mPendingActions.size(); 616 if (mTmpActions == null || mTmpActions.length < numActions) { 617 mTmpActions = new Runnable[numActions]; 618 } 619 mPendingActions.toArray(mTmpActions); 620 mPendingActions.clear(); 621 mActivity.mHandler.removeCallbacks(mExecCommit); 622 } 623 624 mExecutingActions = true; 625 for (int i=0; i<numActions; i++) { 626 mTmpActions[i].run(); 627 } 628 mExecutingActions = false; 629 } 630 } 631 632 public void addBackStackState(BackStackEntry state) { 633 if (mBackStack == null) { 634 mBackStack = new ArrayList<BackStackEntry>(); 635 } 636 mBackStack.add(state); 637 } 638 639 public boolean popBackStackState(Handler handler, String name, int flags) { 640 return popBackStackState(handler, name, -1, flags); 641 } 642 643 public boolean popBackStackState(Handler handler, int id, int flags) { 644 if (id < 0) { 645 return false; 646 } 647 return popBackStackState(handler, null, id, flags); 648 } 649 650 boolean popBackStackState(Handler handler, String name, int id, int flags) { 651 if (mBackStack == null) { 652 return false; 653 } 654 if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) { 655 int last = mBackStack.size()-1; 656 if (last < 0) { 657 return false; 658 } 659 final BackStackEntry bss = mBackStack.remove(last); 660 enqueueAction(new Runnable() { 661 public void run() { 662 if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss); 663 bss.popFromBackStack(); 664 moveToState(mCurState, reverseTransit(bss.getTransition()), 665 bss.getTransitionStyle(), true); 666 } 667 }); 668 } else { 669 int index = -1; 670 if (name != null || id >= 0) { 671 // If a name or ID is specified, look for that place in 672 // the stack. 673 index = mBackStack.size()-1; 674 while (index >= 0) { 675 BackStackEntry bss = mBackStack.get(index); 676 if (name != null && name.equals(bss.getName())) { 677 break; 678 } 679 if (id >= 0 && id == bss.mIndex) { 680 break; 681 } 682 index--; 683 } 684 if (index < 0) { 685 return false; 686 } 687 if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) { 688 index--; 689 // Consume all following entries that match. 690 while (index >= 0) { 691 BackStackEntry bss = mBackStack.get(index); 692 if ((name != null && name.equals(bss.getName())) 693 || (id >= 0 && id == bss.mIndex)) { 694 index--; 695 continue; 696 } 697 break; 698 } 699 } 700 } 701 if (index == mBackStack.size()-1) { 702 return false; 703 } 704 final ArrayList<BackStackEntry> states 705 = new ArrayList<BackStackEntry>(); 706 for (int i=mBackStack.size()-1; i>index; i--) { 707 states.add(mBackStack.remove(i)); 708 } 709 enqueueAction(new Runnable() { 710 public void run() { 711 for (int i=0; i<states.size(); i++) { 712 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); 713 states.get(i).popFromBackStack(); 714 } 715 moveToState(mCurState, true); 716 } 717 }); 718 } 719 return true; 720 } 721 722 ArrayList<Fragment> retainNonConfig() { 723 ArrayList<Fragment> fragments = null; 724 if (mActive != null) { 725 for (int i=0; i<mActive.size(); i++) { 726 Fragment f = mActive.get(i); 727 if (f != null && f.mRetainInstance) { 728 if (fragments == null) { 729 fragments = new ArrayList<Fragment>(); 730 } 731 fragments.add(f); 732 f.mRetaining = true; 733 } 734 } 735 } 736 return fragments; 737 } 738 739 void saveFragmentViewState(Fragment f) { 740 if (f.mView == null) { 741 return; 742 } 743 if (mStateArray == null) { 744 mStateArray = new SparseArray<Parcelable>(); 745 } 746 f.mView.saveHierarchyState(mStateArray); 747 if (mStateArray.size() > 0) { 748 f.mSavedViewState = mStateArray; 749 mStateArray = null; 750 } 751 } 752 753 Parcelable saveAllState() { 754 if (mActive == null || mActive.size() <= 0) { 755 return null; 756 } 757 758 // First collect all active fragments. 759 int N = mActive.size(); 760 FragmentState[] active = new FragmentState[N]; 761 boolean haveFragments = false; 762 for (int i=0; i<N; i++) { 763 Fragment f = mActive.get(i); 764 if (f != null) { 765 haveFragments = true; 766 767 FragmentState fs = new FragmentState(f); 768 active[i] = fs; 769 770 if (mStateBundle == null) { 771 mStateBundle = new Bundle(); 772 } 773 f.onSaveInstanceState(mStateBundle); 774 if (!mStateBundle.isEmpty()) { 775 fs.mSavedFragmentState = mStateBundle; 776 mStateBundle = null; 777 } 778 779 if (f.mView != null) { 780 saveFragmentViewState(f); 781 if (f.mSavedViewState != null) { 782 if (fs.mSavedFragmentState == null) { 783 fs.mSavedFragmentState = new Bundle(); 784 } 785 fs.mSavedFragmentState.putSparseParcelableArray( 786 FragmentState.VIEW_STATE_TAG, f.mSavedViewState); 787 } 788 } 789 790 } 791 } 792 793 if (!haveFragments) { 794 return null; 795 } 796 797 int[] added = null; 798 BackStackState[] backStack = null; 799 800 // Build list of currently added fragments. 801 N = mAdded.size(); 802 if (N > 0) { 803 added = new int[N]; 804 for (int i=0; i<N; i++) { 805 added[i] = mAdded.get(i).mIndex; 806 } 807 } 808 809 // Now save back stack. 810 if (mBackStack != null) { 811 N = mBackStack.size(); 812 if (N > 0) { 813 backStack = new BackStackState[N]; 814 for (int i=0; i<N; i++) { 815 backStack[i] = new BackStackState(this, mBackStack.get(i)); 816 } 817 } 818 } 819 820 FragmentManagerState fms = new FragmentManagerState(); 821 fms.mActive = active; 822 fms.mAdded = added; 823 fms.mBackStack = backStack; 824 return fms; 825 } 826 827 void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 828 // If there is no saved state at all, then there can not be 829 // any nonConfig fragments either, so that is that. 830 if (state == null) return; 831 FragmentManagerState fms = (FragmentManagerState)state; 832 if (fms.mActive == null) return; 833 834 // First re-attach any non-config instances we are retaining back 835 // to their saved state, so we don't try to instantiate them again. 836 if (nonConfig != null) { 837 for (int i=0; i<nonConfig.size(); i++) { 838 Fragment f = nonConfig.get(i); 839 FragmentState fs = fms.mActive[f.mIndex]; 840 fs.mInstance = f; 841 f.mSavedViewState = null; 842 f.mBackStackNesting = 0; 843 f.mAdded = false; 844 if (fs.mSavedFragmentState != null) { 845 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 846 FragmentState.VIEW_STATE_TAG); 847 } 848 } 849 } 850 851 // Build the full list of active fragments, instantiating them from 852 // their saved state. 853 mActive = new ArrayList<Fragment>(fms.mActive.length); 854 if (mAvailIndices != null) { 855 mAvailIndices.clear(); 856 } 857 for (int i=0; i<fms.mActive.length; i++) { 858 FragmentState fs = fms.mActive[i]; 859 if (fs != null) { 860 mActive.add(fs.instantiate(mActivity)); 861 } else { 862 mActive.add(null); 863 if (mAvailIndices == null) { 864 mAvailIndices = new ArrayList<Integer>(); 865 } 866 mAvailIndices.add(i); 867 } 868 } 869 870 // Build the list of currently added fragments. 871 if (fms.mAdded != null) { 872 mAdded = new ArrayList<Fragment>(fms.mAdded.length); 873 for (int i=0; i<fms.mAdded.length; i++) { 874 Fragment f = mActive.get(fms.mAdded[i]); 875 if (f == null) { 876 throw new IllegalStateException( 877 "No instantiated fragment for index #" + fms.mAdded[i]); 878 } 879 f.mAdded = true; 880 f.mImmediateActivity = mActivity; 881 mAdded.add(f); 882 } 883 } else { 884 mAdded = null; 885 } 886 887 // Build the back stack. 888 if (fms.mBackStack != null) { 889 mBackStack = new ArrayList<BackStackEntry>(fms.mBackStack.length); 890 for (int i=0; i<fms.mBackStack.length; i++) { 891 BackStackEntry bse = fms.mBackStack[i].instantiate(this); 892 mBackStack.add(bse); 893 if (bse.mIndex >= 0) { 894 setBackStackIndex(bse.mIndex, bse); 895 } 896 } 897 } else { 898 mBackStack = null; 899 } 900 } 901 902 public void attachActivity(Activity activity) { 903 if (mActivity != null) throw new IllegalStateException(); 904 mActivity = activity; 905 } 906 907 public void dispatchCreate() { 908 moveToState(Fragment.CREATED, false); 909 } 910 911 public void dispatchActivityCreated() { 912 moveToState(Fragment.ACTIVITY_CREATED, false); 913 } 914 915 public void dispatchStart() { 916 moveToState(Fragment.STARTED, false); 917 } 918 919 public void dispatchResume() { 920 moveToState(Fragment.RESUMED, false); 921 } 922 923 public void dispatchPause() { 924 moveToState(Fragment.STARTED, false); 925 } 926 927 public void dispatchStop() { 928 moveToState(Fragment.ACTIVITY_CREATED, false); 929 } 930 931 public void dispatchDestroy() { 932 moveToState(Fragment.INITIALIZING, false); 933 mActivity = null; 934 } 935 936 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 937 boolean show = false; 938 if (mActive != null) { 939 for (int i=0; i<mAdded.size(); i++) { 940 Fragment f = mAdded.get(i); 941 if (f != null && !f.mHidden && f.mHasMenu) { 942 show = true; 943 f.onCreateOptionsMenu(menu, inflater); 944 } 945 } 946 } 947 return show; 948 } 949 950 public boolean dispatchPrepareOptionsMenu(Menu menu) { 951 boolean show = false; 952 if (mActive != null) { 953 for (int i=0; i<mAdded.size(); i++) { 954 Fragment f = mAdded.get(i); 955 if (f != null && !f.mHidden && f.mHasMenu) { 956 show = true; 957 f.onPrepareOptionsMenu(menu); 958 } 959 } 960 } 961 return show; 962 } 963 964 public boolean dispatchOptionsItemSelected(MenuItem item) { 965 if (mActive != null) { 966 for (int i=0; i<mAdded.size(); i++) { 967 Fragment f = mAdded.get(i); 968 if (f != null && !f.mHidden && f.mHasMenu) { 969 if (f.onOptionsItemSelected(item)) { 970 return true; 971 } 972 } 973 } 974 } 975 return false; 976 } 977 978 public boolean dispatchContextItemSelected(MenuItem item) { 979 if (mActive != null) { 980 for (int i=0; i<mAdded.size(); i++) { 981 Fragment f = mAdded.get(i); 982 if (f != null && !f.mHidden) { 983 if (f.onContextItemSelected(item)) { 984 return true; 985 } 986 } 987 } 988 } 989 return false; 990 } 991 992 public void dispatchOptionsMenuClosed(Menu menu) { 993 if (mActive != null) { 994 for (int i=0; i<mAdded.size(); i++) { 995 Fragment f = mAdded.get(i); 996 if (f != null && !f.mHidden && f.mHasMenu) { 997 f.onOptionsMenuClosed(menu); 998 } 999 } 1000 } 1001 } 1002 1003 public static int reverseTransit(int transit) { 1004 int rev = 0; 1005 switch (transit) { 1006 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1007 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 1008 break; 1009 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1010 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 1011 break; 1012 } 1013 return rev; 1014 1015 } 1016 1017 public static int transitToStyleIndex(int transit, boolean enter) { 1018 int animAttr = -1; 1019 switch (transit) { 1020 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1021 animAttr = enter 1022 ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation 1023 : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation; 1024 break; 1025 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1026 animAttr = enter 1027 ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation 1028 : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation; 1029 break; 1030 } 1031 return animAttr; 1032 } 1033} 1034