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