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