1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app; 18 19 import android.os.Build; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 import android.text.TextUtils; 23 import android.util.Log; 24 import android.util.LogWriter; 25 import android.view.View; 26 27 import com.android.internal.util.FastPrintWriter; 28 29 import java.io.FileDescriptor; 30 import java.io.PrintWriter; 31 import java.lang.reflect.Modifier; 32 import java.util.ArrayList; 33 34 final class BackStackState implements Parcelable { 35 final int[] mOps; 36 final int mTransition; 37 final int mTransitionStyle; 38 final String mName; 39 final int mIndex; 40 final int mBreadCrumbTitleRes; 41 final CharSequence mBreadCrumbTitleText; 42 final int mBreadCrumbShortTitleRes; 43 final CharSequence mBreadCrumbShortTitleText; 44 final ArrayList<String> mSharedElementSourceNames; 45 final ArrayList<String> mSharedElementTargetNames; 46 final boolean mReorderingAllowed; 47 BackStackState(FragmentManagerImpl fm, BackStackRecord bse)48 public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) { 49 final int numOps = bse.mOps.size(); 50 mOps = new int[numOps * 6]; 51 52 if (!bse.mAddToBackStack) { 53 throw new IllegalStateException("Not on back stack"); 54 } 55 56 int pos = 0; 57 for (int opNum = 0; opNum < numOps; opNum++) { 58 final BackStackRecord.Op op = bse.mOps.get(opNum); 59 mOps[pos++] = op.cmd; 60 mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1; 61 mOps[pos++] = op.enterAnim; 62 mOps[pos++] = op.exitAnim; 63 mOps[pos++] = op.popEnterAnim; 64 mOps[pos++] = op.popExitAnim; 65 } 66 mTransition = bse.mTransition; 67 mTransitionStyle = bse.mTransitionStyle; 68 mName = bse.mName; 69 mIndex = bse.mIndex; 70 mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes; 71 mBreadCrumbTitleText = bse.mBreadCrumbTitleText; 72 mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes; 73 mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText; 74 mSharedElementSourceNames = bse.mSharedElementSourceNames; 75 mSharedElementTargetNames = bse.mSharedElementTargetNames; 76 mReorderingAllowed = bse.mReorderingAllowed; 77 } 78 BackStackState(Parcel in)79 public BackStackState(Parcel in) { 80 mOps = in.createIntArray(); 81 mTransition = in.readInt(); 82 mTransitionStyle = in.readInt(); 83 mName = in.readString(); 84 mIndex = in.readInt(); 85 mBreadCrumbTitleRes = in.readInt(); 86 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 87 mBreadCrumbShortTitleRes = in.readInt(); 88 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 89 mSharedElementSourceNames = in.createStringArrayList(); 90 mSharedElementTargetNames = in.createStringArrayList(); 91 mReorderingAllowed = in.readInt() != 0; 92 } 93 instantiate(FragmentManagerImpl fm)94 public BackStackRecord instantiate(FragmentManagerImpl fm) { 95 BackStackRecord bse = new BackStackRecord(fm); 96 int pos = 0; 97 int num = 0; 98 while (pos < mOps.length) { 99 BackStackRecord.Op op = new BackStackRecord.Op(); 100 op.cmd = mOps[pos++]; 101 if (FragmentManagerImpl.DEBUG) { 102 Log.v(FragmentManagerImpl.TAG, 103 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]); 104 } 105 int findex = mOps[pos++]; 106 if (findex >= 0) { 107 Fragment f = fm.mActive.get(findex); 108 op.fragment = f; 109 } else { 110 op.fragment = null; 111 } 112 op.enterAnim = mOps[pos++]; 113 op.exitAnim = mOps[pos++]; 114 op.popEnterAnim = mOps[pos++]; 115 op.popExitAnim = mOps[pos++]; 116 bse.mEnterAnim = op.enterAnim; 117 bse.mExitAnim = op.exitAnim; 118 bse.mPopEnterAnim = op.popEnterAnim; 119 bse.mPopExitAnim = op.popExitAnim; 120 bse.addOp(op); 121 num++; 122 } 123 bse.mTransition = mTransition; 124 bse.mTransitionStyle = mTransitionStyle; 125 bse.mName = mName; 126 bse.mIndex = mIndex; 127 bse.mAddToBackStack = true; 128 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes; 129 bse.mBreadCrumbTitleText = mBreadCrumbTitleText; 130 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes; 131 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText; 132 bse.mSharedElementSourceNames = mSharedElementSourceNames; 133 bse.mSharedElementTargetNames = mSharedElementTargetNames; 134 bse.mReorderingAllowed = mReorderingAllowed; 135 bse.bumpBackStackNesting(1); 136 return bse; 137 } 138 describeContents()139 public int describeContents() { 140 return 0; 141 } 142 writeToParcel(Parcel dest, int flags)143 public void writeToParcel(Parcel dest, int flags) { 144 dest.writeIntArray(mOps); 145 dest.writeInt(mTransition); 146 dest.writeInt(mTransitionStyle); 147 dest.writeString(mName); 148 dest.writeInt(mIndex); 149 dest.writeInt(mBreadCrumbTitleRes); 150 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0); 151 dest.writeInt(mBreadCrumbShortTitleRes); 152 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0); 153 dest.writeStringList(mSharedElementSourceNames); 154 dest.writeStringList(mSharedElementTargetNames); 155 dest.writeInt(mReorderingAllowed ? 1 : 0); 156 } 157 158 public static final @android.annotation.NonNull Parcelable.Creator<BackStackState> CREATOR 159 = new Parcelable.Creator<BackStackState>() { 160 public BackStackState createFromParcel(Parcel in) { 161 return new BackStackState(in); 162 } 163 164 public BackStackState[] newArray(int size) { 165 return new BackStackState[size]; 166 } 167 }; 168 } 169 170 /** 171 * @hide Entry of an operation on the fragment back stack. 172 */ 173 final class BackStackRecord extends FragmentTransaction implements 174 FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator { 175 static final String TAG = FragmentManagerImpl.TAG; 176 177 final FragmentManagerImpl mManager; 178 179 static final int OP_NULL = 0; 180 static final int OP_ADD = 1; 181 static final int OP_REPLACE = 2; 182 static final int OP_REMOVE = 3; 183 static final int OP_HIDE = 4; 184 static final int OP_SHOW = 5; 185 static final int OP_DETACH = 6; 186 static final int OP_ATTACH = 7; 187 static final int OP_SET_PRIMARY_NAV = 8; 188 static final int OP_UNSET_PRIMARY_NAV = 9; 189 190 static final class Op { 191 int cmd; 192 Fragment fragment; 193 int enterAnim; 194 int exitAnim; 195 int popEnterAnim; 196 int popExitAnim; 197 Op()198 Op() { 199 } 200 Op(int cmd, Fragment fragment)201 Op(int cmd, Fragment fragment) { 202 this.cmd = cmd; 203 this.fragment = fragment; 204 } 205 } 206 207 ArrayList<Op> mOps = new ArrayList<>(); 208 int mEnterAnim; 209 int mExitAnim; 210 int mPopEnterAnim; 211 int mPopExitAnim; 212 int mTransition; 213 int mTransitionStyle; 214 boolean mAddToBackStack; 215 boolean mAllowAddToBackStack = true; 216 String mName; 217 boolean mCommitted; 218 int mIndex = -1; 219 boolean mReorderingAllowed; 220 221 ArrayList<Runnable> mCommitRunnables; 222 223 int mBreadCrumbTitleRes; 224 CharSequence mBreadCrumbTitleText; 225 int mBreadCrumbShortTitleRes; 226 CharSequence mBreadCrumbShortTitleText; 227 228 ArrayList<String> mSharedElementSourceNames; 229 ArrayList<String> mSharedElementTargetNames; 230 231 @Override toString()232 public String toString() { 233 StringBuilder sb = new StringBuilder(128); 234 sb.append("BackStackEntry{"); 235 sb.append(Integer.toHexString(System.identityHashCode(this))); 236 if (mIndex >= 0) { 237 sb.append(" #"); 238 sb.append(mIndex); 239 } 240 if (mName != null) { 241 sb.append(" "); 242 sb.append(mName); 243 } 244 sb.append("}"); 245 return sb.toString(); 246 } 247 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)248 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 249 dump(prefix, writer, true); 250 } 251 dump(String prefix, PrintWriter writer, boolean full)252 void dump(String prefix, PrintWriter writer, boolean full) { 253 if (full) { 254 writer.print(prefix); 255 writer.print("mName="); 256 writer.print(mName); 257 writer.print(" mIndex="); 258 writer.print(mIndex); 259 writer.print(" mCommitted="); 260 writer.println(mCommitted); 261 if (mTransition != FragmentTransaction.TRANSIT_NONE) { 262 writer.print(prefix); 263 writer.print("mTransition=#"); 264 writer.print(Integer.toHexString(mTransition)); 265 writer.print(" mTransitionStyle=#"); 266 writer.println(Integer.toHexString(mTransitionStyle)); 267 } 268 if (mEnterAnim != 0 || mExitAnim != 0) { 269 writer.print(prefix); 270 writer.print("mEnterAnim=#"); 271 writer.print(Integer.toHexString(mEnterAnim)); 272 writer.print(" mExitAnim=#"); 273 writer.println(Integer.toHexString(mExitAnim)); 274 } 275 if (mPopEnterAnim != 0 || mPopExitAnim != 0) { 276 writer.print(prefix); 277 writer.print("mPopEnterAnim=#"); 278 writer.print(Integer.toHexString(mPopEnterAnim)); 279 writer.print(" mPopExitAnim=#"); 280 writer.println(Integer.toHexString(mPopExitAnim)); 281 } 282 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) { 283 writer.print(prefix); 284 writer.print("mBreadCrumbTitleRes=#"); 285 writer.print(Integer.toHexString(mBreadCrumbTitleRes)); 286 writer.print(" mBreadCrumbTitleText="); 287 writer.println(mBreadCrumbTitleText); 288 } 289 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) { 290 writer.print(prefix); 291 writer.print("mBreadCrumbShortTitleRes=#"); 292 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes)); 293 writer.print(" mBreadCrumbShortTitleText="); 294 writer.println(mBreadCrumbShortTitleText); 295 } 296 } 297 298 if (!mOps.isEmpty()) { 299 writer.print(prefix); 300 writer.println("Operations:"); 301 String innerPrefix = prefix + " "; 302 final int numOps = mOps.size(); 303 for (int opNum = 0; opNum < numOps; opNum++) { 304 final Op op = mOps.get(opNum); 305 String cmdStr; 306 switch (op.cmd) { 307 case OP_NULL: 308 cmdStr = "NULL"; 309 break; 310 case OP_ADD: 311 cmdStr = "ADD"; 312 break; 313 case OP_REPLACE: 314 cmdStr = "REPLACE"; 315 break; 316 case OP_REMOVE: 317 cmdStr = "REMOVE"; 318 break; 319 case OP_HIDE: 320 cmdStr = "HIDE"; 321 break; 322 case OP_SHOW: 323 cmdStr = "SHOW"; 324 break; 325 case OP_DETACH: 326 cmdStr = "DETACH"; 327 break; 328 case OP_ATTACH: 329 cmdStr = "ATTACH"; 330 break; 331 case OP_SET_PRIMARY_NAV: 332 cmdStr="SET_PRIMARY_NAV"; 333 break; 334 case OP_UNSET_PRIMARY_NAV: 335 cmdStr="UNSET_PRIMARY_NAV"; 336 break; 337 338 default: 339 cmdStr = "cmd=" + op.cmd; 340 break; 341 } 342 writer.print(prefix); 343 writer.print(" Op #"); 344 writer.print(opNum); 345 writer.print(": "); 346 writer.print(cmdStr); 347 writer.print(" "); 348 writer.println(op.fragment); 349 if (full) { 350 if (op.enterAnim != 0 || op.exitAnim != 0) { 351 writer.print(innerPrefix); 352 writer.print("enterAnim=#"); 353 writer.print(Integer.toHexString(op.enterAnim)); 354 writer.print(" exitAnim=#"); 355 writer.println(Integer.toHexString(op.exitAnim)); 356 } 357 if (op.popEnterAnim != 0 || op.popExitAnim != 0) { 358 writer.print(innerPrefix); 359 writer.print("popEnterAnim=#"); 360 writer.print(Integer.toHexString(op.popEnterAnim)); 361 writer.print(" popExitAnim=#"); 362 writer.println(Integer.toHexString(op.popExitAnim)); 363 } 364 } 365 } 366 } 367 } 368 BackStackRecord(FragmentManagerImpl manager)369 public BackStackRecord(FragmentManagerImpl manager) { 370 mManager = manager; 371 mReorderingAllowed = mManager.getTargetSdk() > Build.VERSION_CODES.N_MR1; 372 } 373 getId()374 public int getId() { 375 return mIndex; 376 } 377 getBreadCrumbTitleRes()378 public int getBreadCrumbTitleRes() { 379 return mBreadCrumbTitleRes; 380 } 381 getBreadCrumbShortTitleRes()382 public int getBreadCrumbShortTitleRes() { 383 return mBreadCrumbShortTitleRes; 384 } 385 getBreadCrumbTitle()386 public CharSequence getBreadCrumbTitle() { 387 if (mBreadCrumbTitleRes != 0 && mManager.mHost != null) { 388 return mManager.mHost.getContext().getText(mBreadCrumbTitleRes); 389 } 390 return mBreadCrumbTitleText; 391 } 392 getBreadCrumbShortTitle()393 public CharSequence getBreadCrumbShortTitle() { 394 if (mBreadCrumbShortTitleRes != 0 && mManager.mHost != null) { 395 return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes); 396 } 397 return mBreadCrumbShortTitleText; 398 } 399 addOp(Op op)400 void addOp(Op op) { 401 mOps.add(op); 402 op.enterAnim = mEnterAnim; 403 op.exitAnim = mExitAnim; 404 op.popEnterAnim = mPopEnterAnim; 405 op.popExitAnim = mPopExitAnim; 406 } 407 add(Fragment fragment, String tag)408 public FragmentTransaction add(Fragment fragment, String tag) { 409 doAddOp(0, fragment, tag, OP_ADD); 410 return this; 411 } 412 add(int containerViewId, Fragment fragment)413 public FragmentTransaction add(int containerViewId, Fragment fragment) { 414 doAddOp(containerViewId, fragment, null, OP_ADD); 415 return this; 416 } 417 add(int containerViewId, Fragment fragment, String tag)418 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { 419 doAddOp(containerViewId, fragment, tag, OP_ADD); 420 return this; 421 } 422 doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd)423 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { 424 if (mManager.getTargetSdk() > Build.VERSION_CODES.N_MR1) { 425 final Class fragmentClass = fragment.getClass(); 426 final int modifiers = fragmentClass.getModifiers(); 427 if ((fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers) 428 || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers)))) { 429 throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName() 430 + " must be a public static class to be properly recreated from" 431 + " instance state."); 432 } 433 } 434 fragment.mFragmentManager = mManager; 435 436 if (tag != null) { 437 if (fragment.mTag != null && !tag.equals(fragment.mTag)) { 438 throw new IllegalStateException("Can't change tag of fragment " 439 + fragment + ": was " + fragment.mTag 440 + " now " + tag); 441 } 442 fragment.mTag = tag; 443 } 444 445 if (containerViewId != 0) { 446 if (containerViewId == View.NO_ID) { 447 throw new IllegalArgumentException("Can't add fragment " 448 + fragment + " with tag " + tag + " to container view with no id"); 449 } 450 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) { 451 throw new IllegalStateException("Can't change container ID of fragment " 452 + fragment + ": was " + fragment.mFragmentId 453 + " now " + containerViewId); 454 } 455 fragment.mContainerId = fragment.mFragmentId = containerViewId; 456 } 457 458 addOp(new Op(opcmd, fragment)); 459 } 460 replace(int containerViewId, Fragment fragment)461 public FragmentTransaction replace(int containerViewId, Fragment fragment) { 462 return replace(containerViewId, fragment, null); 463 } 464 replace(int containerViewId, Fragment fragment, String tag)465 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) { 466 if (containerViewId == 0) { 467 throw new IllegalArgumentException("Must use non-zero containerViewId"); 468 } 469 470 doAddOp(containerViewId, fragment, tag, OP_REPLACE); 471 return this; 472 } 473 remove(Fragment fragment)474 public FragmentTransaction remove(Fragment fragment) { 475 addOp(new Op(OP_REMOVE, fragment)); 476 477 return this; 478 } 479 hide(Fragment fragment)480 public FragmentTransaction hide(Fragment fragment) { 481 addOp(new Op(OP_HIDE, fragment)); 482 483 return this; 484 } 485 show(Fragment fragment)486 public FragmentTransaction show(Fragment fragment) { 487 addOp(new Op(OP_SHOW, fragment)); 488 489 return this; 490 } 491 detach(Fragment fragment)492 public FragmentTransaction detach(Fragment fragment) { 493 addOp(new Op(OP_DETACH, fragment)); 494 495 return this; 496 } 497 attach(Fragment fragment)498 public FragmentTransaction attach(Fragment fragment) { 499 addOp(new Op(OP_ATTACH, fragment)); 500 501 return this; 502 } 503 setPrimaryNavigationFragment(Fragment fragment)504 public FragmentTransaction setPrimaryNavigationFragment(Fragment fragment) { 505 addOp(new Op(OP_SET_PRIMARY_NAV, fragment)); 506 507 return this; 508 } 509 setCustomAnimations(int enter, int exit)510 public FragmentTransaction setCustomAnimations(int enter, int exit) { 511 return setCustomAnimations(enter, exit, 0, 0); 512 } 513 setCustomAnimations(int enter, int exit, int popEnter, int popExit)514 public FragmentTransaction setCustomAnimations(int enter, int exit, 515 int popEnter, int popExit) { 516 mEnterAnim = enter; 517 mExitAnim = exit; 518 mPopEnterAnim = popEnter; 519 mPopExitAnim = popExit; 520 return this; 521 } 522 setTransition(int transition)523 public FragmentTransaction setTransition(int transition) { 524 mTransition = transition; 525 return this; 526 } 527 528 @Override addSharedElement(View sharedElement, String name)529 public FragmentTransaction addSharedElement(View sharedElement, String name) { 530 String transitionName = sharedElement.getTransitionName(); 531 if (transitionName == null) { 532 throw new IllegalArgumentException("Unique transitionNames are required for all" + 533 " sharedElements"); 534 } 535 if (mSharedElementSourceNames == null) { 536 mSharedElementSourceNames = new ArrayList<String>(); 537 mSharedElementTargetNames = new ArrayList<String>(); 538 } else if (mSharedElementTargetNames.contains(name)) { 539 throw new IllegalArgumentException("A shared element with the target name '" 540 + name + "' has already been added to the transaction."); 541 } else if (mSharedElementSourceNames.contains(transitionName)) { 542 throw new IllegalArgumentException("A shared element with the source name '" 543 + transitionName + " has already been added to the transaction."); 544 } 545 mSharedElementSourceNames.add(transitionName); 546 mSharedElementTargetNames.add(name); 547 return this; 548 } 549 setTransitionStyle(int styleRes)550 public FragmentTransaction setTransitionStyle(int styleRes) { 551 mTransitionStyle = styleRes; 552 return this; 553 } 554 addToBackStack(String name)555 public FragmentTransaction addToBackStack(String name) { 556 if (!mAllowAddToBackStack) { 557 throw new IllegalStateException( 558 "This FragmentTransaction is not allowed to be added to the back stack."); 559 } 560 mAddToBackStack = true; 561 mName = name; 562 return this; 563 } 564 isAddToBackStackAllowed()565 public boolean isAddToBackStackAllowed() { 566 return mAllowAddToBackStack; 567 } 568 disallowAddToBackStack()569 public FragmentTransaction disallowAddToBackStack() { 570 if (mAddToBackStack) { 571 throw new IllegalStateException( 572 "This transaction is already being added to the back stack"); 573 } 574 mAllowAddToBackStack = false; 575 return this; 576 } 577 setBreadCrumbTitle(int res)578 public FragmentTransaction setBreadCrumbTitle(int res) { 579 mBreadCrumbTitleRes = res; 580 mBreadCrumbTitleText = null; 581 return this; 582 } 583 setBreadCrumbTitle(CharSequence text)584 public FragmentTransaction setBreadCrumbTitle(CharSequence text) { 585 mBreadCrumbTitleRes = 0; 586 mBreadCrumbTitleText = text; 587 return this; 588 } 589 setBreadCrumbShortTitle(int res)590 public FragmentTransaction setBreadCrumbShortTitle(int res) { 591 mBreadCrumbShortTitleRes = res; 592 mBreadCrumbShortTitleText = null; 593 return this; 594 } 595 setBreadCrumbShortTitle(CharSequence text)596 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) { 597 mBreadCrumbShortTitleRes = 0; 598 mBreadCrumbShortTitleText = text; 599 return this; 600 } 601 bumpBackStackNesting(int amt)602 void bumpBackStackNesting(int amt) { 603 if (!mAddToBackStack) { 604 return; 605 } 606 if (FragmentManagerImpl.DEBUG) { 607 Log.v(TAG, "Bump nesting in " + this 608 + " by " + amt); 609 } 610 final int numOps = mOps.size(); 611 for (int opNum = 0; opNum < numOps; opNum++) { 612 final Op op = mOps.get(opNum); 613 if (op.fragment != null) { 614 op.fragment.mBackStackNesting += amt; 615 if (FragmentManagerImpl.DEBUG) { 616 Log.v(TAG, "Bump nesting of " 617 + op.fragment + " to " + op.fragment.mBackStackNesting); 618 } 619 } 620 } 621 } 622 623 @Override runOnCommit(Runnable runnable)624 public FragmentTransaction runOnCommit(Runnable runnable) { 625 if (runnable == null) { 626 throw new IllegalArgumentException("runnable cannot be null"); 627 } 628 disallowAddToBackStack(); 629 if (mCommitRunnables == null) { 630 mCommitRunnables = new ArrayList<>(); 631 } 632 mCommitRunnables.add(runnable); 633 return this; 634 } 635 runOnCommitRunnables()636 public void runOnCommitRunnables() { 637 if (mCommitRunnables != null) { 638 for (int i = 0, N = mCommitRunnables.size(); i < N; i++) { 639 mCommitRunnables.get(i).run(); 640 } 641 mCommitRunnables = null; 642 } 643 } 644 commit()645 public int commit() { 646 return commitInternal(false); 647 } 648 commitAllowingStateLoss()649 public int commitAllowingStateLoss() { 650 return commitInternal(true); 651 } 652 653 @Override commitNow()654 public void commitNow() { 655 disallowAddToBackStack(); 656 mManager.execSingleAction(this, false); 657 } 658 659 @Override commitNowAllowingStateLoss()660 public void commitNowAllowingStateLoss() { 661 disallowAddToBackStack(); 662 mManager.execSingleAction(this, true); 663 } 664 665 @Override setReorderingAllowed(boolean reorderingAllowed)666 public FragmentTransaction setReorderingAllowed(boolean reorderingAllowed) { 667 mReorderingAllowed = reorderingAllowed; 668 return this; 669 } 670 commitInternal(boolean allowStateLoss)671 int commitInternal(boolean allowStateLoss) { 672 if (mCommitted) { 673 throw new IllegalStateException("commit already called"); 674 } 675 if (FragmentManagerImpl.DEBUG) { 676 Log.v(TAG, "Commit: " + this); 677 LogWriter logw = new LogWriter(Log.VERBOSE, TAG); 678 PrintWriter pw = new FastPrintWriter(logw, false, 1024); 679 dump(" ", null, pw, null); 680 pw.flush(); 681 } 682 mCommitted = true; 683 if (mAddToBackStack) { 684 mIndex = mManager.allocBackStackIndex(this); 685 } else { 686 mIndex = -1; 687 } 688 mManager.enqueueAction(this, allowStateLoss); 689 return mIndex; 690 } 691 692 /** 693 * Implementation of {@link android.app.FragmentManagerImpl.OpGenerator}. 694 * This operation is added to the list of pending actions during {@link #commit()}, and 695 * will be executed on the UI thread to run this FragmentTransaction. 696 * 697 * @param records Modified to add this BackStackRecord 698 * @param isRecordPop Modified to add a false (this isn't a pop) 699 * @return true always because the records and isRecordPop will always be changed 700 */ 701 @Override generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop)702 public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) { 703 if (FragmentManagerImpl.DEBUG) { 704 Log.v(TAG, "Run: " + this); 705 } 706 707 records.add(this); 708 isRecordPop.add(false); 709 if (mAddToBackStack) { 710 mManager.addBackStackState(this); 711 } 712 return true; 713 } 714 interactsWith(int containerId)715 boolean interactsWith(int containerId) { 716 final int numOps = mOps.size(); 717 for (int opNum = 0; opNum < numOps; opNum++) { 718 final Op op = mOps.get(opNum); 719 final int fragContainer = op.fragment != null ? op.fragment.mContainerId : 0; 720 if (fragContainer != 0 && fragContainer == containerId) { 721 return true; 722 } 723 } 724 return false; 725 } 726 interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex)727 boolean interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex) { 728 if (endIndex == startIndex) { 729 return false; 730 } 731 final int numOps = mOps.size(); 732 int lastContainer = -1; 733 for (int opNum = 0; opNum < numOps; opNum++) { 734 final Op op = mOps.get(opNum); 735 final int container = op.fragment != null ? op.fragment.mContainerId : 0; 736 if (container != 0 && container != lastContainer) { 737 lastContainer = container; 738 for (int i = startIndex; i < endIndex; i++) { 739 BackStackRecord record = records.get(i); 740 final int numThoseOps = record.mOps.size(); 741 for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) { 742 final Op thatOp = record.mOps.get(thoseOpIndex); 743 final int thatContainer = thatOp.fragment != null 744 ? thatOp.fragment.mContainerId : 0; 745 if (thatContainer == container) { 746 return true; 747 } 748 } 749 } 750 } 751 } 752 return false; 753 } 754 755 /** 756 * Executes the operations contained within this transaction. The Fragment states will only 757 * be modified if optimizations are not allowed. 758 */ executeOps()759 void executeOps() { 760 final int numOps = mOps.size(); 761 for (int opNum = 0; opNum < numOps; opNum++) { 762 final Op op = mOps.get(opNum); 763 final Fragment f = op.fragment; 764 if (f != null) { 765 f.setNextTransition(mTransition, mTransitionStyle); 766 } 767 switch (op.cmd) { 768 case OP_ADD: 769 f.setNextAnim(op.enterAnim); 770 mManager.addFragment(f, false); 771 break; 772 case OP_REMOVE: 773 f.setNextAnim(op.exitAnim); 774 mManager.removeFragment(f); 775 break; 776 case OP_HIDE: 777 f.setNextAnim(op.exitAnim); 778 mManager.hideFragment(f); 779 break; 780 case OP_SHOW: 781 f.setNextAnim(op.enterAnim); 782 mManager.showFragment(f); 783 break; 784 case OP_DETACH: 785 f.setNextAnim(op.exitAnim); 786 mManager.detachFragment(f); 787 break; 788 case OP_ATTACH: 789 f.setNextAnim(op.enterAnim); 790 mManager.attachFragment(f); 791 break; 792 case OP_SET_PRIMARY_NAV: 793 mManager.setPrimaryNavigationFragment(f); 794 break; 795 case OP_UNSET_PRIMARY_NAV: 796 mManager.setPrimaryNavigationFragment(null); 797 break; 798 default: 799 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 800 } 801 if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) { 802 mManager.moveFragmentToExpectedState(f); 803 } 804 } 805 if (!mReorderingAllowed) { 806 // Added fragments are added at the end to comply with prior behavior. 807 mManager.moveToState(mManager.mCurState, true); 808 } 809 } 810 811 /** 812 * Reverses the execution of the operations within this transaction. The Fragment states will 813 * only be modified if optimizations are not allowed. 814 * 815 * @param moveToState {@code true} if added fragments should be moved to their final state 816 * in unoptimized transactions 817 */ executePopOps(boolean moveToState)818 void executePopOps(boolean moveToState) { 819 for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) { 820 final Op op = mOps.get(opNum); 821 Fragment f = op.fragment; 822 if (f != null) { 823 f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition), 824 mTransitionStyle); 825 } 826 switch (op.cmd) { 827 case OP_ADD: 828 f.setNextAnim(op.popExitAnim); 829 mManager.removeFragment(f); 830 break; 831 case OP_REMOVE: 832 f.setNextAnim(op.popEnterAnim); 833 mManager.addFragment(f, false); 834 break; 835 case OP_HIDE: 836 f.setNextAnim(op.popEnterAnim); 837 mManager.showFragment(f); 838 break; 839 case OP_SHOW: 840 f.setNextAnim(op.popExitAnim); 841 mManager.hideFragment(f); 842 break; 843 case OP_DETACH: 844 f.setNextAnim(op.popEnterAnim); 845 mManager.attachFragment(f); 846 break; 847 case OP_ATTACH: 848 f.setNextAnim(op.popExitAnim); 849 mManager.detachFragment(f); 850 break; 851 case OP_SET_PRIMARY_NAV: 852 mManager.setPrimaryNavigationFragment(null); 853 break; 854 case OP_UNSET_PRIMARY_NAV: 855 mManager.setPrimaryNavigationFragment(f); 856 break; 857 default: 858 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 859 } 860 if (!mReorderingAllowed && op.cmd != OP_REMOVE && f != null) { 861 mManager.moveFragmentToExpectedState(f); 862 } 863 } 864 if (!mReorderingAllowed && moveToState) { 865 mManager.moveToState(mManager.mCurState, true); 866 } 867 } 868 869 /** 870 * Expands all meta-ops into their more primitive equivalents. This must be called prior to 871 * {@link #executeOps()} or any other call that operations on mOps for forward navigation. 872 * It should not be called for pop/reverse navigation operations. 873 * 874 * <p>Removes all OP_REPLACE ops and replaces them with the proper add and remove 875 * operations that are equivalent to the replace.</p> 876 * 877 * <p>Adds OP_UNSET_PRIMARY_NAV ops to match OP_SET_PRIMARY_NAV, OP_REMOVE and OP_DETACH 878 * ops so that we can restore the old primary nav fragment later. Since callers call this 879 * method in a loop before running ops from several transactions at once, the caller should 880 * pass the return value from this method as the oldPrimaryNav parameter for the next call. 881 * The first call in such a loop should pass the value of 882 * {@link FragmentManager#getPrimaryNavigationFragment()}.</p> 883 * 884 * @param added Initialized to the fragments that are in the mManager.mAdded, this 885 * will be modified to contain the fragments that will be in mAdded 886 * after the execution ({@link #executeOps()}. 887 * @param oldPrimaryNav The tracked primary navigation fragment as of the beginning of 888 * this set of ops 889 * @return the new oldPrimaryNav fragment after this record's ops would be run 890 */ 891 @SuppressWarnings("ReferenceEquality") expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav)892 Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) { 893 for (int opNum = 0; opNum < mOps.size(); opNum++) { 894 final Op op = mOps.get(opNum); 895 switch (op.cmd) { 896 case OP_ADD: 897 case OP_ATTACH: 898 added.add(op.fragment); 899 break; 900 case OP_REMOVE: 901 case OP_DETACH: { 902 added.remove(op.fragment); 903 if (op.fragment == oldPrimaryNav) { 904 mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment)); 905 opNum++; 906 oldPrimaryNav = null; 907 } 908 } 909 break; 910 case OP_REPLACE: { 911 final Fragment f = op.fragment; 912 final int containerId = f.mContainerId; 913 boolean alreadyAdded = false; 914 for (int i = added.size() - 1; i >= 0; i--) { 915 final Fragment old = added.get(i); 916 if (old.mContainerId == containerId) { 917 if (old == f) { 918 alreadyAdded = true; 919 } else { 920 // This is duplicated from above since we only make 921 // a single pass for expanding ops. Unset any outgoing primary nav. 922 if (old == oldPrimaryNav) { 923 mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old)); 924 opNum++; 925 oldPrimaryNav = null; 926 } 927 final Op removeOp = new Op(OP_REMOVE, old); 928 removeOp.enterAnim = op.enterAnim; 929 removeOp.popEnterAnim = op.popEnterAnim; 930 removeOp.exitAnim = op.exitAnim; 931 removeOp.popExitAnim = op.popExitAnim; 932 mOps.add(opNum, removeOp); 933 added.remove(old); 934 opNum++; 935 } 936 } 937 } 938 if (alreadyAdded) { 939 mOps.remove(opNum); 940 opNum--; 941 } else { 942 op.cmd = OP_ADD; 943 added.add(f); 944 } 945 } 946 break; 947 case OP_SET_PRIMARY_NAV: { 948 // It's ok if this is null, that means we will restore to no active 949 // primary navigation fragment on a pop. 950 mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, oldPrimaryNav)); 951 opNum++; 952 // Will be set by the OP_SET_PRIMARY_NAV we inserted before when run 953 oldPrimaryNav = op.fragment; 954 } 955 break; 956 } 957 } 958 return oldPrimaryNav; 959 } 960 961 /** 962 * Removes fragments that are added or removed during a pop operation. 963 * 964 * @param added Initialized to the fragments that are in the mManager.mAdded, this 965 * will be modified to contain the fragments that will be in mAdded 966 * after the execution ({@link #executeOps()}. 967 */ trackAddedFragmentsInPop(ArrayList<Fragment> added)968 void trackAddedFragmentsInPop(ArrayList<Fragment> added) { 969 for (int opNum = 0; opNum < mOps.size(); opNum++) { 970 final Op op = mOps.get(opNum); 971 switch (op.cmd) { 972 case OP_ADD: 973 case OP_ATTACH: 974 added.remove(op.fragment); 975 break; 976 case OP_REMOVE: 977 case OP_DETACH: 978 added.add(op.fragment); 979 break; 980 } 981 } 982 } 983 isPostponed()984 boolean isPostponed() { 985 for (int opNum = 0; opNum < mOps.size(); opNum++) { 986 final Op op = mOps.get(opNum); 987 if (isFragmentPostponed(op)) { 988 return true; 989 } 990 } 991 return false; 992 } 993 setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener)994 void setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener) { 995 for (int opNum = 0; opNum < mOps.size(); opNum++) { 996 final Op op = mOps.get(opNum); 997 if (isFragmentPostponed(op)) { 998 op.fragment.setOnStartEnterTransitionListener(listener); 999 } 1000 } 1001 } 1002 isFragmentPostponed(Op op)1003 private static boolean isFragmentPostponed(Op op) { 1004 final Fragment fragment = op.fragment; 1005 return fragment != null && fragment.mAdded && fragment.mView != null && !fragment.mDetached 1006 && !fragment.mHidden && fragment.isPostponed(); 1007 } 1008 getName()1009 public String getName() { 1010 return mName; 1011 } 1012 getTransition()1013 public int getTransition() { 1014 return mTransition; 1015 } 1016 getTransitionStyle()1017 public int getTransitionStyle() { 1018 return mTransitionStyle; 1019 } 1020 isEmpty()1021 public boolean isEmpty() { 1022 return mOps.isEmpty(); 1023 } 1024 } 1025