1 /* 2 * Copyright 2018 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 17 package androidx.fragment.app; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21 import android.animation.Animator; 22 import android.animation.AnimatorInflater; 23 import android.animation.AnimatorListenerAdapter; 24 import android.animation.AnimatorSet; 25 import android.animation.PropertyValuesHolder; 26 import android.animation.ValueAnimator; 27 import android.content.Context; 28 import android.content.res.Configuration; 29 import android.content.res.Resources.NotFoundException; 30 import android.content.res.TypedArray; 31 import android.os.Build; 32 import android.os.Bundle; 33 import android.os.Looper; 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 import android.util.AttributeSet; 37 import android.util.Log; 38 import android.util.SparseArray; 39 import android.view.LayoutInflater; 40 import android.view.Menu; 41 import android.view.MenuInflater; 42 import android.view.MenuItem; 43 import android.view.View; 44 import android.view.ViewGroup; 45 import android.view.animation.AccelerateInterpolator; 46 import android.view.animation.AlphaAnimation; 47 import android.view.animation.Animation; 48 import android.view.animation.Animation.AnimationListener; 49 import android.view.animation.AnimationSet; 50 import android.view.animation.AnimationUtils; 51 import android.view.animation.DecelerateInterpolator; 52 import android.view.animation.Interpolator; 53 import android.view.animation.ScaleAnimation; 54 import android.view.animation.Transformation; 55 56 import androidx.annotation.CallSuper; 57 import androidx.annotation.IdRes; 58 import androidx.annotation.NonNull; 59 import androidx.annotation.Nullable; 60 import androidx.annotation.RestrictTo; 61 import androidx.annotation.StringRes; 62 import androidx.collection.ArraySet; 63 import androidx.core.util.DebugUtils; 64 import androidx.core.util.LogWriter; 65 import androidx.core.view.ViewCompat; 66 import androidx.lifecycle.ViewModelStore; 67 68 import java.io.FileDescriptor; 69 import java.io.PrintWriter; 70 import java.lang.reflect.Field; 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.Collections; 74 import java.util.List; 75 import java.util.concurrent.CopyOnWriteArrayList; 76 77 /** 78 * Static library support version of the framework's {@link android.app.FragmentManager}. 79 * Used to write apps that run on platforms prior to Android 3.0. When running 80 * on Android 3.0 or above, this implementation is still used; it does not try 81 * to switch to the framework's implementation. See the framework {@link FragmentManager} 82 * documentation for a class overview. 83 * 84 * <p>Your activity must derive from {@link FragmentActivity} to use this. From such an activity, 85 * you can acquire the {@link FragmentManager} by calling 86 * {@link FragmentActivity#getSupportFragmentManager}. 87 */ 88 public abstract class FragmentManager { 89 /** 90 * Representation of an entry on the fragment back stack, as created 91 * with {@link FragmentTransaction#addToBackStack(String) 92 * FragmentTransaction.addToBackStack()}. Entries can later be 93 * retrieved with {@link FragmentManager#getBackStackEntryAt(int) 94 * FragmentManager.getBackStackEntryAt()}. 95 * 96 * <p>Note that you should never hold on to a BackStackEntry object; 97 * the identifier as returned by {@link #getId} is the only thing that 98 * will be persisted across activity instances. 99 */ 100 public interface BackStackEntry { 101 /** 102 * Return the unique identifier for the entry. This is the only 103 * representation of the entry that will persist across activity 104 * instances. 105 */ getId()106 public int getId(); 107 108 /** 109 * Get the name that was supplied to 110 * {@link FragmentTransaction#addToBackStack(String) 111 * FragmentTransaction.addToBackStack(String)} when creating this entry. 112 */ 113 @Nullable getName()114 public String getName(); 115 116 /** 117 * Return the full bread crumb title resource identifier for the entry, 118 * or 0 if it does not have one. 119 */ 120 @StringRes getBreadCrumbTitleRes()121 public int getBreadCrumbTitleRes(); 122 123 /** 124 * Return the short bread crumb title resource identifier for the entry, 125 * or 0 if it does not have one. 126 */ 127 @StringRes getBreadCrumbShortTitleRes()128 public int getBreadCrumbShortTitleRes(); 129 130 /** 131 * Return the full bread crumb title for the entry, or null if it 132 * does not have one. 133 */ 134 @Nullable getBreadCrumbTitle()135 public CharSequence getBreadCrumbTitle(); 136 137 /** 138 * Return the short bread crumb title for the entry, or null if it 139 * does not have one. 140 */ 141 @Nullable getBreadCrumbShortTitle()142 public CharSequence getBreadCrumbShortTitle(); 143 } 144 145 /** 146 * Interface to watch for changes to the back stack. 147 */ 148 public interface OnBackStackChangedListener { 149 /** 150 * Called whenever the contents of the back stack change. 151 */ onBackStackChanged()152 public void onBackStackChanged(); 153 } 154 155 /** 156 * Start a series of edit operations on the Fragments associated with 157 * this FragmentManager. 158 * 159 * <p>Note: A fragment transaction can only be created/committed prior 160 * to an activity saving its state. If you try to commit a transaction 161 * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()} 162 * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart} 163 * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error. 164 * This is because the framework takes care of saving your current fragments 165 * in the state, and if changes are made after the state is saved then they 166 * will be lost.</p> 167 */ 168 @NonNull beginTransaction()169 public abstract FragmentTransaction beginTransaction(); 170 171 /** 172 * @hide -- remove once prebuilts are in. 173 * @deprecated 174 */ 175 @RestrictTo(LIBRARY_GROUP) 176 @Deprecated openTransaction()177 public FragmentTransaction openTransaction() { 178 return beginTransaction(); 179 } 180 181 /** 182 * After a {@link FragmentTransaction} is committed with 183 * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it 184 * is scheduled to be executed asynchronously on the process's main thread. 185 * If you want to immediately executing any such pending operations, you 186 * can call this function (only from the main thread) to do so. Note that 187 * all callbacks and other related behavior will be done from within this 188 * call, so be careful about where this is called from. 189 * 190 * <p>If you are committing a single transaction that does not modify the 191 * fragment back stack, strongly consider using 192 * {@link FragmentTransaction#commitNow()} instead. This can help avoid 193 * unwanted side effects when other code in your app has pending committed 194 * transactions that expect different timing.</p> 195 * <p> 196 * This also forces the start of any postponed Transactions where 197 * {@link Fragment#postponeEnterTransition()} has been called. 198 * 199 * @return Returns true if there were any pending transactions to be 200 * executed. 201 */ executePendingTransactions()202 public abstract boolean executePendingTransactions(); 203 204 /** 205 * Finds a fragment that was identified by the given id either when inflated 206 * from XML or as the container ID when added in a transaction. This first 207 * searches through fragments that are currently added to the manager's 208 * activity; if no such fragment is found, then all fragments currently 209 * on the back stack associated with this ID are searched. 210 * @return The fragment if found or null otherwise. 211 */ 212 @Nullable findFragmentById(@dRes int id)213 public abstract Fragment findFragmentById(@IdRes int id); 214 215 /** 216 * Finds a fragment that was identified by the given tag either when inflated 217 * from XML or as supplied when added in a transaction. This first 218 * searches through fragments that are currently added to the manager's 219 * activity; if no such fragment is found, then all fragments currently 220 * on the back stack are searched. 221 * @return The fragment if found or null otherwise. 222 */ 223 @Nullable findFragmentByTag(@ullable String tag)224 public abstract Fragment findFragmentByTag(@Nullable String tag); 225 226 /** 227 * Flag for {@link #popBackStack(String, int)} 228 * and {@link #popBackStack(int, int)}: If set, and the name or ID of 229 * a back stack entry has been supplied, then all matching entries will 230 * be consumed until one that doesn't match is found or the bottom of 231 * the stack is reached. Otherwise, all entries up to but not including that entry 232 * will be removed. 233 */ 234 public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; 235 236 /** 237 * Pop the top state off the back stack. Returns true if there was one 238 * to pop, else false. This function is asynchronous -- it enqueues the 239 * request to pop, but the action will not be performed until the application 240 * returns to its event loop. 241 */ popBackStack()242 public abstract void popBackStack(); 243 244 /** 245 * Like {@link #popBackStack()}, but performs the operation immediately 246 * inside of the call. This is like calling {@link #executePendingTransactions()} 247 * afterwards without forcing the start of postponed Transactions. 248 * @return Returns true if there was something popped, else false. 249 */ popBackStackImmediate()250 public abstract boolean popBackStackImmediate(); 251 252 /** 253 * Pop the last fragment transition from the manager's fragment 254 * back stack. If there is nothing to pop, false is returned. 255 * This function is asynchronous -- it enqueues the 256 * request to pop, but the action will not be performed until the application 257 * returns to its event loop. 258 * 259 * @param name If non-null, this is the name of a previous back state 260 * to look for; if found, all states up to that state will be popped. The 261 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 262 * the named state itself is popped. If null, only the top state is popped. 263 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 264 */ popBackStack(@ullable String name, int flags)265 public abstract void popBackStack(@Nullable String name, int flags); 266 267 /** 268 * Like {@link #popBackStack(String, int)}, but performs the operation immediately 269 * inside of the call. This is like calling {@link #executePendingTransactions()} 270 * afterwards without forcing the start of postponed Transactions. 271 * @return Returns true if there was something popped, else false. 272 */ popBackStackImmediate(@ullable String name, int flags)273 public abstract boolean popBackStackImmediate(@Nullable String name, int flags); 274 275 /** 276 * Pop all back stack states up to the one with the given identifier. 277 * This function is asynchronous -- it enqueues the 278 * request to pop, but the action will not be performed until the application 279 * returns to its event loop. 280 * 281 * @param id Identifier of the stated to be popped. If no identifier exists, 282 * false is returned. 283 * The identifier is the number returned by 284 * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The 285 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 286 * the named state itself is popped. 287 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 288 */ popBackStack(int id, int flags)289 public abstract void popBackStack(int id, int flags); 290 291 /** 292 * Like {@link #popBackStack(int, int)}, but performs the operation immediately 293 * inside of the call. This is like calling {@link #executePendingTransactions()} 294 * afterwards without forcing the start of postponed Transactions. 295 * @return Returns true if there was something popped, else false. 296 */ popBackStackImmediate(int id, int flags)297 public abstract boolean popBackStackImmediate(int id, int flags); 298 299 /** 300 * Return the number of entries currently in the back stack. 301 */ getBackStackEntryCount()302 public abstract int getBackStackEntryCount(); 303 304 /** 305 * Return the BackStackEntry at index <var>index</var> in the back stack; 306 * entries start index 0 being the bottom of the stack. 307 */ 308 @NonNull getBackStackEntryAt(int index)309 public abstract BackStackEntry getBackStackEntryAt(int index); 310 311 /** 312 * Add a new listener for changes to the fragment back stack. 313 */ addOnBackStackChangedListener( @onNull OnBackStackChangedListener listener)314 public abstract void addOnBackStackChangedListener( 315 @NonNull OnBackStackChangedListener listener); 316 317 /** 318 * Remove a listener that was previously added with 319 * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. 320 */ removeOnBackStackChangedListener( @onNull OnBackStackChangedListener listener)321 public abstract void removeOnBackStackChangedListener( 322 @NonNull OnBackStackChangedListener listener); 323 324 /** 325 * Put a reference to a fragment in a Bundle. This Bundle can be 326 * persisted as saved state, and when later restoring 327 * {@link #getFragment(Bundle, String)} will return the current 328 * instance of the same fragment. 329 * 330 * @param bundle The bundle in which to put the fragment reference. 331 * @param key The name of the entry in the bundle. 332 * @param fragment The Fragment whose reference is to be stored. 333 */ putFragment(@onNull Bundle bundle, @NonNull String key, @NonNull Fragment fragment)334 public abstract void putFragment(@NonNull Bundle bundle, @NonNull String key, 335 @NonNull Fragment fragment); 336 337 /** 338 * Retrieve the current Fragment instance for a reference previously 339 * placed with {@link #putFragment(Bundle, String, Fragment)}. 340 * 341 * @param bundle The bundle from which to retrieve the fragment reference. 342 * @param key The name of the entry in the bundle. 343 * @return Returns the current Fragment instance that is associated with 344 * the given reference. 345 */ 346 @Nullable getFragment(@onNull Bundle bundle, @NonNull String key)347 public abstract Fragment getFragment(@NonNull Bundle bundle, @NonNull String key); 348 349 /** 350 * Get a list of all fragments that are currently added to the FragmentManager. 351 * This may include those that are hidden as well as those that are shown. 352 * This will not include any fragments only in the back stack, or fragments that 353 * are detached or removed. 354 * <p> 355 * The order of the fragments in the list is the order in which they were 356 * added or attached. 357 * 358 * @return A list of all fragments that are added to the FragmentManager. 359 */ 360 @NonNull getFragments()361 public abstract List<Fragment> getFragments(); 362 363 /** 364 * Save the current instance state of the given Fragment. This can be 365 * used later when creating a new instance of the Fragment and adding 366 * it to the fragment manager, to have it create itself to match the 367 * current state returned here. Note that there are limits on how 368 * this can be used: 369 * 370 * <ul> 371 * <li>The Fragment must currently be attached to the FragmentManager. 372 * <li>A new Fragment created using this saved state must be the same class 373 * type as the Fragment it was created from. 374 * <li>The saved state can not contain dependencies on other fragments -- 375 * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to 376 * store a fragment reference because that reference may not be valid when 377 * this saved state is later used. Likewise the Fragment's target and 378 * result code are not included in this state. 379 * </ul> 380 * 381 * @param f The Fragment whose state is to be saved. 382 * @return The generated state. This will be null if there was no 383 * interesting state created by the fragment. 384 */ 385 @Nullable saveFragmentInstanceState(Fragment f)386 public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f); 387 388 /** 389 * Returns true if the final {@link android.app.Activity#onDestroy() Activity.onDestroy()} 390 * call has been made on the FragmentManager's Activity, so this instance is now dead. 391 */ isDestroyed()392 public abstract boolean isDestroyed(); 393 394 /** 395 * Registers a {@link FragmentLifecycleCallbacks} to listen to fragment lifecycle events 396 * happening in this FragmentManager. All registered callbacks will be automatically 397 * unregistered when this FragmentManager is destroyed. 398 * 399 * @param cb Callbacks to register 400 * @param recursive true to automatically register this callback for all child FragmentManagers 401 */ registerFragmentLifecycleCallbacks(@onNull FragmentLifecycleCallbacks cb, boolean recursive)402 public abstract void registerFragmentLifecycleCallbacks(@NonNull FragmentLifecycleCallbacks cb, 403 boolean recursive); 404 405 /** 406 * Unregisters a previously registered {@link FragmentLifecycleCallbacks}. If the callback 407 * was not previously registered this call has no effect. All registered callbacks will be 408 * automatically unregistered when this FragmentManager is destroyed. 409 * 410 * @param cb Callbacks to unregister 411 */ unregisterFragmentLifecycleCallbacks( @onNull FragmentLifecycleCallbacks cb)412 public abstract void unregisterFragmentLifecycleCallbacks( 413 @NonNull FragmentLifecycleCallbacks cb); 414 415 /** 416 * Return the currently active primary navigation fragment for this FragmentManager. 417 * The primary navigation fragment is set by fragment transactions using 418 * {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}. 419 * 420 * <p>The primary navigation fragment's 421 * {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first 422 * to process delegated navigation actions such as {@link #popBackStack()} if no ID 423 * or transaction name is provided to pop to.</p> 424 * 425 * @return the fragment designated as the primary navigation fragment 426 */ 427 @Nullable getPrimaryNavigationFragment()428 public abstract Fragment getPrimaryNavigationFragment(); 429 430 /** 431 * Print the FragmentManager's state into the given stream. 432 * 433 * @param prefix Text to print at the front of each line. 434 * @param fd The raw file descriptor that the dump is being sent to. 435 * @param writer A PrintWriter to which the dump is to be set. 436 * @param args Additional arguments to the dump request. 437 */ dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)438 public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); 439 440 /** 441 * Control whether the framework's internal fragment manager debugging 442 * logs are turned on. If enabled, you will see output in logcat as 443 * the framework performs fragment operations. 444 */ enableDebugLogging(boolean enabled)445 public static void enableDebugLogging(boolean enabled) { 446 FragmentManagerImpl.DEBUG = enabled; 447 } 448 449 /** 450 * Returns {@code true} if the FragmentManager's state has already been saved 451 * by its host. Any operations that would change saved state should not be performed 452 * if this method returns true. For example, any popBackStack() method, such as 453 * {@link #popBackStackImmediate()} or any FragmentTransaction using 454 * {@link FragmentTransaction#commit()} instead of 455 * {@link FragmentTransaction#commitAllowingStateLoss()} will change 456 * the state and will result in an error. 457 * 458 * @return true if this FragmentManager's state has already been saved by its host 459 */ isStateSaved()460 public abstract boolean isStateSaved(); 461 462 /** 463 * Callback interface for listening to fragment state changes that happen 464 * within a given FragmentManager. 465 */ 466 public abstract static class FragmentLifecycleCallbacks { 467 /** 468 * Called right before the fragment's {@link Fragment#onAttach(Context)} method is called. 469 * This is a good time to inject any required dependencies or perform other configuration 470 * for the fragment before any of the fragment's lifecycle methods are invoked. 471 * 472 * @param fm Host FragmentManager 473 * @param f Fragment changing state 474 * @param context Context that the Fragment is being attached to 475 */ onFragmentPreAttached(@onNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context)476 public void onFragmentPreAttached(@NonNull FragmentManager fm, @NonNull Fragment f, 477 @NonNull Context context) {} 478 479 /** 480 * Called after the fragment has been attached to its host. Its host will have had 481 * <code>onAttachFragment</code> called before this call happens. 482 * 483 * @param fm Host FragmentManager 484 * @param f Fragment changing state 485 * @param context Context that the Fragment was attached to 486 */ onFragmentAttached(@onNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context)487 public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, 488 @NonNull Context context) {} 489 490 /** 491 * Called right before the fragment's {@link Fragment#onCreate(Bundle)} method is called. 492 * This is a good time to inject any required dependencies or perform other configuration 493 * for the fragment. 494 * 495 * @param fm Host FragmentManager 496 * @param f Fragment changing state 497 * @param savedInstanceState Saved instance bundle from a previous instance 498 */ onFragmentPreCreated(@onNull FragmentManager fm, @NonNull Fragment f, @Nullable Bundle savedInstanceState)499 public void onFragmentPreCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 500 @Nullable Bundle savedInstanceState) {} 501 502 /** 503 * Called after the fragment has returned from the FragmentManager's call to 504 * {@link Fragment#onCreate(Bundle)}. This will only happen once for any given 505 * fragment instance, though the fragment may be attached and detached multiple times. 506 * 507 * @param fm Host FragmentManager 508 * @param f Fragment changing state 509 * @param savedInstanceState Saved instance bundle from a previous instance 510 */ onFragmentCreated(@onNull FragmentManager fm, @NonNull Fragment f, @Nullable Bundle savedInstanceState)511 public void onFragmentCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 512 @Nullable Bundle savedInstanceState) {} 513 514 /** 515 * Called after the fragment has returned from the FragmentManager's call to 516 * {@link Fragment#onActivityCreated(Bundle)}. This will only happen once for any given 517 * fragment instance, though the fragment may be attached and detached multiple times. 518 * 519 * @param fm Host FragmentManager 520 * @param f Fragment changing state 521 * @param savedInstanceState Saved instance bundle from a previous instance 522 */ onFragmentActivityCreated(@onNull FragmentManager fm, @NonNull Fragment f, @Nullable Bundle savedInstanceState)523 public void onFragmentActivityCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 524 @Nullable Bundle savedInstanceState) {} 525 526 /** 527 * Called after the fragment has returned a non-null view from the FragmentManager's 528 * request to {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. 529 * 530 * @param fm Host FragmentManager 531 * @param f Fragment that created and owns the view 532 * @param v View returned by the fragment 533 * @param savedInstanceState Saved instance bundle from a previous instance 534 */ onFragmentViewCreated(@onNull FragmentManager fm, @NonNull Fragment f, @NonNull View v, @Nullable Bundle savedInstanceState)535 public void onFragmentViewCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 536 @NonNull View v, @Nullable Bundle savedInstanceState) {} 537 538 /** 539 * Called after the fragment has returned from the FragmentManager's call to 540 * {@link Fragment#onStart()}. 541 * 542 * @param fm Host FragmentManager 543 * @param f Fragment changing state 544 */ onFragmentStarted(@onNull FragmentManager fm, @NonNull Fragment f)545 public void onFragmentStarted(@NonNull FragmentManager fm, @NonNull Fragment f) {} 546 547 /** 548 * Called after the fragment has returned from the FragmentManager's call to 549 * {@link Fragment#onResume()}. 550 * 551 * @param fm Host FragmentManager 552 * @param f Fragment changing state 553 */ onFragmentResumed(@onNull FragmentManager fm, @NonNull Fragment f)554 public void onFragmentResumed(@NonNull FragmentManager fm, @NonNull Fragment f) {} 555 556 /** 557 * Called after the fragment has returned from the FragmentManager's call to 558 * {@link Fragment#onPause()}. 559 * 560 * @param fm Host FragmentManager 561 * @param f Fragment changing state 562 */ onFragmentPaused(@onNull FragmentManager fm, @NonNull Fragment f)563 public void onFragmentPaused(@NonNull FragmentManager fm, @NonNull Fragment f) {} 564 565 /** 566 * Called after the fragment has returned from the FragmentManager's call to 567 * {@link Fragment#onStop()}. 568 * 569 * @param fm Host FragmentManager 570 * @param f Fragment changing state 571 */ onFragmentStopped(@onNull FragmentManager fm, @NonNull Fragment f)572 public void onFragmentStopped(@NonNull FragmentManager fm, @NonNull Fragment f) {} 573 574 /** 575 * Called after the fragment has returned from the FragmentManager's call to 576 * {@link Fragment#onSaveInstanceState(Bundle)}. 577 * 578 * @param fm Host FragmentManager 579 * @param f Fragment changing state 580 * @param outState Saved state bundle for the fragment 581 */ onFragmentSaveInstanceState(@onNull FragmentManager fm, @NonNull Fragment f, @NonNull Bundle outState)582 public void onFragmentSaveInstanceState(@NonNull FragmentManager fm, @NonNull Fragment f, 583 @NonNull Bundle outState) {} 584 585 /** 586 * Called after the fragment has returned from the FragmentManager's call to 587 * {@link Fragment#onDestroyView()}. 588 * 589 * @param fm Host FragmentManager 590 * @param f Fragment changing state 591 */ onFragmentViewDestroyed(@onNull FragmentManager fm, @NonNull Fragment f)592 public void onFragmentViewDestroyed(@NonNull FragmentManager fm, @NonNull Fragment f) {} 593 594 /** 595 * Called after the fragment has returned from the FragmentManager's call to 596 * {@link Fragment#onDestroy()}. 597 * 598 * @param fm Host FragmentManager 599 * @param f Fragment changing state 600 */ onFragmentDestroyed(@onNull FragmentManager fm, @NonNull Fragment f)601 public void onFragmentDestroyed(@NonNull FragmentManager fm, @NonNull Fragment f) {} 602 603 /** 604 * Called after the fragment has returned from the FragmentManager's call to 605 * {@link Fragment#onDetach()}. 606 * 607 * @param fm Host FragmentManager 608 * @param f Fragment changing state 609 */ onFragmentDetached(@onNull FragmentManager fm, @NonNull Fragment f)610 public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {} 611 } 612 } 613 614 final class FragmentManagerState implements Parcelable { 615 FragmentState[] mActive; 616 int[] mAdded; 617 BackStackState[] mBackStack; 618 int mPrimaryNavActiveIndex = -1; 619 int mNextFragmentIndex; 620 FragmentManagerState()621 public FragmentManagerState() { 622 } 623 FragmentManagerState(Parcel in)624 public FragmentManagerState(Parcel in) { 625 mActive = in.createTypedArray(FragmentState.CREATOR); 626 mAdded = in.createIntArray(); 627 mBackStack = in.createTypedArray(BackStackState.CREATOR); 628 mPrimaryNavActiveIndex = in.readInt(); 629 mNextFragmentIndex = in.readInt(); 630 } 631 632 @Override describeContents()633 public int describeContents() { 634 return 0; 635 } 636 637 @Override writeToParcel(Parcel dest, int flags)638 public void writeToParcel(Parcel dest, int flags) { 639 dest.writeTypedArray(mActive, flags); 640 dest.writeIntArray(mAdded); 641 dest.writeTypedArray(mBackStack, flags); 642 dest.writeInt(mPrimaryNavActiveIndex); 643 dest.writeInt(mNextFragmentIndex); 644 } 645 646 public static final Parcelable.Creator<FragmentManagerState> CREATOR 647 = new Parcelable.Creator<FragmentManagerState>() { 648 @Override 649 public FragmentManagerState createFromParcel(Parcel in) { 650 return new FragmentManagerState(in); 651 } 652 653 @Override 654 public FragmentManagerState[] newArray(int size) { 655 return new FragmentManagerState[size]; 656 } 657 }; 658 } 659 660 /** 661 * Container for fragments associated with an activity. 662 */ 663 final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 { 664 static boolean DEBUG = false; 665 static final String TAG = "FragmentManager"; 666 667 static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; 668 static final String TARGET_STATE_TAG = "android:target_state"; 669 static final String VIEW_STATE_TAG = "android:view_state"; 670 static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint"; 671 672 private static final class FragmentLifecycleCallbacksHolder { 673 final FragmentLifecycleCallbacks mCallback; 674 final boolean mRecursive; 675 FragmentLifecycleCallbacksHolder(FragmentLifecycleCallbacks callback, boolean recursive)676 FragmentLifecycleCallbacksHolder(FragmentLifecycleCallbacks callback, boolean recursive) { 677 mCallback = callback; 678 mRecursive = recursive; 679 } 680 } 681 682 ArrayList<OpGenerator> mPendingActions; 683 boolean mExecutingActions; 684 685 int mNextFragmentIndex = 0; 686 687 final ArrayList<Fragment> mAdded = new ArrayList<>(); 688 SparseArray<Fragment> mActive; 689 ArrayList<BackStackRecord> mBackStack; 690 ArrayList<Fragment> mCreatedMenus; 691 692 // Must be accessed while locked. 693 ArrayList<BackStackRecord> mBackStackIndices; 694 ArrayList<Integer> mAvailBackStackIndices; 695 696 ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; 697 private final CopyOnWriteArrayList<FragmentLifecycleCallbacksHolder> 698 mLifecycleCallbacks = new CopyOnWriteArrayList<>(); 699 700 int mCurState = Fragment.INITIALIZING; 701 FragmentHostCallback mHost; 702 FragmentContainer mContainer; 703 Fragment mParent; 704 @Nullable Fragment mPrimaryNav; 705 706 static Field sAnimationListenerField = null; 707 708 boolean mNeedMenuInvalidate; 709 boolean mStateSaved; 710 boolean mStopped; 711 boolean mDestroyed; 712 String mNoTransactionsBecause; 713 boolean mHavePendingDeferredStart; 714 715 // Temporary vars for removing redundant operations in BackStackRecords: 716 ArrayList<BackStackRecord> mTmpRecords; 717 ArrayList<Boolean> mTmpIsPop; 718 ArrayList<Fragment> mTmpAddedFragments; 719 720 // Temporary vars for state save and restore. 721 Bundle mStateBundle = null; 722 SparseArray<Parcelable> mStateArray = null; 723 724 // Postponed transactions. 725 ArrayList<StartEnterTransitionListener> mPostponedTransactions; 726 727 // Saved FragmentManagerNonConfig during saveAllState() and cleared in noteStateNotSaved() 728 FragmentManagerNonConfig mSavedNonConfig; 729 730 Runnable mExecCommit = new Runnable() { 731 @Override 732 public void run() { 733 execPendingActions(); 734 } 735 }; 736 modifiesAlpha(AnimationOrAnimator anim)737 static boolean modifiesAlpha(AnimationOrAnimator anim) { 738 if (anim.animation instanceof AlphaAnimation) { 739 return true; 740 } else if (anim.animation instanceof AnimationSet) { 741 List<Animation> anims = ((AnimationSet) anim.animation).getAnimations(); 742 for (int i = 0; i < anims.size(); i++) { 743 if (anims.get(i) instanceof AlphaAnimation) { 744 return true; 745 } 746 } 747 return false; 748 } else { 749 return modifiesAlpha(anim.animator); 750 } 751 } 752 modifiesAlpha(Animator anim)753 static boolean modifiesAlpha(Animator anim) { 754 if (anim == null) { 755 return false; 756 } 757 if (anim instanceof ValueAnimator) { 758 ValueAnimator valueAnim = (ValueAnimator) anim; 759 PropertyValuesHolder[] values = valueAnim.getValues(); 760 for (int i = 0; i < values.length; i++) { 761 if (("alpha").equals(values[i].getPropertyName())) { 762 return true; 763 } 764 } 765 } else if (anim instanceof AnimatorSet) { 766 List<Animator> animList = ((AnimatorSet) anim).getChildAnimations(); 767 for (int i = 0; i < animList.size(); i++) { 768 if (modifiesAlpha(animList.get(i))) { 769 return true; 770 } 771 } 772 } 773 return false; 774 } 775 shouldRunOnHWLayer(View v, AnimationOrAnimator anim)776 static boolean shouldRunOnHWLayer(View v, AnimationOrAnimator anim) { 777 if (v == null || anim == null) { 778 return false; 779 } 780 return Build.VERSION.SDK_INT >= 19 781 && v.getLayerType() == View.LAYER_TYPE_NONE 782 && ViewCompat.hasOverlappingRendering(v) 783 && modifiesAlpha(anim); 784 } 785 throwException(RuntimeException ex)786 private void throwException(RuntimeException ex) { 787 Log.e(TAG, ex.getMessage()); 788 Log.e(TAG, "Activity state:"); 789 LogWriter logw = new LogWriter(TAG); 790 PrintWriter pw = new PrintWriter(logw); 791 if (mHost != null) { 792 try { 793 mHost.onDump(" ", null, pw, new String[] { }); 794 } catch (Exception e) { 795 Log.e(TAG, "Failed dumping state", e); 796 } 797 } else { 798 try { 799 dump(" ", null, pw, new String[] { }); 800 } catch (Exception e) { 801 Log.e(TAG, "Failed dumping state", e); 802 } 803 } 804 throw ex; 805 } 806 807 @Override beginTransaction()808 public FragmentTransaction beginTransaction() { 809 return new BackStackRecord(this); 810 } 811 812 @Override executePendingTransactions()813 public boolean executePendingTransactions() { 814 boolean updates = execPendingActions(); 815 forcePostponedTransactions(); 816 return updates; 817 } 818 819 @Override popBackStack()820 public void popBackStack() { 821 enqueueAction(new PopBackStackState(null, -1, 0), false); 822 } 823 824 @Override popBackStackImmediate()825 public boolean popBackStackImmediate() { 826 checkStateLoss(); 827 return popBackStackImmediate(null, -1, 0); 828 } 829 830 @Override popBackStack(@ullable final String name, final int flags)831 public void popBackStack(@Nullable final String name, final int flags) { 832 enqueueAction(new PopBackStackState(name, -1, flags), false); 833 } 834 835 @Override popBackStackImmediate(@ullable String name, int flags)836 public boolean popBackStackImmediate(@Nullable String name, int flags) { 837 checkStateLoss(); 838 return popBackStackImmediate(name, -1, flags); 839 } 840 841 @Override popBackStack(final int id, final int flags)842 public void popBackStack(final int id, final int flags) { 843 if (id < 0) { 844 throw new IllegalArgumentException("Bad id: " + id); 845 } 846 enqueueAction(new PopBackStackState(null, id, flags), false); 847 } 848 849 @Override popBackStackImmediate(int id, int flags)850 public boolean popBackStackImmediate(int id, int flags) { 851 checkStateLoss(); 852 execPendingActions(); 853 if (id < 0) { 854 throw new IllegalArgumentException("Bad id: " + id); 855 } 856 return popBackStackImmediate(null, id, flags); 857 } 858 859 /** 860 * Used by all public popBackStackImmediate methods, this executes pending transactions and 861 * returns true if the pop action did anything, regardless of what other pending 862 * transactions did. 863 * 864 * @return true if the pop operation did anything or false otherwise. 865 */ popBackStackImmediate(String name, int id, int flags)866 private boolean popBackStackImmediate(String name, int id, int flags) { 867 execPendingActions(); 868 ensureExecReady(true); 869 870 if (mPrimaryNav != null // We have a primary nav fragment 871 && id < 0 // No valid id (since they're local) 872 && name == null) { // no name to pop to (since they're local) 873 final FragmentManager childManager = mPrimaryNav.peekChildFragmentManager(); 874 if (childManager != null && childManager.popBackStackImmediate()) { 875 // We did something, just not to this specific FragmentManager. Return true. 876 return true; 877 } 878 } 879 880 boolean executePop = popBackStackState(mTmpRecords, mTmpIsPop, name, id, flags); 881 if (executePop) { 882 mExecutingActions = true; 883 try { 884 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); 885 } finally { 886 cleanupExec(); 887 } 888 } 889 890 doPendingDeferredStart(); 891 burpActive(); 892 return executePop; 893 } 894 895 @Override getBackStackEntryCount()896 public int getBackStackEntryCount() { 897 return mBackStack != null ? mBackStack.size() : 0; 898 } 899 900 @Override getBackStackEntryAt(int index)901 public BackStackEntry getBackStackEntryAt(int index) { 902 return mBackStack.get(index); 903 } 904 905 @Override addOnBackStackChangedListener(OnBackStackChangedListener listener)906 public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { 907 if (mBackStackChangeListeners == null) { 908 mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>(); 909 } 910 mBackStackChangeListeners.add(listener); 911 } 912 913 @Override removeOnBackStackChangedListener(OnBackStackChangedListener listener)914 public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { 915 if (mBackStackChangeListeners != null) { 916 mBackStackChangeListeners.remove(listener); 917 } 918 } 919 920 @Override putFragment(Bundle bundle, String key, Fragment fragment)921 public void putFragment(Bundle bundle, String key, Fragment fragment) { 922 if (fragment.mIndex < 0) { 923 throwException(new IllegalStateException("Fragment " + fragment 924 + " is not currently in the FragmentManager")); 925 } 926 bundle.putInt(key, fragment.mIndex); 927 } 928 929 @Override 930 @Nullable getFragment(Bundle bundle, String key)931 public Fragment getFragment(Bundle bundle, String key) { 932 int index = bundle.getInt(key, -1); 933 if (index == -1) { 934 return null; 935 } 936 Fragment f = mActive.get(index); 937 if (f == null) { 938 throwException(new IllegalStateException("Fragment no longer exists for key " 939 + key + ": index " + index)); 940 } 941 return f; 942 } 943 944 @Override getFragments()945 public List<Fragment> getFragments() { 946 if (mAdded.isEmpty()) { 947 return Collections.EMPTY_LIST; 948 } 949 synchronized (mAdded) { 950 return (List<Fragment>) mAdded.clone(); 951 } 952 } 953 954 /** 955 * This is used by FragmentController to get the Active fragments. 956 * 957 * @return A list of active fragments in the fragment manager, including those that are in the 958 * back stack. 959 */ getActiveFragments()960 List<Fragment> getActiveFragments() { 961 if (mActive == null) { 962 return null; 963 } 964 final int count = mActive.size(); 965 ArrayList<Fragment> fragments = new ArrayList<>(count); 966 for (int i = 0; i < count; i++) { 967 fragments.add(mActive.valueAt(i)); 968 } 969 return fragments; 970 } 971 972 /** 973 * Used by FragmentController to get the number of Active Fragments. 974 * 975 * @return The number of active fragments. 976 */ getActiveFragmentCount()977 int getActiveFragmentCount() { 978 if (mActive == null) { 979 return 0; 980 } 981 return mActive.size(); 982 } 983 984 @Override 985 @Nullable saveFragmentInstanceState(Fragment fragment)986 public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) { 987 if (fragment.mIndex < 0) { 988 throwException( new IllegalStateException("Fragment " + fragment 989 + " is not currently in the FragmentManager")); 990 } 991 if (fragment.mState > Fragment.INITIALIZING) { 992 Bundle result = saveFragmentBasicState(fragment); 993 return result != null ? new Fragment.SavedState(result) : null; 994 } 995 return null; 996 } 997 998 @Override isDestroyed()999 public boolean isDestroyed() { 1000 return mDestroyed; 1001 } 1002 1003 @Override toString()1004 public String toString() { 1005 StringBuilder sb = new StringBuilder(128); 1006 sb.append("FragmentManager{"); 1007 sb.append(Integer.toHexString(System.identityHashCode(this))); 1008 sb.append(" in "); 1009 if (mParent != null) { 1010 DebugUtils.buildShortClassTag(mParent, sb); 1011 } else { 1012 DebugUtils.buildShortClassTag(mHost, sb); 1013 } 1014 sb.append("}}"); 1015 return sb.toString(); 1016 } 1017 1018 @Override dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)1019 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 1020 String innerPrefix = prefix + " "; 1021 1022 int N; 1023 if (mActive != null) { 1024 N = mActive.size(); 1025 if (N > 0) { 1026 writer.print(prefix); writer.print("Active Fragments in "); 1027 writer.print(Integer.toHexString(System.identityHashCode(this))); 1028 writer.println(":"); 1029 for (int i=0; i<N; i++) { 1030 Fragment f = mActive.valueAt(i); 1031 writer.print(prefix); writer.print(" #"); writer.print(i); 1032 writer.print(": "); writer.println(f); 1033 if (f != null) { 1034 f.dump(innerPrefix, fd, writer, args); 1035 } 1036 } 1037 } 1038 } 1039 1040 N = mAdded.size(); 1041 if (N > 0) { 1042 writer.print(prefix); writer.println("Added Fragments:"); 1043 for (int i = 0; i < N; i++) { 1044 Fragment f = mAdded.get(i); 1045 writer.print(prefix); 1046 writer.print(" #"); 1047 writer.print(i); 1048 writer.print(": "); 1049 writer.println(f.toString()); 1050 } 1051 } 1052 1053 if (mCreatedMenus != null) { 1054 N = mCreatedMenus.size(); 1055 if (N > 0) { 1056 writer.print(prefix); writer.println("Fragments Created Menus:"); 1057 for (int i=0; i<N; i++) { 1058 Fragment f = mCreatedMenus.get(i); 1059 writer.print(prefix); writer.print(" #"); writer.print(i); 1060 writer.print(": "); writer.println(f.toString()); 1061 } 1062 } 1063 } 1064 1065 if (mBackStack != null) { 1066 N = mBackStack.size(); 1067 if (N > 0) { 1068 writer.print(prefix); writer.println("Back Stack:"); 1069 for (int i=0; i<N; i++) { 1070 BackStackRecord bs = mBackStack.get(i); 1071 writer.print(prefix); writer.print(" #"); writer.print(i); 1072 writer.print(": "); writer.println(bs.toString()); 1073 bs.dump(innerPrefix, fd, writer, args); 1074 } 1075 } 1076 } 1077 1078 synchronized (this) { 1079 if (mBackStackIndices != null) { 1080 N = mBackStackIndices.size(); 1081 if (N > 0) { 1082 writer.print(prefix); writer.println("Back Stack Indices:"); 1083 for (int i=0; i<N; i++) { 1084 BackStackRecord bs = mBackStackIndices.get(i); 1085 writer.print(prefix); writer.print(" #"); writer.print(i); 1086 writer.print(": "); writer.println(bs); 1087 } 1088 } 1089 } 1090 1091 if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) { 1092 writer.print(prefix); writer.print("mAvailBackStackIndices: "); 1093 writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); 1094 } 1095 } 1096 1097 if (mPendingActions != null) { 1098 N = mPendingActions.size(); 1099 if (N > 0) { 1100 writer.print(prefix); writer.println("Pending Actions:"); 1101 for (int i=0; i<N; i++) { 1102 OpGenerator r = mPendingActions.get(i); 1103 writer.print(prefix); writer.print(" #"); writer.print(i); 1104 writer.print(": "); writer.println(r); 1105 } 1106 } 1107 } 1108 1109 writer.print(prefix); writer.println("FragmentManager misc state:"); 1110 writer.print(prefix); writer.print(" mHost="); writer.println(mHost); 1111 writer.print(prefix); writer.print(" mContainer="); writer.println(mContainer); 1112 if (mParent != null) { 1113 writer.print(prefix); writer.print(" mParent="); writer.println(mParent); 1114 } 1115 writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState); 1116 writer.print(" mStateSaved="); writer.print(mStateSaved); 1117 writer.print(" mStopped="); writer.print(mStopped); 1118 writer.print(" mDestroyed="); writer.println(mDestroyed); 1119 if (mNeedMenuInvalidate) { 1120 writer.print(prefix); writer.print(" mNeedMenuInvalidate="); 1121 writer.println(mNeedMenuInvalidate); 1122 } 1123 if (mNoTransactionsBecause != null) { 1124 writer.print(prefix); writer.print(" mNoTransactionsBecause="); 1125 writer.println(mNoTransactionsBecause); 1126 } 1127 } 1128 1129 static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); 1130 static final Interpolator DECELERATE_CUBIC = new DecelerateInterpolator(1.5f); 1131 static final Interpolator ACCELERATE_QUINT = new AccelerateInterpolator(2.5f); 1132 static final Interpolator ACCELERATE_CUBIC = new AccelerateInterpolator(1.5f); 1133 1134 static final int ANIM_DUR = 220; 1135 makeOpenCloseAnimation(Context context, float startScale, float endScale, float startAlpha, float endAlpha)1136 static AnimationOrAnimator makeOpenCloseAnimation(Context context, float startScale, 1137 float endScale, float startAlpha, float endAlpha) { 1138 AnimationSet set = new AnimationSet(false); 1139 ScaleAnimation scale = new ScaleAnimation(startScale, endScale, startScale, endScale, 1140 Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f); 1141 scale.setInterpolator(DECELERATE_QUINT); 1142 scale.setDuration(ANIM_DUR); 1143 set.addAnimation(scale); 1144 AlphaAnimation alpha = new AlphaAnimation(startAlpha, endAlpha); 1145 alpha.setInterpolator(DECELERATE_CUBIC); 1146 alpha.setDuration(ANIM_DUR); 1147 set.addAnimation(alpha); 1148 return new AnimationOrAnimator(set); 1149 } 1150 makeFadeAnimation(Context context, float start, float end)1151 static AnimationOrAnimator makeFadeAnimation(Context context, float start, float end) { 1152 AlphaAnimation anim = new AlphaAnimation(start, end); 1153 anim.setInterpolator(DECELERATE_CUBIC); 1154 anim.setDuration(ANIM_DUR); 1155 return new AnimationOrAnimator(anim); 1156 } 1157 loadAnimation(Fragment fragment, int transit, boolean enter, int transitionStyle)1158 AnimationOrAnimator loadAnimation(Fragment fragment, int transit, boolean enter, 1159 int transitionStyle) { 1160 int nextAnim = fragment.getNextAnim(); 1161 Animation animation = fragment.onCreateAnimation(transit, enter, nextAnim); 1162 if (animation != null) { 1163 return new AnimationOrAnimator(animation); 1164 } 1165 1166 Animator animator = fragment.onCreateAnimator(transit, enter, nextAnim); 1167 if (animator != null) { 1168 return new AnimationOrAnimator(animator); 1169 } 1170 1171 if (nextAnim != 0) { 1172 String dir = mHost.getContext().getResources().getResourceTypeName(nextAnim); 1173 boolean isAnim = "anim".equals(dir); 1174 boolean successfulLoad = false; 1175 if (isAnim) { 1176 // try AnimationUtils first 1177 try { 1178 animation = AnimationUtils.loadAnimation(mHost.getContext(), nextAnim); 1179 if (animation != null) { 1180 return new AnimationOrAnimator(animation); 1181 } 1182 // A null animation may be returned and that is acceptable 1183 successfulLoad = true; // succeeded in loading animation, but it is null 1184 } catch (NotFoundException e) { 1185 throw e; // Rethrow it -- the resource should be found if it is provided. 1186 } catch (RuntimeException e) { 1187 // Other exceptions can occur when loading an Animator from AnimationUtils. 1188 } 1189 } 1190 if (!successfulLoad) { 1191 // try Animator 1192 try { 1193 animator = AnimatorInflater.loadAnimator(mHost.getContext(), nextAnim); 1194 if (animator != null) { 1195 return new AnimationOrAnimator(animator); 1196 } 1197 } catch (RuntimeException e) { 1198 if (isAnim) { 1199 // Rethrow it -- we already tried AnimationUtils and it failed. 1200 throw e; 1201 } 1202 // Otherwise, it is probably an animation resource 1203 animation = AnimationUtils.loadAnimation(mHost.getContext(), nextAnim); 1204 if (animation != null) { 1205 return new AnimationOrAnimator(animation); 1206 } 1207 } 1208 } 1209 } 1210 1211 if (transit == 0) { 1212 return null; 1213 } 1214 1215 int styleIndex = transitToStyleIndex(transit, enter); 1216 if (styleIndex < 0) { 1217 return null; 1218 } 1219 1220 switch (styleIndex) { 1221 case ANIM_STYLE_OPEN_ENTER: 1222 return makeOpenCloseAnimation(mHost.getContext(), 1.125f, 1.0f, 0, 1); 1223 case ANIM_STYLE_OPEN_EXIT: 1224 return makeOpenCloseAnimation(mHost.getContext(), 1.0f, .975f, 1, 0); 1225 case ANIM_STYLE_CLOSE_ENTER: 1226 return makeOpenCloseAnimation(mHost.getContext(), .975f, 1.0f, 0, 1); 1227 case ANIM_STYLE_CLOSE_EXIT: 1228 return makeOpenCloseAnimation(mHost.getContext(), 1.0f, 1.075f, 1, 0); 1229 case ANIM_STYLE_FADE_ENTER: 1230 return makeFadeAnimation(mHost.getContext(), 0, 1); 1231 case ANIM_STYLE_FADE_EXIT: 1232 return makeFadeAnimation(mHost.getContext(), 1, 0); 1233 } 1234 1235 // TODO: remove or fix transitionStyle -- it apparently never worked. 1236 if (transitionStyle == 0 && mHost.onHasWindowAnimations()) { 1237 transitionStyle = mHost.onGetWindowAnimations(); 1238 } 1239 if (transitionStyle == 0) { 1240 return null; 1241 } 1242 1243 //TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, 1244 // com.android.internal.R.styleable.FragmentAnimation); 1245 //int anim = attrs.getResourceId(styleIndex, 0); 1246 //attrs.recycle(); 1247 1248 //if (anim == 0) { 1249 // return null; 1250 //} 1251 1252 //return AnimatorInflater.loadAnimator(mActivity, anim); 1253 return null; 1254 } 1255 performPendingDeferredStart(Fragment f)1256 public void performPendingDeferredStart(Fragment f) { 1257 if (f.mDeferStart) { 1258 if (mExecutingActions) { 1259 // Wait until we're done executing our pending transactions 1260 mHavePendingDeferredStart = true; 1261 return; 1262 } 1263 f.mDeferStart = false; 1264 moveToState(f, mCurState, 0, 0, false); 1265 } 1266 } 1267 1268 /** 1269 * Sets the to be animated view on hardware layer during the animation. Note 1270 * that calling this will replace any existing animation listener on the animation 1271 * with a new one, as animations do not support more than one listeners. Therefore, 1272 * animations that already have listeners should do the layer change operations 1273 * in their existing listeners, rather than calling this function. 1274 */ setHWLayerAnimListenerIfAlpha(final View v, AnimationOrAnimator anim)1275 private static void setHWLayerAnimListenerIfAlpha(final View v, AnimationOrAnimator anim) { 1276 if (v == null || anim == null) { 1277 return; 1278 } 1279 if (shouldRunOnHWLayer(v, anim)) { 1280 if (anim.animator != null) { 1281 anim.animator.addListener(new AnimatorOnHWLayerIfNeededListener(v)); 1282 } else { 1283 AnimationListener originalListener = getAnimationListener(anim.animation); 1284 // If there's already a listener set on the animation, we need wrap the new listener 1285 // around the existing listener, so that they will both get animation listener 1286 // callbacks. 1287 v.setLayerType(View.LAYER_TYPE_HARDWARE, null); 1288 anim.animation.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, 1289 originalListener)); 1290 } 1291 } 1292 } 1293 1294 /** 1295 * Returns an existing AnimationListener on an Animation or {@code null} if none exists. 1296 */ getAnimationListener(Animation animation)1297 private static AnimationListener getAnimationListener(Animation animation) { 1298 AnimationListener originalListener = null; 1299 try { 1300 if (sAnimationListenerField == null) { 1301 sAnimationListenerField = Animation.class.getDeclaredField("mListener"); 1302 sAnimationListenerField.setAccessible(true); 1303 } 1304 originalListener = (AnimationListener) sAnimationListenerField.get(animation); 1305 } catch (NoSuchFieldException e) { 1306 Log.e(TAG, "No field with the name mListener is found in Animation class", e); 1307 } catch (IllegalAccessException e) { 1308 Log.e(TAG, "Cannot access Animation's mListener field", e); 1309 } 1310 return originalListener; 1311 } 1312 isStateAtLeast(int state)1313 boolean isStateAtLeast(int state) { 1314 return mCurState >= state; 1315 } 1316 1317 @SuppressWarnings("ReferenceEquality") moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive)1318 void moveToState(Fragment f, int newState, int transit, int transitionStyle, 1319 boolean keepActive) { 1320 // Fragments that are not currently added will sit in the onCreate() state. 1321 if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) { 1322 newState = Fragment.CREATED; 1323 } 1324 if (f.mRemoving && newState > f.mState) { 1325 if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) { 1326 // Allow the fragment to be created so that it can be saved later. 1327 newState = Fragment.CREATED; 1328 } else { 1329 // While removing a fragment, we can't change it to a higher state. 1330 newState = f.mState; 1331 } 1332 } 1333 // Defer start if requested; don't allow it to move to STARTED or higher 1334 // if it's not already started. 1335 if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.ACTIVITY_CREATED) { 1336 newState = Fragment.ACTIVITY_CREATED; 1337 } 1338 if (f.mState <= newState) { 1339 // For fragments that are created from a layout, when restoring from 1340 // state we don't want to allow them to be created until they are 1341 // being reloaded from the layout. 1342 if (f.mFromLayout && !f.mInLayout) { 1343 return; 1344 } 1345 if (f.getAnimatingAway() != null || f.getAnimator() != null) { 1346 // The fragment is currently being animated... but! Now we 1347 // want to move our state back up. Give up on waiting for the 1348 // animation, move to whatever the final state should be once 1349 // the animation is done, and then we can proceed from there. 1350 f.setAnimatingAway(null); 1351 f.setAnimator(null); 1352 moveToState(f, f.getStateAfterAnimating(), 0, 0, true); 1353 } 1354 switch (f.mState) { 1355 case Fragment.INITIALIZING: 1356 if (newState > Fragment.INITIALIZING) { 1357 if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); 1358 if (f.mSavedFragmentState != null) { 1359 f.mSavedFragmentState.setClassLoader(mHost.getContext() 1360 .getClassLoader()); 1361 f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( 1362 FragmentManagerImpl.VIEW_STATE_TAG); 1363 f.mTarget = getFragment(f.mSavedFragmentState, 1364 FragmentManagerImpl.TARGET_STATE_TAG); 1365 if (f.mTarget != null) { 1366 f.mTargetRequestCode = f.mSavedFragmentState.getInt( 1367 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); 1368 } 1369 if (f.mSavedUserVisibleHint != null) { 1370 f.mUserVisibleHint = f.mSavedUserVisibleHint; 1371 f.mSavedUserVisibleHint = null; 1372 } else { 1373 f.mUserVisibleHint = f.mSavedFragmentState.getBoolean( 1374 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true); 1375 } 1376 if (!f.mUserVisibleHint) { 1377 f.mDeferStart = true; 1378 if (newState > Fragment.ACTIVITY_CREATED) { 1379 newState = Fragment.ACTIVITY_CREATED; 1380 } 1381 } 1382 } 1383 1384 f.mHost = mHost; 1385 f.mParentFragment = mParent; 1386 f.mFragmentManager = mParent != null 1387 ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl(); 1388 1389 // If we have a target fragment, push it along to at least CREATED 1390 // so that this one can rely on it as an initialized dependency. 1391 if (f.mTarget != null) { 1392 if (mActive.get(f.mTarget.mIndex) != f.mTarget) { 1393 throw new IllegalStateException("Fragment " + f 1394 + " declared target fragment " + f.mTarget 1395 + " that does not belong to this FragmentManager!"); 1396 } 1397 if (f.mTarget.mState < Fragment.CREATED) { 1398 moveToState(f.mTarget, Fragment.CREATED, 0, 0, true); 1399 } 1400 } 1401 1402 dispatchOnFragmentPreAttached(f, mHost.getContext(), false); 1403 f.mCalled = false; 1404 f.onAttach(mHost.getContext()); 1405 if (!f.mCalled) { 1406 throw new SuperNotCalledException("Fragment " + f 1407 + " did not call through to super.onAttach()"); 1408 } 1409 if (f.mParentFragment == null) { 1410 mHost.onAttachFragment(f); 1411 } else { 1412 f.mParentFragment.onAttachFragment(f); 1413 } 1414 dispatchOnFragmentAttached(f, mHost.getContext(), false); 1415 1416 if (!f.mIsCreated) { 1417 dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false); 1418 f.performCreate(f.mSavedFragmentState); 1419 dispatchOnFragmentCreated(f, f.mSavedFragmentState, false); 1420 } else { 1421 f.restoreChildFragmentState(f.mSavedFragmentState); 1422 f.mState = Fragment.CREATED; 1423 } 1424 f.mRetaining = false; 1425 } 1426 // fall through 1427 case Fragment.CREATED: 1428 // This is outside the if statement below on purpose; we want this to run 1429 // even if we do a moveToState from CREATED => *, CREATED => CREATED, and 1430 // * => CREATED as part of the case fallthrough above. 1431 ensureInflatedFragmentView(f); 1432 1433 if (newState > Fragment.CREATED) { 1434 if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); 1435 if (!f.mFromLayout) { 1436 ViewGroup container = null; 1437 if (f.mContainerId != 0) { 1438 if (f.mContainerId == View.NO_ID) { 1439 throwException(new IllegalArgumentException( 1440 "Cannot create fragment " 1441 + f 1442 + " for a container view with no id")); 1443 } 1444 container = (ViewGroup) mContainer.onFindViewById(f.mContainerId); 1445 if (container == null && !f.mRestored) { 1446 String resName; 1447 try { 1448 resName = f.getResources().getResourceName(f.mContainerId); 1449 } catch (NotFoundException e) { 1450 resName = "unknown"; 1451 } 1452 throwException(new IllegalArgumentException( 1453 "No view found for id 0x" 1454 + Integer.toHexString(f.mContainerId) + " (" 1455 + resName 1456 + ") for fragment " + f)); 1457 } 1458 } 1459 f.mContainer = container; 1460 f.performCreateView(f.performGetLayoutInflater( 1461 f.mSavedFragmentState), container, f.mSavedFragmentState); 1462 if (f.mView != null) { 1463 f.mInnerView = f.mView; 1464 f.mView.setSaveFromParentEnabled(false); 1465 if (container != null) { 1466 container.addView(f.mView); 1467 } 1468 if (f.mHidden) { 1469 f.mView.setVisibility(View.GONE); 1470 } 1471 f.onViewCreated(f.mView, f.mSavedFragmentState); 1472 dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, 1473 false); 1474 // Only animate the view if it is visible. This is done after 1475 // dispatchOnFragmentViewCreated in case visibility is changed 1476 f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE) 1477 && f.mContainer != null; 1478 } else { 1479 f.mInnerView = null; 1480 } 1481 } 1482 1483 f.performActivityCreated(f.mSavedFragmentState); 1484 dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false); 1485 if (f.mView != null) { 1486 f.restoreViewState(f.mSavedFragmentState); 1487 } 1488 f.mSavedFragmentState = null; 1489 } 1490 // fall through 1491 case Fragment.ACTIVITY_CREATED: 1492 if (newState > Fragment.ACTIVITY_CREATED) { 1493 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 1494 f.performStart(); 1495 dispatchOnFragmentStarted(f, false); 1496 } 1497 // fall through 1498 case Fragment.STARTED: 1499 if (newState > Fragment.STARTED) { 1500 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 1501 f.performResume(); 1502 dispatchOnFragmentResumed(f, false); 1503 f.mSavedFragmentState = null; 1504 f.mSavedViewState = null; 1505 } 1506 } 1507 } else if (f.mState > newState) { 1508 switch (f.mState) { 1509 case Fragment.RESUMED: 1510 if (newState < Fragment.RESUMED) { 1511 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 1512 f.performPause(); 1513 dispatchOnFragmentPaused(f, false); 1514 } 1515 // fall through 1516 case Fragment.STARTED: 1517 if (newState < Fragment.STARTED) { 1518 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 1519 f.performStop(); 1520 dispatchOnFragmentStopped(f, false); 1521 } 1522 // fall through 1523 case Fragment.ACTIVITY_CREATED: 1524 if (newState < Fragment.ACTIVITY_CREATED) { 1525 if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f); 1526 if (f.mView != null) { 1527 // Need to save the current view state if not 1528 // done already. 1529 if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { 1530 saveFragmentViewState(f); 1531 } 1532 } 1533 f.performDestroyView(); 1534 dispatchOnFragmentViewDestroyed(f, false); 1535 if (f.mView != null && f.mContainer != null) { 1536 // Stop any current animations: 1537 f.mContainer.endViewTransition(f.mView); 1538 f.mView.clearAnimation(); 1539 AnimationOrAnimator anim = null; 1540 if (mCurState > Fragment.INITIALIZING && !mDestroyed 1541 && f.mView.getVisibility() == View.VISIBLE 1542 && f.mPostponedAlpha >= 0) { 1543 anim = loadAnimation(f, transit, false, 1544 transitionStyle); 1545 } 1546 f.mPostponedAlpha = 0; 1547 if (anim != null) { 1548 animateRemoveFragment(f, anim, newState); 1549 } 1550 f.mContainer.removeView(f.mView); 1551 } 1552 f.mContainer = null; 1553 f.mView = null; 1554 // Set here to ensure that Observers are called after 1555 // the Fragment's view is set to null 1556 f.mViewLifecycleOwner = null; 1557 f.mViewLifecycleOwnerLiveData.setValue(null); 1558 f.mInnerView = null; 1559 f.mInLayout = false; 1560 } 1561 // fall through 1562 case Fragment.CREATED: 1563 if (newState < Fragment.CREATED) { 1564 if (mDestroyed) { 1565 // The fragment's containing activity is 1566 // being destroyed, but this fragment is 1567 // currently animating away. Stop the 1568 // animation right now -- it is not needed, 1569 // and we can't wait any more on destroying 1570 // the fragment. 1571 if (f.getAnimatingAway() != null) { 1572 View v = f.getAnimatingAway(); 1573 f.setAnimatingAway(null); 1574 v.clearAnimation(); 1575 } else if (f.getAnimator() != null) { 1576 Animator animator = f.getAnimator(); 1577 f.setAnimator(null); 1578 animator.cancel(); 1579 } 1580 } 1581 if (f.getAnimatingAway() != null || f.getAnimator() != null) { 1582 // We are waiting for the fragment's view to finish 1583 // animating away. Just make a note of the state 1584 // the fragment now should move to once the animation 1585 // is done. 1586 f.setStateAfterAnimating(newState); 1587 newState = Fragment.CREATED; 1588 } else { 1589 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 1590 if (!f.mRetaining) { 1591 f.performDestroy(); 1592 dispatchOnFragmentDestroyed(f, false); 1593 } else { 1594 f.mState = Fragment.INITIALIZING; 1595 } 1596 1597 f.performDetach(); 1598 dispatchOnFragmentDetached(f, false); 1599 if (!keepActive) { 1600 if (!f.mRetaining) { 1601 makeInactive(f); 1602 } else { 1603 f.mHost = null; 1604 f.mParentFragment = null; 1605 f.mFragmentManager = null; 1606 } 1607 } 1608 } 1609 } 1610 } 1611 } 1612 1613 if (f.mState != newState) { 1614 Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; " 1615 + "expected state " + newState + " found " + f.mState); 1616 f.mState = newState; 1617 } 1618 } 1619 1620 /** 1621 * Animates the removal of a fragment with the given animator or animation. After animating, 1622 * the fragment's view will be removed from the hierarchy. 1623 * 1624 * @param fragment The fragment to animate out 1625 * @param anim The animator or animation to run on the fragment's view 1626 * @param newState The final state after animating. 1627 */ animateRemoveFragment(@onNull final Fragment fragment, @NonNull AnimationOrAnimator anim, final int newState)1628 private void animateRemoveFragment(@NonNull final Fragment fragment, 1629 @NonNull AnimationOrAnimator anim, final int newState) { 1630 final View viewToAnimate = fragment.mView; 1631 final ViewGroup container = fragment.mContainer; 1632 container.startViewTransition(viewToAnimate); 1633 fragment.setStateAfterAnimating(newState); 1634 if (anim.animation != null) { 1635 Animation animation = 1636 new EndViewTransitionAnimator(anim.animation, container, viewToAnimate); 1637 fragment.setAnimatingAway(fragment.mView); 1638 AnimationListener listener = getAnimationListener(animation); 1639 animation.setAnimationListener(new AnimationListenerWrapper(listener) { 1640 @Override 1641 public void onAnimationEnd(Animation animation) { 1642 super.onAnimationEnd(animation); 1643 1644 // onAnimationEnd() comes during draw(), so there can still be some 1645 // draw events happening after this call. We don't want to detach 1646 // the view until after the onAnimationEnd() 1647 container.post(new Runnable() { 1648 @Override 1649 public void run() { 1650 if (fragment.getAnimatingAway() != null) { 1651 fragment.setAnimatingAway(null); 1652 moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, 1653 false); 1654 } 1655 } 1656 }); 1657 } 1658 }); 1659 setHWLayerAnimListenerIfAlpha(viewToAnimate, anim); 1660 fragment.mView.startAnimation(animation); 1661 } else { 1662 Animator animator = anim.animator; 1663 fragment.setAnimator(anim.animator); 1664 animator.addListener(new AnimatorListenerAdapter() { 1665 @Override 1666 public void onAnimationEnd(Animator anim) { 1667 container.endViewTransition(viewToAnimate); 1668 // If an animator ends immediately, we can just pretend there is no animation. 1669 // When that happens the the fragment's view won't have been removed yet. 1670 Animator animator = fragment.getAnimator(); 1671 fragment.setAnimator(null); 1672 if (animator != null && container.indexOfChild(viewToAnimate) < 0) { 1673 moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false); 1674 } 1675 } 1676 }); 1677 animator.setTarget(fragment.mView); 1678 setHWLayerAnimListenerIfAlpha(fragment.mView, anim); 1679 animator.start(); 1680 } 1681 } 1682 moveToState(Fragment f)1683 void moveToState(Fragment f) { 1684 moveToState(f, mCurState, 0, 0, false); 1685 } 1686 ensureInflatedFragmentView(Fragment f)1687 void ensureInflatedFragmentView(Fragment f) { 1688 if (f.mFromLayout && !f.mPerformedCreateView) { 1689 f.performCreateView(f.performGetLayoutInflater( 1690 f.mSavedFragmentState), null, f.mSavedFragmentState); 1691 if (f.mView != null) { 1692 f.mInnerView = f.mView; 1693 f.mView.setSaveFromParentEnabled(false); 1694 if (f.mHidden) f.mView.setVisibility(View.GONE); 1695 f.onViewCreated(f.mView, f.mSavedFragmentState); 1696 dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false); 1697 } else { 1698 f.mInnerView = null; 1699 } 1700 } 1701 } 1702 1703 /** 1704 * Fragments that have been shown or hidden don't have their visibility changed or 1705 * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)} 1706 * calls. After fragments are brought to their final state in 1707 * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or 1708 * hidden must have their visibility changed and their animations started here. 1709 * 1710 * @param fragment The fragment with mHiddenChanged = true that should change its View's 1711 * visibility and start the show or hide animation. 1712 */ completeShowHideFragment(final Fragment fragment)1713 void completeShowHideFragment(final Fragment fragment) { 1714 if (fragment.mView != null) { 1715 AnimationOrAnimator anim = loadAnimation(fragment, fragment.getNextTransition(), 1716 !fragment.mHidden, fragment.getNextTransitionStyle()); 1717 if (anim != null && anim.animator != null) { 1718 anim.animator.setTarget(fragment.mView); 1719 if (fragment.mHidden) { 1720 if (fragment.isHideReplaced()) { 1721 fragment.setHideReplaced(false); 1722 } else { 1723 final ViewGroup container = fragment.mContainer; 1724 final View animatingView = fragment.mView; 1725 container.startViewTransition(animatingView); 1726 // Delay the actual hide operation until the animation finishes, 1727 // otherwise the fragment will just immediately disappear 1728 anim.animator.addListener(new AnimatorListenerAdapter() { 1729 @Override 1730 public void onAnimationEnd(Animator animation) { 1731 container.endViewTransition(animatingView); 1732 animation.removeListener(this); 1733 if (fragment.mView != null) { 1734 fragment.mView.setVisibility(View.GONE); 1735 } 1736 } 1737 }); 1738 } 1739 } else { 1740 fragment.mView.setVisibility(View.VISIBLE); 1741 } 1742 setHWLayerAnimListenerIfAlpha(fragment.mView, anim); 1743 anim.animator.start(); 1744 } else { 1745 if (anim != null) { 1746 setHWLayerAnimListenerIfAlpha(fragment.mView, anim); 1747 fragment.mView.startAnimation(anim.animation); 1748 anim.animation.start(); 1749 } 1750 final int visibility = fragment.mHidden && !fragment.isHideReplaced() 1751 ? View.GONE 1752 : View.VISIBLE; 1753 fragment.mView.setVisibility(visibility); 1754 if (fragment.isHideReplaced()) { 1755 fragment.setHideReplaced(false); 1756 } 1757 } 1758 } 1759 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1760 mNeedMenuInvalidate = true; 1761 } 1762 fragment.mHiddenChanged = false; 1763 fragment.onHiddenChanged(fragment.mHidden); 1764 } 1765 1766 /** 1767 * Moves a fragment to its expected final state or the fragment manager's state, depending 1768 * on whether the fragment manager's state is raised properly. 1769 * 1770 * @param f The fragment to change. 1771 */ moveFragmentToExpectedState(Fragment f)1772 void moveFragmentToExpectedState(Fragment f) { 1773 if (f == null) { 1774 return; 1775 } 1776 int nextState = mCurState; 1777 if (f.mRemoving) { 1778 if (f.isInBackStack()) { 1779 nextState = Math.min(nextState, Fragment.CREATED); 1780 } else { 1781 nextState = Math.min(nextState, Fragment.INITIALIZING); 1782 } 1783 } 1784 moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false); 1785 1786 if (f.mView != null) { 1787 // Move the view if it is out of order 1788 Fragment underFragment = findFragmentUnder(f); 1789 if (underFragment != null) { 1790 final View underView = underFragment.mView; 1791 // make sure this fragment is in the right order. 1792 final ViewGroup container = f.mContainer; 1793 int underIndex = container.indexOfChild(underView); 1794 int viewIndex = container.indexOfChild(f.mView); 1795 if (viewIndex < underIndex) { 1796 container.removeViewAt(viewIndex); 1797 container.addView(f.mView, underIndex); 1798 } 1799 } 1800 if (f.mIsNewlyAdded && f.mContainer != null) { 1801 // Make it visible and run the animations 1802 if (f.mPostponedAlpha > 0f) { 1803 f.mView.setAlpha(f.mPostponedAlpha); 1804 } 1805 f.mPostponedAlpha = 0f; 1806 f.mIsNewlyAdded = false; 1807 // run animations: 1808 AnimationOrAnimator anim = loadAnimation(f, f.getNextTransition(), true, 1809 f.getNextTransitionStyle()); 1810 if (anim != null) { 1811 setHWLayerAnimListenerIfAlpha(f.mView, anim); 1812 if (anim.animation != null) { 1813 f.mView.startAnimation(anim.animation); 1814 } else { 1815 anim.animator.setTarget(f.mView); 1816 anim.animator.start(); 1817 } 1818 } 1819 } 1820 } 1821 if (f.mHiddenChanged) { 1822 completeShowHideFragment(f); 1823 } 1824 } 1825 1826 /** 1827 * Changes the state of the fragment manager to {@code newState}. If the fragment manager 1828 * changes state or {@code always} is {@code true}, any fragments within it have their 1829 * states updated as well. 1830 * 1831 * @param newState The new state for the fragment manager 1832 * @param always If {@code true}, all fragments update their state, even 1833 * if {@code newState} matches the current fragment manager's state. 1834 */ moveToState(int newState, boolean always)1835 void moveToState(int newState, boolean always) { 1836 if (mHost == null && newState != Fragment.INITIALIZING) { 1837 throw new IllegalStateException("No activity"); 1838 } 1839 1840 if (!always && newState == mCurState) { 1841 return; 1842 } 1843 1844 mCurState = newState; 1845 1846 if (mActive != null) { 1847 1848 // Must add them in the proper order. mActive fragments may be out of order 1849 final int numAdded = mAdded.size(); 1850 for (int i = 0; i < numAdded; i++) { 1851 Fragment f = mAdded.get(i); 1852 moveFragmentToExpectedState(f); 1853 } 1854 1855 // Now iterate through all active fragments. These will include those that are removed 1856 // and detached. 1857 final int numActive = mActive.size(); 1858 for (int i = 0; i < numActive; i++) { 1859 Fragment f = mActive.valueAt(i); 1860 if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) { 1861 moveFragmentToExpectedState(f); 1862 } 1863 } 1864 1865 startPendingDeferredFragments(); 1866 1867 if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) { 1868 mHost.onSupportInvalidateOptionsMenu(); 1869 mNeedMenuInvalidate = false; 1870 } 1871 } 1872 } 1873 startPendingDeferredFragments()1874 void startPendingDeferredFragments() { 1875 if (mActive == null) return; 1876 1877 for (int i=0; i<mActive.size(); i++) { 1878 Fragment f = mActive.valueAt(i); 1879 if (f != null) { 1880 performPendingDeferredStart(f); 1881 } 1882 } 1883 } 1884 makeActive(Fragment f)1885 void makeActive(Fragment f) { 1886 if (f.mIndex >= 0) { 1887 return; 1888 } 1889 1890 f.setIndex(mNextFragmentIndex++, mParent); 1891 if (mActive == null) { 1892 mActive = new SparseArray<>(); 1893 } 1894 mActive.put(f.mIndex, f); 1895 if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); 1896 } 1897 makeInactive(Fragment f)1898 void makeInactive(Fragment f) { 1899 if (f.mIndex < 0) { 1900 return; 1901 } 1902 1903 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f); 1904 // Don't remove yet. That happens in burpActive(). This prevents 1905 // concurrent modification while iterating over mActive 1906 mActive.put(f.mIndex, null); 1907 1908 f.initState(); 1909 } 1910 addFragment(Fragment fragment, boolean moveToStateNow)1911 public void addFragment(Fragment fragment, boolean moveToStateNow) { 1912 if (DEBUG) Log.v(TAG, "add: " + fragment); 1913 makeActive(fragment); 1914 if (!fragment.mDetached) { 1915 if (mAdded.contains(fragment)) { 1916 throw new IllegalStateException("Fragment already added: " + fragment); 1917 } 1918 synchronized (mAdded) { 1919 mAdded.add(fragment); 1920 } 1921 fragment.mAdded = true; 1922 fragment.mRemoving = false; 1923 if (fragment.mView == null) { 1924 fragment.mHiddenChanged = false; 1925 } 1926 if (fragment.mHasMenu && fragment.mMenuVisible) { 1927 mNeedMenuInvalidate = true; 1928 } 1929 if (moveToStateNow) { 1930 moveToState(fragment); 1931 } 1932 } 1933 } 1934 removeFragment(Fragment fragment)1935 public void removeFragment(Fragment fragment) { 1936 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 1937 final boolean inactive = !fragment.isInBackStack(); 1938 if (!fragment.mDetached || inactive) { 1939 synchronized (mAdded) { 1940 mAdded.remove(fragment); 1941 } 1942 if (fragment.mHasMenu && fragment.mMenuVisible) { 1943 mNeedMenuInvalidate = true; 1944 } 1945 fragment.mAdded = false; 1946 fragment.mRemoving = true; 1947 } 1948 } 1949 1950 /** 1951 * Marks a fragment as hidden to be later animated in with 1952 * {@link #completeShowHideFragment(Fragment)}. 1953 * 1954 * @param fragment The fragment to be shown. 1955 */ hideFragment(Fragment fragment)1956 public void hideFragment(Fragment fragment) { 1957 if (DEBUG) Log.v(TAG, "hide: " + fragment); 1958 if (!fragment.mHidden) { 1959 fragment.mHidden = true; 1960 // Toggle hidden changed so that if a fragment goes through show/hide/show 1961 // it doesn't go through the animation. 1962 fragment.mHiddenChanged = !fragment.mHiddenChanged; 1963 } 1964 } 1965 1966 /** 1967 * Marks a fragment as shown to be later animated in with 1968 * {@link #completeShowHideFragment(Fragment)}. 1969 * 1970 * @param fragment The fragment to be shown. 1971 */ showFragment(Fragment fragment)1972 public void showFragment(Fragment fragment) { 1973 if (DEBUG) Log.v(TAG, "show: " + fragment); 1974 if (fragment.mHidden) { 1975 fragment.mHidden = false; 1976 // Toggle hidden changed so that if a fragment goes through show/hide/show 1977 // it doesn't go through the animation. 1978 fragment.mHiddenChanged = !fragment.mHiddenChanged; 1979 } 1980 } 1981 detachFragment(Fragment fragment)1982 public void detachFragment(Fragment fragment) { 1983 if (DEBUG) Log.v(TAG, "detach: " + fragment); 1984 if (!fragment.mDetached) { 1985 fragment.mDetached = true; 1986 if (fragment.mAdded) { 1987 // We are not already in back stack, so need to remove the fragment. 1988 if (DEBUG) Log.v(TAG, "remove from detach: " + fragment); 1989 synchronized (mAdded) { 1990 mAdded.remove(fragment); 1991 } 1992 if (fragment.mHasMenu && fragment.mMenuVisible) { 1993 mNeedMenuInvalidate = true; 1994 } 1995 fragment.mAdded = false; 1996 } 1997 } 1998 } 1999 attachFragment(Fragment fragment)2000 public void attachFragment(Fragment fragment) { 2001 if (DEBUG) Log.v(TAG, "attach: " + fragment); 2002 if (fragment.mDetached) { 2003 fragment.mDetached = false; 2004 if (!fragment.mAdded) { 2005 if (mAdded.contains(fragment)) { 2006 throw new IllegalStateException("Fragment already added: " + fragment); 2007 } 2008 if (DEBUG) Log.v(TAG, "add from attach: " + fragment); 2009 synchronized (mAdded) { 2010 mAdded.add(fragment); 2011 } 2012 fragment.mAdded = true; 2013 if (fragment.mHasMenu && fragment.mMenuVisible) { 2014 mNeedMenuInvalidate = true; 2015 } 2016 } 2017 } 2018 } 2019 2020 @Override 2021 @Nullable findFragmentById(int id)2022 public Fragment findFragmentById(int id) { 2023 // First look through added fragments. 2024 for (int i = mAdded.size() - 1; i >= 0; i--) { 2025 Fragment f = mAdded.get(i); 2026 if (f != null && f.mFragmentId == id) { 2027 return f; 2028 } 2029 } 2030 if (mActive != null) { 2031 // Now for any known fragment. 2032 for (int i=mActive.size()-1; i>=0; i--) { 2033 Fragment f = mActive.valueAt(i); 2034 if (f != null && f.mFragmentId == id) { 2035 return f; 2036 } 2037 } 2038 } 2039 return null; 2040 } 2041 2042 @Override 2043 @Nullable findFragmentByTag(@ullable String tag)2044 public Fragment findFragmentByTag(@Nullable String tag) { 2045 if (tag != null) { 2046 // First look through added fragments. 2047 for (int i=mAdded.size()-1; i>=0; i--) { 2048 Fragment f = mAdded.get(i); 2049 if (f != null && tag.equals(f.mTag)) { 2050 return f; 2051 } 2052 } 2053 } 2054 if (mActive != null && tag != null) { 2055 // Now for any known fragment. 2056 for (int i=mActive.size()-1; i>=0; i--) { 2057 Fragment f = mActive.valueAt(i); 2058 if (f != null && tag.equals(f.mTag)) { 2059 return f; 2060 } 2061 } 2062 } 2063 return null; 2064 } 2065 findFragmentByWho(String who)2066 public Fragment findFragmentByWho(String who) { 2067 if (mActive != null && who != null) { 2068 for (int i=mActive.size()-1; i>=0; i--) { 2069 Fragment f = mActive.valueAt(i); 2070 if (f != null && (f=f.findFragmentByWho(who)) != null) { 2071 return f; 2072 } 2073 } 2074 } 2075 return null; 2076 } 2077 checkStateLoss()2078 private void checkStateLoss() { 2079 if (isStateSaved()) { 2080 throw new IllegalStateException( 2081 "Can not perform this action after onSaveInstanceState"); 2082 } 2083 if (mNoTransactionsBecause != null) { 2084 throw new IllegalStateException( 2085 "Can not perform this action inside of " + mNoTransactionsBecause); 2086 } 2087 } 2088 2089 @Override isStateSaved()2090 public boolean isStateSaved() { 2091 // See saveAllState() for the explanation of this. We do this for 2092 // all platform versions, to keep our behavior more consistent between 2093 // them. 2094 return mStateSaved || mStopped; 2095 } 2096 2097 /** 2098 * Adds an action to the queue of pending actions. 2099 * 2100 * @param action the action to add 2101 * @param allowStateLoss whether to allow loss of state information 2102 * @throws IllegalStateException if the activity has been destroyed 2103 */ enqueueAction(OpGenerator action, boolean allowStateLoss)2104 public void enqueueAction(OpGenerator action, boolean allowStateLoss) { 2105 if (!allowStateLoss) { 2106 checkStateLoss(); 2107 } 2108 synchronized (this) { 2109 if (mDestroyed || mHost == null) { 2110 if (allowStateLoss) { 2111 // This FragmentManager isn't attached, so drop the entire transaction. 2112 return; 2113 } 2114 throw new IllegalStateException("Activity has been destroyed"); 2115 } 2116 if (mPendingActions == null) { 2117 mPendingActions = new ArrayList<>(); 2118 } 2119 mPendingActions.add(action); 2120 scheduleCommit(); 2121 } 2122 } 2123 2124 /** 2125 * Schedules the execution when one hasn't been scheduled already. This should happen 2126 * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when 2127 * a postponed transaction has been started with 2128 * {@link Fragment#startPostponedEnterTransition()} 2129 */ scheduleCommit()2130 private void scheduleCommit() { 2131 synchronized (this) { 2132 boolean postponeReady = 2133 mPostponedTransactions != null && !mPostponedTransactions.isEmpty(); 2134 boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1; 2135 if (postponeReady || pendingReady) { 2136 mHost.getHandler().removeCallbacks(mExecCommit); 2137 mHost.getHandler().post(mExecCommit); 2138 } 2139 } 2140 } 2141 allocBackStackIndex(BackStackRecord bse)2142 public int allocBackStackIndex(BackStackRecord bse) { 2143 synchronized (this) { 2144 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 2145 if (mBackStackIndices == null) { 2146 mBackStackIndices = new ArrayList<BackStackRecord>(); 2147 } 2148 int index = mBackStackIndices.size(); 2149 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 2150 mBackStackIndices.add(bse); 2151 return index; 2152 2153 } else { 2154 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 2155 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 2156 mBackStackIndices.set(index, bse); 2157 return index; 2158 } 2159 } 2160 } 2161 setBackStackIndex(int index, BackStackRecord bse)2162 public void setBackStackIndex(int index, BackStackRecord bse) { 2163 synchronized (this) { 2164 if (mBackStackIndices == null) { 2165 mBackStackIndices = new ArrayList<BackStackRecord>(); 2166 } 2167 int N = mBackStackIndices.size(); 2168 if (index < N) { 2169 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 2170 mBackStackIndices.set(index, bse); 2171 } else { 2172 while (N < index) { 2173 mBackStackIndices.add(null); 2174 if (mAvailBackStackIndices == null) { 2175 mAvailBackStackIndices = new ArrayList<Integer>(); 2176 } 2177 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 2178 mAvailBackStackIndices.add(N); 2179 N++; 2180 } 2181 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 2182 mBackStackIndices.add(bse); 2183 } 2184 } 2185 } 2186 freeBackStackIndex(int index)2187 public void freeBackStackIndex(int index) { 2188 synchronized (this) { 2189 mBackStackIndices.set(index, null); 2190 if (mAvailBackStackIndices == null) { 2191 mAvailBackStackIndices = new ArrayList<Integer>(); 2192 } 2193 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 2194 mAvailBackStackIndices.add(index); 2195 } 2196 } 2197 2198 /** 2199 * Broken out from exec*, this prepares for gathering and executing operations. 2200 * 2201 * @param allowStateLoss true if state loss should be ignored or false if it should be 2202 * checked. 2203 */ ensureExecReady(boolean allowStateLoss)2204 private void ensureExecReady(boolean allowStateLoss) { 2205 if (mExecutingActions) { 2206 throw new IllegalStateException("FragmentManager is already executing transactions"); 2207 } 2208 2209 if (mHost == null) { 2210 throw new IllegalStateException("Fragment host has been destroyed"); 2211 } 2212 2213 if (Looper.myLooper() != mHost.getHandler().getLooper()) { 2214 throw new IllegalStateException("Must be called from main thread of fragment host"); 2215 } 2216 2217 if (!allowStateLoss) { 2218 checkStateLoss(); 2219 } 2220 2221 if (mTmpRecords == null) { 2222 mTmpRecords = new ArrayList<>(); 2223 mTmpIsPop = new ArrayList<>(); 2224 } 2225 mExecutingActions = true; 2226 try { 2227 executePostponedTransaction(null, null); 2228 } finally { 2229 mExecutingActions = false; 2230 } 2231 } 2232 execSingleAction(OpGenerator action, boolean allowStateLoss)2233 public void execSingleAction(OpGenerator action, boolean allowStateLoss) { 2234 if (allowStateLoss && (mHost == null || mDestroyed)) { 2235 // This FragmentManager isn't attached, so drop the entire transaction. 2236 return; 2237 } 2238 ensureExecReady(allowStateLoss); 2239 if (action.generateOps(mTmpRecords, mTmpIsPop)) { 2240 mExecutingActions = true; 2241 try { 2242 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); 2243 } finally { 2244 cleanupExec(); 2245 } 2246 } 2247 2248 doPendingDeferredStart(); 2249 burpActive(); 2250 } 2251 2252 /** 2253 * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures 2254 * used in executing operations. 2255 */ cleanupExec()2256 private void cleanupExec() { 2257 mExecutingActions = false; 2258 mTmpIsPop.clear(); 2259 mTmpRecords.clear(); 2260 } 2261 2262 /** 2263 * Only call from main thread! 2264 */ execPendingActions()2265 public boolean execPendingActions() { 2266 ensureExecReady(true); 2267 2268 boolean didSomething = false; 2269 while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) { 2270 mExecutingActions = true; 2271 try { 2272 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); 2273 } finally { 2274 cleanupExec(); 2275 } 2276 didSomething = true; 2277 } 2278 2279 doPendingDeferredStart(); 2280 burpActive(); 2281 2282 return didSomething; 2283 } 2284 2285 /** 2286 * Complete the execution of transactions that have previously been postponed, but are 2287 * now ready. 2288 */ executePostponedTransaction(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop)2289 private void executePostponedTransaction(ArrayList<BackStackRecord> records, 2290 ArrayList<Boolean> isRecordPop) { 2291 int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size(); 2292 for (int i = 0; i < numPostponed; i++) { 2293 StartEnterTransitionListener listener = mPostponedTransactions.get(i); 2294 if (records != null && !listener.mIsBack) { 2295 int index = records.indexOf(listener.mRecord); 2296 if (index != -1 && isRecordPop.get(index)) { 2297 listener.cancelTransaction(); 2298 continue; 2299 } 2300 } 2301 if (listener.isReady() || (records != null 2302 && listener.mRecord.interactsWith(records, 0, records.size()))) { 2303 mPostponedTransactions.remove(i); 2304 i--; 2305 numPostponed--; 2306 int index; 2307 if (records != null && !listener.mIsBack 2308 && (index = records.indexOf(listener.mRecord)) != -1 2309 && isRecordPop.get(index)) { 2310 // This is popping a postponed transaction 2311 listener.cancelTransaction(); 2312 } else { 2313 listener.completeTransaction(); 2314 } 2315 } 2316 } 2317 } 2318 2319 /** 2320 * Remove redundant BackStackRecord operations and executes them. This method merges operations 2321 * of proximate records that allow reordering. See 2322 * {@link FragmentTransaction#setReorderingAllowed(boolean)}. 2323 * <p> 2324 * For example, a transaction that adds to the back stack and then another that pops that 2325 * back stack record will be optimized to remove the unnecessary operation. 2326 * <p> 2327 * Likewise, two transactions committed that are executed at the same time will be optimized 2328 * to remove the redundant operations as well as two pop operations executed together. 2329 * 2330 * @param records The records pending execution 2331 * @param isRecordPop The direction that these records are being run. 2332 */ removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop)2333 private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, 2334 ArrayList<Boolean> isRecordPop) { 2335 if (records == null || records.isEmpty()) { 2336 return; 2337 } 2338 2339 if (isRecordPop == null || records.size() != isRecordPop.size()) { 2340 throw new IllegalStateException("Internal error with the back stack records"); 2341 } 2342 2343 // Force start of any postponed transactions that interact with scheduled transactions: 2344 executePostponedTransaction(records, isRecordPop); 2345 2346 final int numRecords = records.size(); 2347 int startIndex = 0; 2348 for (int recordNum = 0; recordNum < numRecords; recordNum++) { 2349 final boolean canReorder = records.get(recordNum).mReorderingAllowed; 2350 if (!canReorder) { 2351 // execute all previous transactions 2352 if (startIndex != recordNum) { 2353 executeOpsTogether(records, isRecordPop, startIndex, recordNum); 2354 } 2355 // execute all pop operations that don't allow reordering together or 2356 // one add operation 2357 int reorderingEnd = recordNum + 1; 2358 if (isRecordPop.get(recordNum)) { 2359 while (reorderingEnd < numRecords 2360 && isRecordPop.get(reorderingEnd) 2361 && !records.get(reorderingEnd).mReorderingAllowed) { 2362 reorderingEnd++; 2363 } 2364 } 2365 executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd); 2366 startIndex = reorderingEnd; 2367 recordNum = reorderingEnd - 1; 2368 } 2369 } 2370 if (startIndex != numRecords) { 2371 executeOpsTogether(records, isRecordPop, startIndex, numRecords); 2372 } 2373 } 2374 2375 /** 2376 * Executes a subset of a list of BackStackRecords, all of which either allow reordering or 2377 * do not allow ordering. 2378 * @param records A list of BackStackRecords that are to be executed 2379 * @param isRecordPop The direction that these records are being run. 2380 * @param startIndex The index of the first record in <code>records</code> to be executed 2381 * @param endIndex One more than the final record index in <code>records</code> to executed. 2382 */ executeOpsTogether(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex)2383 private void executeOpsTogether(ArrayList<BackStackRecord> records, 2384 ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) { 2385 final boolean allowReordering = records.get(startIndex).mReorderingAllowed; 2386 boolean addToBackStack = false; 2387 if (mTmpAddedFragments == null) { 2388 mTmpAddedFragments = new ArrayList<>(); 2389 } else { 2390 mTmpAddedFragments.clear(); 2391 } 2392 mTmpAddedFragments.addAll(mAdded); 2393 Fragment oldPrimaryNav = getPrimaryNavigationFragment(); 2394 for (int recordNum = startIndex; recordNum < endIndex; recordNum++) { 2395 final BackStackRecord record = records.get(recordNum); 2396 final boolean isPop = isRecordPop.get(recordNum); 2397 if (!isPop) { 2398 oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav); 2399 } else { 2400 oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav); 2401 } 2402 addToBackStack = addToBackStack || record.mAddToBackStack; 2403 } 2404 mTmpAddedFragments.clear(); 2405 2406 if (!allowReordering) { 2407 FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex, 2408 false); 2409 } 2410 executeOps(records, isRecordPop, startIndex, endIndex); 2411 2412 int postponeIndex = endIndex; 2413 if (allowReordering) { 2414 ArraySet<Fragment> addedFragments = new ArraySet<>(); 2415 addAddedFragments(addedFragments); 2416 postponeIndex = postponePostponableTransactions(records, isRecordPop, 2417 startIndex, endIndex, addedFragments); 2418 makeRemovedFragmentsInvisible(addedFragments); 2419 } 2420 2421 if (postponeIndex != startIndex && allowReordering) { 2422 // need to run something now 2423 FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, 2424 postponeIndex, true); 2425 moveToState(mCurState, true); 2426 } 2427 2428 for (int recordNum = startIndex; recordNum < endIndex; recordNum++) { 2429 final BackStackRecord record = records.get(recordNum); 2430 final boolean isPop = isRecordPop.get(recordNum); 2431 if (isPop && record.mIndex >= 0) { 2432 freeBackStackIndex(record.mIndex); 2433 record.mIndex = -1; 2434 } 2435 record.runOnCommitRunnables(); 2436 } 2437 if (addToBackStack) { 2438 reportBackStackChanged(); 2439 } 2440 } 2441 2442 /** 2443 * Any fragments that were removed because they have been postponed should have their views 2444 * made invisible by setting their alpha to 0. 2445 * 2446 * @param fragments The fragments that were added during operation execution. Only the ones 2447 * that are no longer added will have their alpha changed. 2448 */ makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments)2449 private void makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments) { 2450 final int numAdded = fragments.size(); 2451 for (int i = 0; i < numAdded; i++) { 2452 final Fragment fragment = fragments.valueAt(i); 2453 if (!fragment.mAdded) { 2454 final View view = fragment.getView(); 2455 fragment.mPostponedAlpha = view.getAlpha(); 2456 view.setAlpha(0f); 2457 } 2458 } 2459 } 2460 2461 /** 2462 * Examine all transactions and determine which ones are marked as postponed. Those will 2463 * have their operations rolled back and moved to the end of the record list (up to endIndex). 2464 * It will also add the postponed transaction to the queue. 2465 * 2466 * @param records A list of BackStackRecords that should be checked. 2467 * @param isRecordPop The direction that these records are being run. 2468 * @param startIndex The index of the first record in <code>records</code> to be checked 2469 * @param endIndex One more than the final record index in <code>records</code> to be checked. 2470 * @return The index of the first postponed transaction or endIndex if no transaction was 2471 * postponed. 2472 */ postponePostponableTransactions(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex, ArraySet<Fragment> added)2473 private int postponePostponableTransactions(ArrayList<BackStackRecord> records, 2474 ArrayList<Boolean> isRecordPop, int startIndex, int endIndex, 2475 ArraySet<Fragment> added) { 2476 int postponeIndex = endIndex; 2477 for (int i = endIndex - 1; i >= startIndex; i--) { 2478 final BackStackRecord record = records.get(i); 2479 final boolean isPop = isRecordPop.get(i); 2480 boolean isPostponed = record.isPostponed() 2481 && !record.interactsWith(records, i + 1, endIndex); 2482 if (isPostponed) { 2483 if (mPostponedTransactions == null) { 2484 mPostponedTransactions = new ArrayList<>(); 2485 } 2486 StartEnterTransitionListener listener = 2487 new StartEnterTransitionListener(record, isPop); 2488 mPostponedTransactions.add(listener); 2489 record.setOnStartPostponedListener(listener); 2490 2491 // roll back the transaction 2492 if (isPop) { 2493 record.executeOps(); 2494 } else { 2495 record.executePopOps(false); 2496 } 2497 2498 // move to the end 2499 postponeIndex--; 2500 if (i != postponeIndex) { 2501 records.remove(i); 2502 records.add(postponeIndex, record); 2503 } 2504 2505 // different views may be visible now 2506 addAddedFragments(added); 2507 } 2508 } 2509 return postponeIndex; 2510 } 2511 2512 /** 2513 * When a postponed transaction is ready to be started, this completes the transaction, 2514 * removing, hiding, or showing views as well as starting the animations and transitions. 2515 * <p> 2516 * {@code runtransitions} is set to false when the transaction postponement was interrupted 2517 * abnormally -- normally by a new transaction being started that affects the postponed 2518 * transaction. 2519 * 2520 * @param record The transaction to run 2521 * @param isPop true if record is popping or false if it is adding 2522 * @param runTransitions true if the fragment transition should be run or false otherwise. 2523 * @param moveToState true if the state should be changed after executing the operations. 2524 * This is false when the transaction is canceled when a postponed 2525 * transaction is popped. 2526 */ completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions, boolean moveToState)2527 private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions, 2528 boolean moveToState) { 2529 if (isPop) { 2530 record.executePopOps(moveToState); 2531 } else { 2532 record.executeOps(); 2533 } 2534 ArrayList<BackStackRecord> records = new ArrayList<>(1); 2535 ArrayList<Boolean> isRecordPop = new ArrayList<>(1); 2536 records.add(record); 2537 isRecordPop.add(isPop); 2538 if (runTransitions) { 2539 FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true); 2540 } 2541 if (moveToState) { 2542 moveToState(mCurState, true); 2543 } 2544 2545 if (mActive != null) { 2546 final int numActive = mActive.size(); 2547 for (int i = 0; i < numActive; i++) { 2548 // Allow added fragments to be removed during the pop since we aren't going 2549 // to move them to the final state with moveToState(mCurState). 2550 Fragment fragment = mActive.valueAt(i); 2551 if (fragment != null && fragment.mView != null && fragment.mIsNewlyAdded 2552 && record.interactsWith(fragment.mContainerId)) { 2553 if (fragment.mPostponedAlpha > 0) { 2554 fragment.mView.setAlpha(fragment.mPostponedAlpha); 2555 } 2556 if (moveToState) { 2557 fragment.mPostponedAlpha = 0; 2558 } else { 2559 fragment.mPostponedAlpha = -1; 2560 fragment.mIsNewlyAdded = false; 2561 } 2562 } 2563 } 2564 } 2565 } 2566 2567 /** 2568 * Find a fragment within the fragment's container whose View should be below the passed 2569 * fragment. {@code null} is returned when the fragment has no View or if there should be 2570 * no fragment with a View below the given fragment. 2571 * 2572 * As an example, if mAdded has two Fragments with Views sharing the same container: 2573 * FragmentA 2574 * FragmentB 2575 * 2576 * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA 2577 * had no View, null would be returned. 2578 * 2579 * @param f The fragment that may be on top of another fragment. 2580 * @return The fragment with a View under f, if one exists or null if f has no View or 2581 * there are no fragments with Views in the same container. 2582 */ findFragmentUnder(Fragment f)2583 private Fragment findFragmentUnder(Fragment f) { 2584 final ViewGroup container = f.mContainer; 2585 final View view = f.mView; 2586 2587 if (container == null || view == null) { 2588 return null; 2589 } 2590 2591 final int fragmentIndex = mAdded.indexOf(f); 2592 for (int i = fragmentIndex - 1; i >= 0; i--) { 2593 Fragment underFragment = mAdded.get(i); 2594 if (underFragment.mContainer == container && underFragment.mView != null) { 2595 // Found the fragment under this one 2596 return underFragment; 2597 } 2598 } 2599 return null; 2600 } 2601 2602 /** 2603 * Run the operations in the BackStackRecords, either to push or pop. 2604 * 2605 * @param records The list of records whose operations should be run. 2606 * @param isRecordPop The direction that these records are being run. 2607 * @param startIndex The index of the first entry in records to run. 2608 * @param endIndex One past the index of the final entry in records to run. 2609 */ executeOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex)2610 private static void executeOps(ArrayList<BackStackRecord> records, 2611 ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) { 2612 for (int i = startIndex; i < endIndex; i++) { 2613 final BackStackRecord record = records.get(i); 2614 final boolean isPop = isRecordPop.get(i); 2615 if (isPop) { 2616 record.bumpBackStackNesting(-1); 2617 // Only execute the add operations at the end of 2618 // all transactions. 2619 boolean moveToState = i == (endIndex - 1); 2620 record.executePopOps(moveToState); 2621 } else { 2622 record.bumpBackStackNesting(1); 2623 record.executeOps(); 2624 } 2625 } 2626 } 2627 2628 /** 2629 * Ensure that fragments that are added are moved to at least the CREATED state. 2630 * Any newly-added Views are inserted into {@code added} so that the Transaction can be 2631 * postponed with {@link Fragment#postponeEnterTransition()}. They will later be made 2632 * invisible (by setting their alpha to 0) if they have been removed when postponed. 2633 */ addAddedFragments(ArraySet<Fragment> added)2634 private void addAddedFragments(ArraySet<Fragment> added) { 2635 if (mCurState < Fragment.CREATED) { 2636 return; 2637 } 2638 // We want to leave the fragment in the started state 2639 final int state = Math.min(mCurState, Fragment.STARTED); 2640 final int numAdded = mAdded.size(); 2641 for (int i = 0; i < numAdded; i++) { 2642 Fragment fragment = mAdded.get(i); 2643 if (fragment.mState < state) { 2644 moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(), 2645 false); 2646 if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) { 2647 added.add(fragment); 2648 } 2649 } 2650 } 2651 } 2652 2653 /** 2654 * Starts all postponed transactions regardless of whether they are ready or not. 2655 */ forcePostponedTransactions()2656 private void forcePostponedTransactions() { 2657 if (mPostponedTransactions != null) { 2658 while (!mPostponedTransactions.isEmpty()) { 2659 mPostponedTransactions.remove(0).completeTransaction(); 2660 } 2661 } 2662 } 2663 2664 /** 2665 * Ends the animations of fragments so that they immediately reach the end state. 2666 * This is used prior to saving the state so that the correct state is saved. 2667 */ endAnimatingAwayFragments()2668 private void endAnimatingAwayFragments() { 2669 final int numFragments = mActive == null ? 0 : mActive.size(); 2670 for (int i = 0; i < numFragments; i++) { 2671 Fragment fragment = mActive.valueAt(i); 2672 if (fragment != null) { 2673 if (fragment.getAnimatingAway() != null) { 2674 // Give up waiting for the animation and just end it. 2675 final int stateAfterAnimating = fragment.getStateAfterAnimating(); 2676 final View animatingAway = fragment.getAnimatingAway(); 2677 Animation animation = animatingAway.getAnimation(); 2678 if (animation != null) { 2679 animation.cancel(); 2680 // force-clear the animation, as Animation#cancel() doesn't work prior to N, 2681 // and will instead cause the animation to infinitely loop 2682 animatingAway.clearAnimation(); 2683 } 2684 fragment.setAnimatingAway(null); 2685 moveToState(fragment, stateAfterAnimating, 0, 0, false); 2686 } else if (fragment.getAnimator() != null) { 2687 fragment.getAnimator().end(); 2688 } 2689 } 2690 } 2691 } 2692 2693 /** 2694 * Adds all records in the pending actions to records and whether they are add or pop 2695 * operations to isPop. After executing, the pending actions will be empty. 2696 * 2697 * @param records All pending actions will generate BackStackRecords added to this. 2698 * This contains the transactions, in order, to execute. 2699 * @param isPop All pending actions will generate booleans to add to this. This contains 2700 * an entry for each entry in records to indicate whether or not it is a 2701 * pop action. 2702 */ generateOpsForPendingActions(ArrayList<BackStackRecord> records, ArrayList<Boolean> isPop)2703 private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records, 2704 ArrayList<Boolean> isPop) { 2705 boolean didSomething = false; 2706 synchronized (this) { 2707 if (mPendingActions == null || mPendingActions.size() == 0) { 2708 return false; 2709 } 2710 2711 final int numActions = mPendingActions.size(); 2712 for (int i = 0; i < numActions; i++) { 2713 didSomething |= mPendingActions.get(i).generateOps(records, isPop); 2714 } 2715 mPendingActions.clear(); 2716 mHost.getHandler().removeCallbacks(mExecCommit); 2717 } 2718 return didSomething; 2719 } 2720 doPendingDeferredStart()2721 void doPendingDeferredStart() { 2722 if (mHavePendingDeferredStart) { 2723 mHavePendingDeferredStart = false; 2724 startPendingDeferredFragments(); 2725 } 2726 } 2727 reportBackStackChanged()2728 void reportBackStackChanged() { 2729 if (mBackStackChangeListeners != null) { 2730 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 2731 mBackStackChangeListeners.get(i).onBackStackChanged(); 2732 } 2733 } 2734 } 2735 addBackStackState(BackStackRecord state)2736 void addBackStackState(BackStackRecord state) { 2737 if (mBackStack == null) { 2738 mBackStack = new ArrayList<BackStackRecord>(); 2739 } 2740 mBackStack.add(state); 2741 } 2742 2743 @SuppressWarnings("unused") popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, String name, int id, int flags)2744 boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, 2745 String name, int id, int flags) { 2746 if (mBackStack == null) { 2747 return false; 2748 } 2749 if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) { 2750 int last = mBackStack.size() - 1; 2751 if (last < 0) { 2752 return false; 2753 } 2754 records.add(mBackStack.remove(last)); 2755 isRecordPop.add(true); 2756 } else { 2757 int index = -1; 2758 if (name != null || id >= 0) { 2759 // If a name or ID is specified, look for that place in 2760 // the stack. 2761 index = mBackStack.size()-1; 2762 while (index >= 0) { 2763 BackStackRecord bss = mBackStack.get(index); 2764 if (name != null && name.equals(bss.getName())) { 2765 break; 2766 } 2767 if (id >= 0 && id == bss.mIndex) { 2768 break; 2769 } 2770 index--; 2771 } 2772 if (index < 0) { 2773 return false; 2774 } 2775 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { 2776 index--; 2777 // Consume all following entries that match. 2778 while (index >= 0) { 2779 BackStackRecord bss = mBackStack.get(index); 2780 if ((name != null && name.equals(bss.getName())) 2781 || (id >= 0 && id == bss.mIndex)) { 2782 index--; 2783 continue; 2784 } 2785 break; 2786 } 2787 } 2788 } 2789 if (index == mBackStack.size()-1) { 2790 return false; 2791 } 2792 for (int i = mBackStack.size() - 1; i > index; i--) { 2793 records.add(mBackStack.remove(i)); 2794 isRecordPop.add(true); 2795 } 2796 } 2797 return true; 2798 } 2799 retainNonConfig()2800 FragmentManagerNonConfig retainNonConfig() { 2801 setRetaining(mSavedNonConfig); 2802 return mSavedNonConfig; 2803 } 2804 2805 /** 2806 * Recurse the FragmentManagerNonConfig fragments and set the mRetaining to true. This 2807 * was previously done while saving the non-config state, but that has been moved to 2808 * {@link #saveNonConfig()} called from {@link #saveAllState()}. If mRetaining is set too 2809 * early, the fragment won't be destroyed when the FragmentManager is destroyed. 2810 */ setRetaining(FragmentManagerNonConfig nonConfig)2811 private static void setRetaining(FragmentManagerNonConfig nonConfig) { 2812 if (nonConfig == null) { 2813 return; 2814 } 2815 List<Fragment> fragments = nonConfig.getFragments(); 2816 if (fragments != null) { 2817 for (Fragment fragment : fragments) { 2818 fragment.mRetaining = true; 2819 } 2820 } 2821 List<FragmentManagerNonConfig> children = nonConfig.getChildNonConfigs(); 2822 if (children != null) { 2823 for (FragmentManagerNonConfig child : children) { 2824 setRetaining(child); 2825 } 2826 } 2827 } 2828 saveNonConfig()2829 void saveNonConfig() { 2830 ArrayList<Fragment> fragments = null; 2831 ArrayList<FragmentManagerNonConfig> childFragments = null; 2832 ArrayList<ViewModelStore> viewModelStores = null; 2833 if (mActive != null) { 2834 for (int i=0; i<mActive.size(); i++) { 2835 Fragment f = mActive.valueAt(i); 2836 if (f != null) { 2837 if (f.mRetainInstance) { 2838 if (fragments == null) { 2839 fragments = new ArrayList<Fragment>(); 2840 } 2841 fragments.add(f); 2842 f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; 2843 if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); 2844 } 2845 FragmentManagerNonConfig child; 2846 if (f.mChildFragmentManager != null) { 2847 f.mChildFragmentManager.saveNonConfig(); 2848 child = f.mChildFragmentManager.mSavedNonConfig; 2849 } else { 2850 // f.mChildNonConfig may be not null, when the parent fragment is 2851 // in the backstack. 2852 child = f.mChildNonConfig; 2853 } 2854 2855 if (childFragments == null && child != null) { 2856 childFragments = new ArrayList<>(mActive.size()); 2857 for (int j = 0; j < i; j++) { 2858 childFragments.add(null); 2859 } 2860 } 2861 2862 if (childFragments != null) { 2863 childFragments.add(child); 2864 } 2865 if (viewModelStores == null && f.mViewModelStore != null) { 2866 viewModelStores = new ArrayList<>(mActive.size()); 2867 for (int j = 0; j < i; j++) { 2868 viewModelStores.add(null); 2869 } 2870 } 2871 2872 if (viewModelStores != null) { 2873 viewModelStores.add(f.mViewModelStore); 2874 } 2875 } 2876 } 2877 } 2878 if (fragments == null && childFragments == null && viewModelStores == null) { 2879 mSavedNonConfig = null; 2880 } else { 2881 mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments, 2882 viewModelStores); 2883 } 2884 } 2885 saveFragmentViewState(Fragment f)2886 void saveFragmentViewState(Fragment f) { 2887 if (f.mInnerView == null) { 2888 return; 2889 } 2890 if (mStateArray == null) { 2891 mStateArray = new SparseArray<Parcelable>(); 2892 } else { 2893 mStateArray.clear(); 2894 } 2895 f.mInnerView.saveHierarchyState(mStateArray); 2896 if (mStateArray.size() > 0) { 2897 f.mSavedViewState = mStateArray; 2898 mStateArray = null; 2899 } 2900 } 2901 saveFragmentBasicState(Fragment f)2902 Bundle saveFragmentBasicState(Fragment f) { 2903 Bundle result = null; 2904 2905 if (mStateBundle == null) { 2906 mStateBundle = new Bundle(); 2907 } 2908 f.performSaveInstanceState(mStateBundle); 2909 dispatchOnFragmentSaveInstanceState(f, mStateBundle, false); 2910 if (!mStateBundle.isEmpty()) { 2911 result = mStateBundle; 2912 mStateBundle = null; 2913 } 2914 2915 if (f.mView != null) { 2916 saveFragmentViewState(f); 2917 } 2918 if (f.mSavedViewState != null) { 2919 if (result == null) { 2920 result = new Bundle(); 2921 } 2922 result.putSparseParcelableArray( 2923 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 2924 } 2925 if (!f.mUserVisibleHint) { 2926 if (result == null) { 2927 result = new Bundle(); 2928 } 2929 // Only add this if it's not the default value 2930 result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint); 2931 } 2932 2933 return result; 2934 } 2935 saveAllState()2936 Parcelable saveAllState() { 2937 // Make sure all pending operations have now been executed to get 2938 // our state update-to-date. 2939 forcePostponedTransactions(); 2940 endAnimatingAwayFragments(); 2941 execPendingActions(); 2942 2943 mStateSaved = true; 2944 mSavedNonConfig = null; 2945 2946 if (mActive == null || mActive.size() <= 0) { 2947 return null; 2948 } 2949 2950 // First collect all active fragments. 2951 int N = mActive.size(); 2952 FragmentState[] active = new FragmentState[N]; 2953 boolean haveFragments = false; 2954 for (int i=0; i<N; i++) { 2955 Fragment f = mActive.valueAt(i); 2956 if (f != null) { 2957 if (f.mIndex < 0) { 2958 throwException(new IllegalStateException( 2959 "Failure saving state: active " + f 2960 + " has cleared index: " + f.mIndex)); 2961 } 2962 2963 haveFragments = true; 2964 2965 FragmentState fs = new FragmentState(f); 2966 active[i] = fs; 2967 2968 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 2969 fs.mSavedFragmentState = saveFragmentBasicState(f); 2970 2971 if (f.mTarget != null) { 2972 if (f.mTarget.mIndex < 0) { 2973 throwException(new IllegalStateException( 2974 "Failure saving state: " + f 2975 + " has target not in fragment manager: " + f.mTarget)); 2976 } 2977 if (fs.mSavedFragmentState == null) { 2978 fs.mSavedFragmentState = new Bundle(); 2979 } 2980 putFragment(fs.mSavedFragmentState, 2981 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 2982 if (f.mTargetRequestCode != 0) { 2983 fs.mSavedFragmentState.putInt( 2984 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 2985 f.mTargetRequestCode); 2986 } 2987 } 2988 2989 } else { 2990 fs.mSavedFragmentState = f.mSavedFragmentState; 2991 } 2992 2993 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 2994 + fs.mSavedFragmentState); 2995 } 2996 } 2997 2998 if (!haveFragments) { 2999 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 3000 return null; 3001 } 3002 3003 int[] added = null; 3004 BackStackState[] backStack = null; 3005 3006 // Build list of currently added fragments. 3007 N = mAdded.size(); 3008 if (N > 0) { 3009 added = new int[N]; 3010 for (int i = 0; i < N; i++) { 3011 added[i] = mAdded.get(i).mIndex; 3012 if (added[i] < 0) { 3013 throwException(new IllegalStateException( 3014 "Failure saving state: active " + mAdded.get(i) 3015 + " has cleared index: " + added[i])); 3016 } 3017 if (DEBUG) { 3018 Log.v(TAG, "saveAllState: adding fragment #" + i 3019 + ": " + mAdded.get(i)); 3020 } 3021 } 3022 } 3023 3024 // Now save back stack. 3025 if (mBackStack != null) { 3026 N = mBackStack.size(); 3027 if (N > 0) { 3028 backStack = new BackStackState[N]; 3029 for (int i=0; i<N; i++) { 3030 backStack[i] = new BackStackState(mBackStack.get(i)); 3031 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 3032 + ": " + mBackStack.get(i)); 3033 } 3034 } 3035 } 3036 3037 FragmentManagerState fms = new FragmentManagerState(); 3038 fms.mActive = active; 3039 fms.mAdded = added; 3040 fms.mBackStack = backStack; 3041 if (mPrimaryNav != null) { 3042 fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex; 3043 } 3044 fms.mNextFragmentIndex = mNextFragmentIndex; 3045 saveNonConfig(); 3046 return fms; 3047 } 3048 restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig)3049 void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) { 3050 // If there is no saved state at all, then there can not be 3051 // any nonConfig fragments either, so that is that. 3052 if (state == null) return; 3053 FragmentManagerState fms = (FragmentManagerState)state; 3054 if (fms.mActive == null) return; 3055 3056 List<FragmentManagerNonConfig> childNonConfigs = null; 3057 List<ViewModelStore> viewModelStores = null; 3058 3059 // First re-attach any non-config instances we are retaining back 3060 // to their saved state, so we don't try to instantiate them again. 3061 if (nonConfig != null) { 3062 List<Fragment> nonConfigFragments = nonConfig.getFragments(); 3063 childNonConfigs = nonConfig.getChildNonConfigs(); 3064 viewModelStores = nonConfig.getViewModelStores(); 3065 final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0; 3066 for (int i = 0; i < count; i++) { 3067 Fragment f = nonConfigFragments.get(i); 3068 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 3069 int index = 0; // index into fms.mActive 3070 while (index < fms.mActive.length && fms.mActive[index].mIndex != f.mIndex) { 3071 index++; 3072 } 3073 if (index == fms.mActive.length) { 3074 throwException(new IllegalStateException("Could not find active fragment " 3075 + "with index " + f.mIndex)); 3076 } 3077 FragmentState fs = fms.mActive[index]; 3078 fs.mInstance = f; 3079 f.mSavedViewState = null; 3080 f.mBackStackNesting = 0; 3081 f.mInLayout = false; 3082 f.mAdded = false; 3083 f.mTarget = null; 3084 if (fs.mSavedFragmentState != null) { 3085 fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader()); 3086 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 3087 FragmentManagerImpl.VIEW_STATE_TAG); 3088 f.mSavedFragmentState = fs.mSavedFragmentState; 3089 } 3090 } 3091 } 3092 3093 // Build the full list of active fragments, instantiating them from 3094 // their saved state. 3095 mActive = new SparseArray<>(fms.mActive.length); 3096 for (int i=0; i<fms.mActive.length; i++) { 3097 FragmentState fs = fms.mActive[i]; 3098 if (fs != null) { 3099 FragmentManagerNonConfig childNonConfig = null; 3100 if (childNonConfigs != null && i < childNonConfigs.size()) { 3101 childNonConfig = childNonConfigs.get(i); 3102 } 3103 ViewModelStore viewModelStore = null; 3104 if (viewModelStores != null && i < viewModelStores.size()) { 3105 viewModelStore = viewModelStores.get(i); 3106 } 3107 Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig, 3108 viewModelStore); 3109 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f); 3110 mActive.put(f.mIndex, f); 3111 // Now that the fragment is instantiated (or came from being 3112 // retained above), clear mInstance in case we end up re-restoring 3113 // from this FragmentState again. 3114 fs.mInstance = null; 3115 } 3116 } 3117 3118 // Update the target of all retained fragments. 3119 if (nonConfig != null) { 3120 List<Fragment> nonConfigFragments = nonConfig.getFragments(); 3121 final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0; 3122 for (int i = 0; i < count; i++) { 3123 Fragment f = nonConfigFragments.get(i); 3124 if (f.mTargetIndex >= 0) { 3125 f.mTarget = mActive.get(f.mTargetIndex); 3126 if (f.mTarget == null) { 3127 Log.w(TAG, "Re-attaching retained fragment " + f 3128 + " target no longer exists: " + f.mTargetIndex); 3129 } 3130 } 3131 } 3132 } 3133 3134 // Build the list of currently added fragments. 3135 mAdded.clear(); 3136 if (fms.mAdded != null) { 3137 for (int i=0; i<fms.mAdded.length; i++) { 3138 Fragment f = mActive.get(fms.mAdded[i]); 3139 if (f == null) { 3140 throwException(new IllegalStateException( 3141 "No instantiated fragment for index #" + fms.mAdded[i])); 3142 } 3143 f.mAdded = true; 3144 if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f); 3145 if (mAdded.contains(f)) { 3146 throw new IllegalStateException("Already added!"); 3147 } 3148 synchronized (mAdded) { 3149 mAdded.add(f); 3150 } 3151 } 3152 } 3153 3154 // Build the back stack. 3155 if (fms.mBackStack != null) { 3156 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 3157 for (int i=0; i<fms.mBackStack.length; i++) { 3158 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 3159 if (DEBUG) { 3160 Log.v(TAG, "restoreAllState: back stack #" + i 3161 + " (index " + bse.mIndex + "): " + bse); 3162 LogWriter logw = new LogWriter(TAG); 3163 PrintWriter pw = new PrintWriter(logw); 3164 bse.dump(" ", pw, false); 3165 pw.close(); 3166 } 3167 mBackStack.add(bse); 3168 if (bse.mIndex >= 0) { 3169 setBackStackIndex(bse.mIndex, bse); 3170 } 3171 } 3172 } else { 3173 mBackStack = null; 3174 } 3175 3176 if (fms.mPrimaryNavActiveIndex >= 0) { 3177 mPrimaryNav = mActive.get(fms.mPrimaryNavActiveIndex); 3178 } 3179 this.mNextFragmentIndex = fms.mNextFragmentIndex; 3180 } 3181 3182 /** 3183 * To prevent list modification errors, mActive sets values to null instead of 3184 * removing them when the Fragment becomes inactive. This cleans up the list at the 3185 * end of executing the transactions. 3186 */ burpActive()3187 private void burpActive() { 3188 if (mActive != null) { 3189 for (int i = mActive.size() - 1; i >= 0; i--) { 3190 if (mActive.valueAt(i) == null) { 3191 mActive.delete(mActive.keyAt(i)); 3192 } 3193 } 3194 } 3195 } 3196 attachController(FragmentHostCallback host, FragmentContainer container, Fragment parent)3197 public void attachController(FragmentHostCallback host, 3198 FragmentContainer container, Fragment parent) { 3199 if (mHost != null) throw new IllegalStateException("Already attached"); 3200 mHost = host; 3201 mContainer = container; 3202 mParent = parent; 3203 } 3204 noteStateNotSaved()3205 public void noteStateNotSaved() { 3206 mSavedNonConfig = null; 3207 mStateSaved = false; 3208 mStopped = false; 3209 final int addedCount = mAdded.size(); 3210 for (int i = 0; i < addedCount; i++) { 3211 Fragment fragment = mAdded.get(i); 3212 if (fragment != null) { 3213 fragment.noteStateNotSaved(); 3214 } 3215 } 3216 } 3217 dispatchCreate()3218 public void dispatchCreate() { 3219 mStateSaved = false; 3220 mStopped = false; 3221 dispatchStateChange(Fragment.CREATED); 3222 } 3223 dispatchActivityCreated()3224 public void dispatchActivityCreated() { 3225 mStateSaved = false; 3226 mStopped = false; 3227 dispatchStateChange(Fragment.ACTIVITY_CREATED); 3228 } 3229 dispatchStart()3230 public void dispatchStart() { 3231 mStateSaved = false; 3232 mStopped = false; 3233 dispatchStateChange(Fragment.STARTED); 3234 } 3235 dispatchResume()3236 public void dispatchResume() { 3237 mStateSaved = false; 3238 mStopped = false; 3239 dispatchStateChange(Fragment.RESUMED); 3240 } 3241 dispatchPause()3242 public void dispatchPause() { 3243 dispatchStateChange(Fragment.STARTED); 3244 } 3245 dispatchStop()3246 public void dispatchStop() { 3247 mStopped = true; 3248 dispatchStateChange(Fragment.ACTIVITY_CREATED); 3249 } 3250 dispatchDestroyView()3251 public void dispatchDestroyView() { 3252 dispatchStateChange(Fragment.CREATED); 3253 } 3254 dispatchDestroy()3255 public void dispatchDestroy() { 3256 mDestroyed = true; 3257 execPendingActions(); 3258 dispatchStateChange(Fragment.INITIALIZING); 3259 mHost = null; 3260 mContainer = null; 3261 mParent = null; 3262 } 3263 dispatchStateChange(int nextState)3264 private void dispatchStateChange(int nextState) { 3265 try { 3266 mExecutingActions = true; 3267 moveToState(nextState, false); 3268 } finally { 3269 mExecutingActions = false; 3270 } 3271 execPendingActions(); 3272 } 3273 dispatchMultiWindowModeChanged(boolean isInMultiWindowMode)3274 public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) { 3275 for (int i = mAdded.size() - 1; i >= 0; --i) { 3276 final Fragment f = mAdded.get(i); 3277 if (f != null) { 3278 f.performMultiWindowModeChanged(isInMultiWindowMode); 3279 } 3280 } 3281 } 3282 dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode)3283 public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) { 3284 for (int i = mAdded.size() - 1; i >= 0; --i) { 3285 final Fragment f = mAdded.get(i); 3286 if (f != null) { 3287 f.performPictureInPictureModeChanged(isInPictureInPictureMode); 3288 } 3289 } 3290 } 3291 dispatchConfigurationChanged(Configuration newConfig)3292 public void dispatchConfigurationChanged(Configuration newConfig) { 3293 for (int i = 0; i < mAdded.size(); i++) { 3294 Fragment f = mAdded.get(i); 3295 if (f != null) { 3296 f.performConfigurationChanged(newConfig); 3297 } 3298 } 3299 } 3300 dispatchLowMemory()3301 public void dispatchLowMemory() { 3302 for (int i = 0; i < mAdded.size(); i++) { 3303 Fragment f = mAdded.get(i); 3304 if (f != null) { 3305 f.performLowMemory(); 3306 } 3307 } 3308 } 3309 dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater)3310 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 3311 if (mCurState < Fragment.CREATED) { 3312 return false; 3313 } 3314 boolean show = false; 3315 ArrayList<Fragment> newMenus = null; 3316 for (int i = 0; i < mAdded.size(); i++) { 3317 Fragment f = mAdded.get(i); 3318 if (f != null) { 3319 if (f.performCreateOptionsMenu(menu, inflater)) { 3320 show = true; 3321 if (newMenus == null) { 3322 newMenus = new ArrayList<Fragment>(); 3323 } 3324 newMenus.add(f); 3325 } 3326 } 3327 } 3328 3329 if (mCreatedMenus != null) { 3330 for (int i=0; i<mCreatedMenus.size(); i++) { 3331 Fragment f = mCreatedMenus.get(i); 3332 if (newMenus == null || !newMenus.contains(f)) { 3333 f.onDestroyOptionsMenu(); 3334 } 3335 } 3336 } 3337 3338 mCreatedMenus = newMenus; 3339 3340 return show; 3341 } 3342 dispatchPrepareOptionsMenu(Menu menu)3343 public boolean dispatchPrepareOptionsMenu(Menu menu) { 3344 if (mCurState < Fragment.CREATED) { 3345 return false; 3346 } 3347 boolean show = false; 3348 for (int i = 0; i < mAdded.size(); i++) { 3349 Fragment f = mAdded.get(i); 3350 if (f != null) { 3351 if (f.performPrepareOptionsMenu(menu)) { 3352 show = true; 3353 } 3354 } 3355 } 3356 return show; 3357 } 3358 dispatchOptionsItemSelected(MenuItem item)3359 public boolean dispatchOptionsItemSelected(MenuItem item) { 3360 if (mCurState < Fragment.CREATED) { 3361 return false; 3362 } 3363 for (int i = 0; i < mAdded.size(); i++) { 3364 Fragment f = mAdded.get(i); 3365 if (f != null) { 3366 if (f.performOptionsItemSelected(item)) { 3367 return true; 3368 } 3369 } 3370 } 3371 return false; 3372 } 3373 dispatchContextItemSelected(MenuItem item)3374 public boolean dispatchContextItemSelected(MenuItem item) { 3375 if (mCurState < Fragment.CREATED) { 3376 return false; 3377 } 3378 for (int i = 0; i < mAdded.size(); i++) { 3379 Fragment f = mAdded.get(i); 3380 if (f != null) { 3381 if (f.performContextItemSelected(item)) { 3382 return true; 3383 } 3384 } 3385 } 3386 return false; 3387 } 3388 dispatchOptionsMenuClosed(Menu menu)3389 public void dispatchOptionsMenuClosed(Menu menu) { 3390 if (mCurState < Fragment.CREATED) { 3391 return; 3392 } 3393 for (int i = 0; i < mAdded.size(); i++) { 3394 Fragment f = mAdded.get(i); 3395 if (f != null) { 3396 f.performOptionsMenuClosed(menu); 3397 } 3398 } 3399 } 3400 3401 @SuppressWarnings("ReferenceEquality") setPrimaryNavigationFragment(Fragment f)3402 public void setPrimaryNavigationFragment(Fragment f) { 3403 if (f != null && (mActive.get(f.mIndex) != f 3404 || (f.mHost != null && f.getFragmentManager() != this))) { 3405 throw new IllegalArgumentException("Fragment " + f 3406 + " is not an active fragment of FragmentManager " + this); 3407 } 3408 mPrimaryNav = f; 3409 } 3410 3411 @Override 3412 @Nullable getPrimaryNavigationFragment()3413 public Fragment getPrimaryNavigationFragment() { 3414 return mPrimaryNav; 3415 } 3416 3417 @Override registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb, boolean recursive)3418 public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb, 3419 boolean recursive) { 3420 mLifecycleCallbacks.add(new FragmentLifecycleCallbacksHolder(cb, recursive)); 3421 } 3422 3423 @Override unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb)3424 public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) { 3425 synchronized (mLifecycleCallbacks) { 3426 for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) { 3427 if (mLifecycleCallbacks.get(i).mCallback == cb) { 3428 mLifecycleCallbacks.remove(i); 3429 break; 3430 } 3431 } 3432 } 3433 } 3434 dispatchOnFragmentPreAttached(@onNull Fragment f, @NonNull Context context, boolean onlyRecursive)3435 void dispatchOnFragmentPreAttached(@NonNull Fragment f, @NonNull Context context, 3436 boolean onlyRecursive) { 3437 if (mParent != null) { 3438 FragmentManager parentManager = mParent.getFragmentManager(); 3439 if (parentManager instanceof FragmentManagerImpl) { 3440 ((FragmentManagerImpl) parentManager) 3441 .dispatchOnFragmentPreAttached(f, context, true); 3442 } 3443 } 3444 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3445 if (!onlyRecursive || holder.mRecursive) { 3446 holder.mCallback.onFragmentPreAttached(this, f, context); 3447 } 3448 } 3449 } 3450 dispatchOnFragmentAttached(@onNull Fragment f, @NonNull Context context, boolean onlyRecursive)3451 void dispatchOnFragmentAttached(@NonNull Fragment f, @NonNull Context context, 3452 boolean onlyRecursive) { 3453 if (mParent != null) { 3454 FragmentManager parentManager = mParent.getFragmentManager(); 3455 if (parentManager instanceof FragmentManagerImpl) { 3456 ((FragmentManagerImpl) parentManager) 3457 .dispatchOnFragmentAttached(f, context, true); 3458 } 3459 } 3460 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3461 if (!onlyRecursive || holder.mRecursive) { 3462 holder.mCallback.onFragmentAttached(this, f, context); 3463 } 3464 } 3465 } 3466 dispatchOnFragmentPreCreated(@onNull Fragment f, @Nullable Bundle savedInstanceState, boolean onlyRecursive)3467 void dispatchOnFragmentPreCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState, 3468 boolean onlyRecursive) { 3469 if (mParent != null) { 3470 FragmentManager parentManager = mParent.getFragmentManager(); 3471 if (parentManager instanceof FragmentManagerImpl) { 3472 ((FragmentManagerImpl) parentManager) 3473 .dispatchOnFragmentPreCreated(f, savedInstanceState, true); 3474 } 3475 } 3476 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3477 if (!onlyRecursive || holder.mRecursive) { 3478 holder.mCallback.onFragmentPreCreated(this, f, savedInstanceState); 3479 } 3480 } 3481 } 3482 dispatchOnFragmentCreated(@onNull Fragment f, @Nullable Bundle savedInstanceState, boolean onlyRecursive)3483 void dispatchOnFragmentCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState, 3484 boolean onlyRecursive) { 3485 if (mParent != null) { 3486 FragmentManager parentManager = mParent.getFragmentManager(); 3487 if (parentManager instanceof FragmentManagerImpl) { 3488 ((FragmentManagerImpl) parentManager) 3489 .dispatchOnFragmentCreated(f, savedInstanceState, true); 3490 } 3491 } 3492 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3493 if (!onlyRecursive || holder.mRecursive) { 3494 holder.mCallback.onFragmentCreated(this, f, savedInstanceState); 3495 } 3496 } 3497 } 3498 dispatchOnFragmentActivityCreated(@onNull Fragment f, @Nullable Bundle savedInstanceState, boolean onlyRecursive)3499 void dispatchOnFragmentActivityCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState, 3500 boolean onlyRecursive) { 3501 if (mParent != null) { 3502 FragmentManager parentManager = mParent.getFragmentManager(); 3503 if (parentManager instanceof FragmentManagerImpl) { 3504 ((FragmentManagerImpl) parentManager) 3505 .dispatchOnFragmentActivityCreated(f, savedInstanceState, true); 3506 } 3507 } 3508 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3509 if (!onlyRecursive || holder.mRecursive) { 3510 holder.mCallback.onFragmentActivityCreated(this, f, savedInstanceState); 3511 } 3512 } 3513 } 3514 dispatchOnFragmentViewCreated(@onNull Fragment f, @NonNull View v, @Nullable Bundle savedInstanceState, boolean onlyRecursive)3515 void dispatchOnFragmentViewCreated(@NonNull Fragment f, @NonNull View v, 3516 @Nullable Bundle savedInstanceState, boolean onlyRecursive) { 3517 if (mParent != null) { 3518 FragmentManager parentManager = mParent.getFragmentManager(); 3519 if (parentManager instanceof FragmentManagerImpl) { 3520 ((FragmentManagerImpl) parentManager) 3521 .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true); 3522 } 3523 } 3524 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3525 if (!onlyRecursive || holder.mRecursive) { 3526 holder.mCallback.onFragmentViewCreated(this, f, v, savedInstanceState); 3527 } 3528 } 3529 } 3530 dispatchOnFragmentStarted(@onNull Fragment f, boolean onlyRecursive)3531 void dispatchOnFragmentStarted(@NonNull Fragment f, boolean onlyRecursive) { 3532 if (mParent != null) { 3533 FragmentManager parentManager = mParent.getFragmentManager(); 3534 if (parentManager instanceof FragmentManagerImpl) { 3535 ((FragmentManagerImpl) parentManager) 3536 .dispatchOnFragmentStarted(f, true); 3537 } 3538 } 3539 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3540 if (!onlyRecursive || holder.mRecursive) { 3541 holder.mCallback.onFragmentStarted(this, f); 3542 } 3543 } 3544 } 3545 dispatchOnFragmentResumed(@onNull Fragment f, boolean onlyRecursive)3546 void dispatchOnFragmentResumed(@NonNull Fragment f, boolean onlyRecursive) { 3547 if (mParent != null) { 3548 FragmentManager parentManager = mParent.getFragmentManager(); 3549 if (parentManager instanceof FragmentManagerImpl) { 3550 ((FragmentManagerImpl) parentManager) 3551 .dispatchOnFragmentResumed(f, true); 3552 } 3553 } 3554 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3555 if (!onlyRecursive || holder.mRecursive) { 3556 holder.mCallback.onFragmentResumed(this, f); 3557 } 3558 } 3559 } 3560 dispatchOnFragmentPaused(@onNull Fragment f, boolean onlyRecursive)3561 void dispatchOnFragmentPaused(@NonNull Fragment f, boolean onlyRecursive) { 3562 if (mParent != null) { 3563 FragmentManager parentManager = mParent.getFragmentManager(); 3564 if (parentManager instanceof FragmentManagerImpl) { 3565 ((FragmentManagerImpl) parentManager) 3566 .dispatchOnFragmentPaused(f, true); 3567 } 3568 } 3569 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3570 if (!onlyRecursive || holder.mRecursive) { 3571 holder.mCallback.onFragmentPaused(this, f); 3572 } 3573 } 3574 } 3575 dispatchOnFragmentStopped(@onNull Fragment f, boolean onlyRecursive)3576 void dispatchOnFragmentStopped(@NonNull Fragment f, boolean onlyRecursive) { 3577 if (mParent != null) { 3578 FragmentManager parentManager = mParent.getFragmentManager(); 3579 if (parentManager instanceof FragmentManagerImpl) { 3580 ((FragmentManagerImpl) parentManager) 3581 .dispatchOnFragmentStopped(f, true); 3582 } 3583 } 3584 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3585 if (!onlyRecursive || holder.mRecursive) { 3586 holder.mCallback.onFragmentStopped(this, f); 3587 } 3588 } 3589 } 3590 dispatchOnFragmentSaveInstanceState(@onNull Fragment f, @NonNull Bundle outState, boolean onlyRecursive)3591 void dispatchOnFragmentSaveInstanceState(@NonNull Fragment f, @NonNull Bundle outState, 3592 boolean onlyRecursive) { 3593 if (mParent != null) { 3594 FragmentManager parentManager = mParent.getFragmentManager(); 3595 if (parentManager instanceof FragmentManagerImpl) { 3596 ((FragmentManagerImpl) parentManager) 3597 .dispatchOnFragmentSaveInstanceState(f, outState, true); 3598 } 3599 } 3600 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3601 if (!onlyRecursive || holder.mRecursive) { 3602 holder.mCallback.onFragmentSaveInstanceState(this, f, outState); 3603 } 3604 } 3605 } 3606 dispatchOnFragmentViewDestroyed(@onNull Fragment f, boolean onlyRecursive)3607 void dispatchOnFragmentViewDestroyed(@NonNull Fragment f, boolean onlyRecursive) { 3608 if (mParent != null) { 3609 FragmentManager parentManager = mParent.getFragmentManager(); 3610 if (parentManager instanceof FragmentManagerImpl) { 3611 ((FragmentManagerImpl) parentManager) 3612 .dispatchOnFragmentViewDestroyed(f, true); 3613 } 3614 } 3615 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3616 if (!onlyRecursive || holder.mRecursive) { 3617 holder.mCallback.onFragmentViewDestroyed(this, f); 3618 } 3619 } 3620 } 3621 dispatchOnFragmentDestroyed(@onNull Fragment f, boolean onlyRecursive)3622 void dispatchOnFragmentDestroyed(@NonNull Fragment f, boolean onlyRecursive) { 3623 if (mParent != null) { 3624 FragmentManager parentManager = mParent.getFragmentManager(); 3625 if (parentManager instanceof FragmentManagerImpl) { 3626 ((FragmentManagerImpl) parentManager) 3627 .dispatchOnFragmentDestroyed(f, true); 3628 } 3629 } 3630 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3631 if (!onlyRecursive || holder.mRecursive) { 3632 holder.mCallback.onFragmentDestroyed(this, f); 3633 } 3634 } 3635 } 3636 dispatchOnFragmentDetached(@onNull Fragment f, boolean onlyRecursive)3637 void dispatchOnFragmentDetached(@NonNull Fragment f, boolean onlyRecursive) { 3638 if (mParent != null) { 3639 FragmentManager parentManager = mParent.getFragmentManager(); 3640 if (parentManager instanceof FragmentManagerImpl) { 3641 ((FragmentManagerImpl) parentManager) 3642 .dispatchOnFragmentDetached(f, true); 3643 } 3644 } 3645 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3646 if (!onlyRecursive || holder.mRecursive) { 3647 holder.mCallback.onFragmentDetached(this, f); 3648 } 3649 } 3650 } 3651 reverseTransit(int transit)3652 public static int reverseTransit(int transit) { 3653 int rev = 0; 3654 switch (transit) { 3655 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 3656 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 3657 break; 3658 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 3659 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 3660 break; 3661 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 3662 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; 3663 break; 3664 } 3665 return rev; 3666 3667 } 3668 3669 public static final int ANIM_STYLE_OPEN_ENTER = 1; 3670 public static final int ANIM_STYLE_OPEN_EXIT = 2; 3671 public static final int ANIM_STYLE_CLOSE_ENTER = 3; 3672 public static final int ANIM_STYLE_CLOSE_EXIT = 4; 3673 public static final int ANIM_STYLE_FADE_ENTER = 5; 3674 public static final int ANIM_STYLE_FADE_EXIT = 6; 3675 transitToStyleIndex(int transit, boolean enter)3676 public static int transitToStyleIndex(int transit, boolean enter) { 3677 int animAttr = -1; 3678 switch (transit) { 3679 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 3680 animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT; 3681 break; 3682 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 3683 animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT; 3684 break; 3685 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 3686 animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT; 3687 break; 3688 } 3689 return animAttr; 3690 } 3691 3692 @Override onCreateView(View parent, String name, Context context, AttributeSet attrs)3693 public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { 3694 if (!"fragment".equals(name)) { 3695 return null; 3696 } 3697 3698 String fname = attrs.getAttributeValue(null, "class"); 3699 TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment); 3700 if (fname == null) { 3701 fname = a.getString(FragmentTag.Fragment_name); 3702 } 3703 int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID); 3704 String tag = a.getString(FragmentTag.Fragment_tag); 3705 a.recycle(); 3706 3707 if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) { 3708 // Invalid support lib fragment; let the device's framework handle it. 3709 // This will allow android.app.Fragments to do the right thing. 3710 return null; 3711 } 3712 3713 int containerId = parent != null ? parent.getId() : 0; 3714 if (containerId == View.NO_ID && id == View.NO_ID && tag == null) { 3715 throw new IllegalArgumentException(attrs.getPositionDescription() 3716 + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname); 3717 } 3718 3719 // If we restored from a previous state, we may already have 3720 // instantiated this fragment from the state and should use 3721 // that instance instead of making a new one. 3722 Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null; 3723 if (fragment == null && tag != null) { 3724 fragment = findFragmentByTag(tag); 3725 } 3726 if (fragment == null && containerId != View.NO_ID) { 3727 fragment = findFragmentById(containerId); 3728 } 3729 3730 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x" 3731 + Integer.toHexString(id) + " fname=" + fname 3732 + " existing=" + fragment); 3733 if (fragment == null) { 3734 fragment = mContainer.instantiate(context, fname, null); 3735 fragment.mFromLayout = true; 3736 fragment.mFragmentId = id != 0 ? id : containerId; 3737 fragment.mContainerId = containerId; 3738 fragment.mTag = tag; 3739 fragment.mInLayout = true; 3740 fragment.mFragmentManager = this; 3741 fragment.mHost = mHost; 3742 fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState); 3743 addFragment(fragment, true); 3744 3745 } else if (fragment.mInLayout) { 3746 // A fragment already exists and it is not one we restored from 3747 // previous state. 3748 throw new IllegalArgumentException(attrs.getPositionDescription() 3749 + ": Duplicate id 0x" + Integer.toHexString(id) 3750 + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId) 3751 + " with another fragment for " + fname); 3752 } else { 3753 // This fragment was retained from a previous instance; get it 3754 // going now. 3755 fragment.mInLayout = true; 3756 fragment.mHost = mHost; 3757 // If this fragment is newly instantiated (either right now, or 3758 // from last saved state), then give it the attributes to 3759 // initialize itself. 3760 if (!fragment.mRetaining) { 3761 fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState); 3762 } 3763 } 3764 3765 // If we haven't finished entering the CREATED state ourselves yet, 3766 // push the inflated child fragment along. This will ensureInflatedFragmentView 3767 // at the right phase of the lifecycle so that we will have mView populated 3768 // for compliant fragments below. 3769 if (mCurState < Fragment.CREATED && fragment.mFromLayout) { 3770 moveToState(fragment, Fragment.CREATED, 0, 0, false); 3771 } else { 3772 moveToState(fragment); 3773 } 3774 3775 if (fragment.mView == null) { 3776 throw new IllegalStateException("Fragment " + fname 3777 + " did not create a view."); 3778 } 3779 if (id != 0) { 3780 fragment.mView.setId(id); 3781 } 3782 if (fragment.mView.getTag() == null) { 3783 fragment.mView.setTag(tag); 3784 } 3785 return fragment.mView; 3786 } 3787 3788 @Override onCreateView(String name, Context context, AttributeSet attrs)3789 public View onCreateView(String name, Context context, AttributeSet attrs) { 3790 return onCreateView(null, name, context, attrs); 3791 } 3792 getLayoutInflaterFactory()3793 LayoutInflater.Factory2 getLayoutInflaterFactory() { 3794 return this; 3795 } 3796 3797 static class FragmentTag { 3798 public static final int[] Fragment = { 3799 0x01010003, 0x010100d0, 0x010100d1 3800 }; 3801 public static final int Fragment_id = 1; 3802 public static final int Fragment_name = 0; 3803 public static final int Fragment_tag = 2; 3804 FragmentTag()3805 private FragmentTag() { 3806 } 3807 } 3808 3809 /** 3810 * An add or pop transaction to be scheduled for the UI thread. 3811 */ 3812 interface OpGenerator { 3813 /** 3814 * Generate transactions to add to {@code records} and whether or not the transaction is 3815 * an add or pop to {@code isRecordPop}. 3816 * 3817 * records and isRecordPop must be added equally so that each transaction in records 3818 * matches the boolean for whether or not it is a pop in isRecordPop. 3819 * 3820 * @param records A list to add transactions to. 3821 * @param isRecordPop A list to add whether or not the transactions added to records is 3822 * a pop transaction. 3823 * @return true if something was added or false otherwise. 3824 */ generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop)3825 boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop); 3826 } 3827 3828 /** 3829 * A pop operation OpGenerator. This will be run on the UI thread and will generate the 3830 * transactions that will be popped if anything can be popped. 3831 */ 3832 private class PopBackStackState implements OpGenerator { 3833 final String mName; 3834 final int mId; 3835 final int mFlags; 3836 PopBackStackState(String name, int id, int flags)3837 PopBackStackState(String name, int id, int flags) { 3838 mName = name; 3839 mId = id; 3840 mFlags = flags; 3841 } 3842 3843 @Override generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop)3844 public boolean generateOps(ArrayList<BackStackRecord> records, 3845 ArrayList<Boolean> isRecordPop) { 3846 if (mPrimaryNav != null // We have a primary nav fragment 3847 && mId < 0 // No valid id (since they're local) 3848 && mName == null) { // no name to pop to (since they're local) 3849 final FragmentManager childManager = mPrimaryNav.peekChildFragmentManager(); 3850 if (childManager != null && childManager.popBackStackImmediate()) { 3851 // We didn't add any operations for this FragmentManager even though 3852 // a child did do work. 3853 return false; 3854 } 3855 } 3856 return popBackStackState(records, isRecordPop, mName, mId, mFlags); 3857 } 3858 } 3859 3860 /** 3861 * A listener for a postponed transaction. This waits until 3862 * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started 3863 * that interacts with this one, based on interactions with the fragment container. 3864 */ 3865 static class StartEnterTransitionListener 3866 implements Fragment.OnStartEnterTransitionListener { 3867 private final boolean mIsBack; 3868 private final BackStackRecord mRecord; 3869 private int mNumPostponed; 3870 StartEnterTransitionListener(BackStackRecord record, boolean isBack)3871 StartEnterTransitionListener(BackStackRecord record, boolean isBack) { 3872 mIsBack = isBack; 3873 mRecord = record; 3874 } 3875 3876 /** 3877 * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the 3878 * number of Fragments that are postponed. This may cause the transaction to schedule 3879 * to finish running and run transitions and animations. 3880 */ 3881 @Override onStartEnterTransition()3882 public void onStartEnterTransition() { 3883 mNumPostponed--; 3884 if (mNumPostponed != 0) { 3885 return; 3886 } 3887 mRecord.mManager.scheduleCommit(); 3888 } 3889 3890 /** 3891 * Called from {@link Fragment# 3892 * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this 3893 * increases the number of fragments that are postponed as part of this transaction. 3894 */ 3895 @Override startListening()3896 public void startListening() { 3897 mNumPostponed++; 3898 } 3899 3900 /** 3901 * @return true if there are no more postponed fragments as part of the transaction. 3902 */ isReady()3903 public boolean isReady() { 3904 return mNumPostponed == 0; 3905 } 3906 3907 /** 3908 * Completes the transaction and start the animations and transitions. This may skip 3909 * the transitions if this is called before all fragments have called 3910 * {@link Fragment#startPostponedEnterTransition()}. 3911 */ completeTransaction()3912 public void completeTransaction() { 3913 final boolean canceled; 3914 canceled = mNumPostponed > 0; 3915 FragmentManagerImpl manager = mRecord.mManager; 3916 final int numAdded = manager.mAdded.size(); 3917 for (int i = 0; i < numAdded; i++) { 3918 final Fragment fragment = manager.mAdded.get(i); 3919 fragment.setOnStartEnterTransitionListener(null); 3920 if (canceled && fragment.isPostponed()) { 3921 fragment.startPostponedEnterTransition(); 3922 } 3923 } 3924 mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true); 3925 } 3926 3927 /** 3928 * Cancels this transaction instead of completing it. That means that the state isn't 3929 * changed, so the pop results in no change to the state. 3930 */ cancelTransaction()3931 public void cancelTransaction() { 3932 mRecord.mManager.completeExecute(mRecord, mIsBack, false, false); 3933 } 3934 } 3935 3936 /** 3937 * Contains either an animator or animation. One of these should be null. 3938 */ 3939 private static class AnimationOrAnimator { 3940 public final Animation animation; 3941 public final Animator animator; 3942 AnimationOrAnimator(Animation animation)3943 private AnimationOrAnimator(Animation animation) { 3944 this.animation = animation; 3945 this.animator = null; 3946 if (animation == null) { 3947 throw new IllegalStateException("Animation cannot be null"); 3948 } 3949 } 3950 AnimationOrAnimator(Animator animator)3951 private AnimationOrAnimator(Animator animator) { 3952 this.animation = null; 3953 this.animator = animator; 3954 if (animator == null) { 3955 throw new IllegalStateException("Animator cannot be null"); 3956 } 3957 } 3958 } 3959 3960 /** 3961 * Wrap an AnimationListener that can be null. This allows us to chain animation listeners. 3962 */ 3963 private static class AnimationListenerWrapper implements AnimationListener { 3964 private final AnimationListener mWrapped; 3965 AnimationListenerWrapper(AnimationListener wrapped)3966 private AnimationListenerWrapper(AnimationListener wrapped) { 3967 mWrapped = wrapped; 3968 } 3969 3970 @CallSuper 3971 @Override onAnimationStart(Animation animation)3972 public void onAnimationStart(Animation animation) { 3973 if (mWrapped != null) { 3974 mWrapped.onAnimationStart(animation); 3975 } 3976 } 3977 3978 @CallSuper 3979 @Override onAnimationEnd(Animation animation)3980 public void onAnimationEnd(Animation animation) { 3981 if (mWrapped != null) { 3982 mWrapped.onAnimationEnd(animation); 3983 } 3984 } 3985 3986 @CallSuper 3987 @Override onAnimationRepeat(Animation animation)3988 public void onAnimationRepeat(Animation animation) { 3989 if (mWrapped != null) { 3990 mWrapped.onAnimationRepeat(animation); 3991 } 3992 } 3993 } 3994 3995 /** 3996 * Reset the layer type to LAYER_TYPE_NONE at the end of an animation. 3997 */ 3998 private static class AnimateOnHWLayerIfNeededListener extends AnimationListenerWrapper { 3999 View mView; 4000 AnimateOnHWLayerIfNeededListener(final View v, AnimationListener listener)4001 AnimateOnHWLayerIfNeededListener(final View v, AnimationListener listener) { 4002 super(listener); 4003 mView = v; 4004 } 4005 4006 @Override 4007 @CallSuper onAnimationEnd(Animation animation)4008 public void onAnimationEnd(Animation animation) { 4009 // If we're attached to a window, assume we're in the normal performTraversals 4010 // drawing path for Animations running. It's not safe to change the layer type 4011 // during drawing, so post it to the View to run later. If we're not attached 4012 // or we're running on N and above, post it to the view. If we're not on N and 4013 // not attached, do it right now since existing platform versions don't run the 4014 // hwui renderer for detached views off the UI thread making changing layer type 4015 // safe, but posting may not be. 4016 // Prior to N posting to a detached view from a non-Looper thread could cause 4017 // leaks, since the thread-local run queue on a non-Looper thread would never 4018 // be flushed. 4019 if (ViewCompat.isAttachedToWindow(mView) || Build.VERSION.SDK_INT >= 24) { 4020 mView.post(new Runnable() { 4021 @Override 4022 public void run() { 4023 mView.setLayerType(View.LAYER_TYPE_NONE, null); 4024 } 4025 }); 4026 } else { 4027 mView.setLayerType(View.LAYER_TYPE_NONE, null); 4028 } 4029 super.onAnimationEnd(animation); 4030 } 4031 } 4032 4033 /** 4034 * Set the layer type to LAYER_TYPE_HARDWARE while an animator is running. 4035 */ 4036 private static class AnimatorOnHWLayerIfNeededListener extends AnimatorListenerAdapter { 4037 View mView; 4038 AnimatorOnHWLayerIfNeededListener(final View v)4039 AnimatorOnHWLayerIfNeededListener(final View v) { 4040 mView = v; 4041 } 4042 4043 @Override onAnimationStart(Animator animation)4044 public void onAnimationStart(Animator animation) { 4045 mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 4046 } 4047 4048 @Override onAnimationEnd(Animator animation)4049 public void onAnimationEnd(Animator animation) { 4050 mView.setLayerType(View.LAYER_TYPE_NONE, null); 4051 animation.removeListener(this); 4052 } 4053 } 4054 4055 /** 4056 * We must call endViewTransition() before the animation ends or else the parent doesn't 4057 * get nulled out. We use both startViewTransition() and startAnimation() to solve a problem 4058 * with Views remaining in the hierarchy as disappearing children after the view has been 4059 * removed in some edge cases. 4060 */ 4061 private static class EndViewTransitionAnimator extends AnimationSet implements Runnable { 4062 private final ViewGroup mParent; 4063 private final View mChild; 4064 private boolean mEnded; 4065 private boolean mTransitionEnded; 4066 EndViewTransitionAnimator(@onNull Animation animation, @NonNull ViewGroup parent, @NonNull View child)4067 EndViewTransitionAnimator(@NonNull Animation animation, 4068 @NonNull ViewGroup parent, @NonNull View child) { 4069 super(false); 4070 mParent = parent; 4071 mChild = child; 4072 addAnimation(animation); 4073 } 4074 4075 @Override getTransformation(long currentTime, Transformation t)4076 public boolean getTransformation(long currentTime, Transformation t) { 4077 if (mEnded) { 4078 return !mTransitionEnded; 4079 } 4080 boolean more = super.getTransformation(currentTime, t); 4081 if (!more) { 4082 mEnded = true; 4083 OneShotPreDrawListener.add(mParent, this); 4084 } 4085 return true; 4086 } 4087 4088 @Override getTransformation(long currentTime, Transformation outTransformation, float scale)4089 public boolean getTransformation(long currentTime, Transformation outTransformation, 4090 float scale) { 4091 if (mEnded) { 4092 return !mTransitionEnded; 4093 } 4094 boolean more = super.getTransformation(currentTime, outTransformation, scale); 4095 if (!more) { 4096 mEnded = true; 4097 OneShotPreDrawListener.add(mParent, this); 4098 } 4099 return true; 4100 } 4101 4102 @Override run()4103 public void run() { 4104 mParent.endViewTransition(mChild); 4105 mTransitionEnded = true; 4106 } 4107 } 4108 } 4109