BackStackRecord.java revision 4500be62dea3895a98336122a2944cc4ab024bc5
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v4.app; 18 19import android.os.Build; 20import android.os.Parcel; 21import android.os.Parcelable; 22import android.support.v4.util.LogWriter; 23import android.support.v4.util.Pair; 24import android.text.TextUtils; 25import android.util.Log; 26import android.view.View; 27import android.view.ViewGroup; 28 29import java.io.FileDescriptor; 30import java.io.PrintWriter; 31import java.util.ArrayList; 32 33final class BackStackState implements Parcelable { 34 final int[] mOps; 35 final int mTransition; 36 final int mTransitionStyle; 37 final String mName; 38 final int mIndex; 39 final int mBreadCrumbTitleRes; 40 final CharSequence mBreadCrumbTitleText; 41 final int mBreadCrumbShortTitleRes; 42 final CharSequence mBreadCrumbShortTitleText; 43 final int mCustomTransition; 44 final int mSceneRoot; 45 final ArrayList<String> mSharedElementSourceNames; 46 final ArrayList<String> mSharedElementTargetNames; 47 48 public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) { 49 int numRemoved = 0; 50 BackStackRecord.Op op = bse.mHead; 51 while (op != null) { 52 if (op.removed != null) numRemoved += op.removed.size(); 53 op = op.next; 54 } 55 mOps = new int[bse.mNumOp*7 + numRemoved]; 56 57 if (!bse.mAddToBackStack) { 58 throw new IllegalStateException("Not on back stack"); 59 } 60 61 op = bse.mHead; 62 int pos = 0; 63 while (op != null) { 64 mOps[pos++] = op.cmd; 65 mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1; 66 mOps[pos++] = op.enterAnim; 67 mOps[pos++] = op.exitAnim; 68 mOps[pos++] = op.popEnterAnim; 69 mOps[pos++] = op.popExitAnim; 70 if (op.removed != null) { 71 final int N = op.removed.size(); 72 mOps[pos++] = N; 73 for (int i=0; i<N; i++) { 74 mOps[pos++] = op.removed.get(i).mIndex; 75 } 76 } else { 77 mOps[pos++] = 0; 78 } 79 op = op.next; 80 } 81 mTransition = bse.mTransition; 82 mTransitionStyle = bse.mTransitionStyle; 83 mName = bse.mName; 84 mIndex = bse.mIndex; 85 mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes; 86 mBreadCrumbTitleText = bse.mBreadCrumbTitleText; 87 mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes; 88 mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText; 89 mCustomTransition = bse.mCustomTransition; 90 mSceneRoot = bse.mSceneRoot; 91 mSharedElementSourceNames = bse.mSharedElementSourceNames; 92 mSharedElementTargetNames = bse.mSharedElementTargetNames; 93 } 94 95 public BackStackState(Parcel in) { 96 mOps = in.createIntArray(); 97 mTransition = in.readInt(); 98 mTransitionStyle = in.readInt(); 99 mName = in.readString(); 100 mIndex = in.readInt(); 101 mBreadCrumbTitleRes = in.readInt(); 102 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 103 mBreadCrumbShortTitleRes = in.readInt(); 104 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 105 mCustomTransition = in.readInt(); 106 mSceneRoot = in.readInt(); 107 mSharedElementSourceNames = in.createStringArrayList(); 108 mSharedElementTargetNames = in.createStringArrayList(); 109 } 110 111 public BackStackRecord instantiate(FragmentManagerImpl fm) { 112 BackStackRecord bse = new BackStackRecord(fm); 113 int pos = 0; 114 int num = 0; 115 while (pos < mOps.length) { 116 BackStackRecord.Op op = new BackStackRecord.Op(); 117 op.cmd = mOps[pos++]; 118 if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, 119 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]); 120 int findex = mOps[pos++]; 121 if (findex >= 0) { 122 Fragment f = fm.mActive.get(findex); 123 op.fragment = f; 124 } else { 125 op.fragment = null; 126 } 127 op.enterAnim = mOps[pos++]; 128 op.exitAnim = mOps[pos++]; 129 op.popEnterAnim = mOps[pos++]; 130 op.popExitAnim = mOps[pos++]; 131 final int N = mOps[pos++]; 132 if (N > 0) { 133 op.removed = new ArrayList<Fragment>(N); 134 for (int i=0; i<N; i++) { 135 if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, 136 "Instantiate " + bse + " set remove fragment #" + mOps[pos]); 137 Fragment r = fm.mActive.get(mOps[pos++]); 138 op.removed.add(r); 139 } 140 } 141 bse.addOp(op); 142 num++; 143 } 144 bse.mTransition = mTransition; 145 bse.mTransitionStyle = mTransitionStyle; 146 bse.mName = mName; 147 bse.mIndex = mIndex; 148 bse.mAddToBackStack = true; 149 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes; 150 bse.mBreadCrumbTitleText = mBreadCrumbTitleText; 151 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes; 152 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText; 153 bse.mCustomTransition = mCustomTransition; 154 bse.mSceneRoot = mSceneRoot; 155 bse.mSharedElementSourceNames = mSharedElementSourceNames; 156 bse.mSharedElementTargetNames = mSharedElementTargetNames; 157 bse.bumpBackStackNesting(1); 158 return bse; 159 } 160 161 public int describeContents() { 162 return 0; 163 } 164 165 public void writeToParcel(Parcel dest, int flags) { 166 dest.writeIntArray(mOps); 167 dest.writeInt(mTransition); 168 dest.writeInt(mTransitionStyle); 169 dest.writeString(mName); 170 dest.writeInt(mIndex); 171 dest.writeInt(mBreadCrumbTitleRes); 172 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0); 173 dest.writeInt(mBreadCrumbShortTitleRes); 174 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0); 175 dest.writeInt(mCustomTransition); 176 dest.writeInt(mSceneRoot); 177 dest.writeStringList(mSharedElementSourceNames); 178 dest.writeStringList(mSharedElementTargetNames); 179 } 180 181 public static final Parcelable.Creator<BackStackState> CREATOR 182 = new Parcelable.Creator<BackStackState>() { 183 public BackStackState createFromParcel(Parcel in) { 184 return new BackStackState(in); 185 } 186 187 public BackStackState[] newArray(int size) { 188 return new BackStackState[size]; 189 } 190 }; 191} 192 193/** 194 * @hide Entry of an operation on the fragment back stack. 195 */ 196final class BackStackRecord extends FragmentTransaction implements 197 FragmentManager.BackStackEntry, Runnable { 198 static final String TAG = FragmentManagerImpl.TAG; 199 200 final FragmentManagerImpl mManager; 201 202 static final int OP_NULL = 0; 203 static final int OP_ADD = 1; 204 static final int OP_REPLACE = 2; 205 static final int OP_REMOVE = 3; 206 static final int OP_HIDE = 4; 207 static final int OP_SHOW = 5; 208 static final int OP_DETACH = 6; 209 static final int OP_ATTACH = 7; 210 211 static final class Op { 212 Op next; 213 Op prev; 214 int cmd; 215 Fragment fragment; 216 int enterAnim; 217 int exitAnim; 218 int popEnterAnim; 219 int popExitAnim; 220 ArrayList<Fragment> removed; 221 } 222 223 Op mHead; 224 Op mTail; 225 int mNumOp; 226 int mEnterAnim; 227 int mExitAnim; 228 int mPopEnterAnim; 229 int mPopExitAnim; 230 int mTransition; 231 int mTransitionStyle; 232 boolean mAddToBackStack; 233 boolean mAllowAddToBackStack = true; 234 String mName; 235 boolean mCommitted; 236 int mIndex = -1; 237 238 int mBreadCrumbTitleRes; 239 CharSequence mBreadCrumbTitleText; 240 int mBreadCrumbShortTitleRes; 241 CharSequence mBreadCrumbShortTitleText; 242 243 int mCustomTransition; 244 int mSceneRoot; 245 ArrayList<String> mSharedElementSourceNames; 246 ArrayList<String> mSharedElementTargetNames; 247 248 @Override 249 public String toString() { 250 StringBuilder sb = new StringBuilder(128); 251 sb.append("BackStackEntry{"); 252 sb.append(Integer.toHexString(System.identityHashCode(this))); 253 if (mIndex >= 0) { 254 sb.append(" #"); 255 sb.append(mIndex); 256 } 257 if (mName != null) { 258 sb.append(" "); 259 sb.append(mName); 260 } 261 sb.append("}"); 262 return sb.toString(); 263 } 264 265 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 266 dump(prefix, writer, true); 267 } 268 269 public void dump(String prefix, PrintWriter writer, boolean full) { 270 if (full) { 271 writer.print(prefix); writer.print("mName="); writer.print(mName); 272 writer.print(" mIndex="); writer.print(mIndex); 273 writer.print(" mCommitted="); writer.println(mCommitted); 274 if (mTransition != FragmentTransaction.TRANSIT_NONE) { 275 writer.print(prefix); writer.print("mTransition=#"); 276 writer.print(Integer.toHexString(mTransition)); 277 writer.print(" mTransitionStyle=#"); 278 writer.println(Integer.toHexString(mTransitionStyle)); 279 } 280 if (mEnterAnim != 0 || mExitAnim !=0) { 281 writer.print(prefix); writer.print("mEnterAnim=#"); 282 writer.print(Integer.toHexString(mEnterAnim)); 283 writer.print(" mExitAnim=#"); 284 writer.println(Integer.toHexString(mExitAnim)); 285 } 286 if (mPopEnterAnim != 0 || mPopExitAnim !=0) { 287 writer.print(prefix); writer.print("mPopEnterAnim=#"); 288 writer.print(Integer.toHexString(mPopEnterAnim)); 289 writer.print(" mPopExitAnim=#"); 290 writer.println(Integer.toHexString(mPopExitAnim)); 291 } 292 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) { 293 writer.print(prefix); writer.print("mBreadCrumbTitleRes=#"); 294 writer.print(Integer.toHexString(mBreadCrumbTitleRes)); 295 writer.print(" mBreadCrumbTitleText="); 296 writer.println(mBreadCrumbTitleText); 297 } 298 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) { 299 writer.print(prefix); writer.print("mBreadCrumbShortTitleRes=#"); 300 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes)); 301 writer.print(" mBreadCrumbShortTitleText="); 302 writer.println(mBreadCrumbShortTitleText); 303 } 304 } 305 306 if (mHead != null) { 307 writer.print(prefix); writer.println("Operations:"); 308 String innerPrefix = prefix + " "; 309 Op op = mHead; 310 int num = 0; 311 while (op != null) { 312 String cmdStr; 313 switch (op.cmd) { 314 case OP_NULL: cmdStr="NULL"; break; 315 case OP_ADD: cmdStr="ADD"; break; 316 case OP_REPLACE: cmdStr="REPLACE"; break; 317 case OP_REMOVE: cmdStr="REMOVE"; break; 318 case OP_HIDE: cmdStr="HIDE"; break; 319 case OP_SHOW: cmdStr="SHOW"; break; 320 case OP_DETACH: cmdStr="DETACH"; break; 321 case OP_ATTACH: cmdStr="ATTACH"; break; 322 default: cmdStr="cmd=" + op.cmd; break; 323 } 324 writer.print(prefix); writer.print(" Op #"); writer.print(num); 325 writer.print(": "); writer.print(cmdStr); 326 writer.print(" "); writer.println(op.fragment); 327 if (full) { 328 if (op.enterAnim != 0 || op.exitAnim != 0) { 329 writer.print(prefix); writer.print("enterAnim=#"); 330 writer.print(Integer.toHexString(op.enterAnim)); 331 writer.print(" exitAnim=#"); 332 writer.println(Integer.toHexString(op.exitAnim)); 333 } 334 if (op.popEnterAnim != 0 || op.popExitAnim != 0) { 335 writer.print(prefix); writer.print("popEnterAnim=#"); 336 writer.print(Integer.toHexString(op.popEnterAnim)); 337 writer.print(" popExitAnim=#"); 338 writer.println(Integer.toHexString(op.popExitAnim)); 339 } 340 } 341 if (op.removed != null && op.removed.size() > 0) { 342 for (int i=0; i<op.removed.size(); i++) { 343 writer.print(innerPrefix); 344 if (op.removed.size() == 1) { 345 writer.print("Removed: "); 346 } else { 347 if (i == 0) { 348 writer.println("Removed:"); 349 } 350 writer.print(innerPrefix); writer.print(" #"); writer.print(i); 351 writer.print(": "); 352 } 353 writer.println(op.removed.get(i)); 354 } 355 } 356 op = op.next; 357 num++; 358 } 359 } 360 } 361 362 public BackStackRecord(FragmentManagerImpl manager) { 363 mManager = manager; 364 } 365 366 public int getId() { 367 return mIndex; 368 } 369 370 public int getBreadCrumbTitleRes() { 371 return mBreadCrumbTitleRes; 372 } 373 374 public int getBreadCrumbShortTitleRes() { 375 return mBreadCrumbShortTitleRes; 376 } 377 378 public CharSequence getBreadCrumbTitle() { 379 if (mBreadCrumbTitleRes != 0) { 380 return mManager.mActivity.getText(mBreadCrumbTitleRes); 381 } 382 return mBreadCrumbTitleText; 383 } 384 385 public CharSequence getBreadCrumbShortTitle() { 386 if (mBreadCrumbShortTitleRes != 0) { 387 return mManager.mActivity.getText(mBreadCrumbShortTitleRes); 388 } 389 return mBreadCrumbShortTitleText; 390 } 391 392 void addOp(Op op) { 393 if (mHead == null) { 394 mHead = mTail = op; 395 } else { 396 op.prev = mTail; 397 mTail.next = op; 398 mTail = op; 399 } 400 op.enterAnim = mEnterAnim; 401 op.exitAnim = mExitAnim; 402 op.popEnterAnim = mPopEnterAnim; 403 op.popExitAnim = mPopExitAnim; 404 mNumOp++; 405 } 406 407 public FragmentTransaction add(Fragment fragment, String tag) { 408 doAddOp(0, fragment, tag, OP_ADD); 409 return this; 410 } 411 412 public FragmentTransaction add(int containerViewId, Fragment fragment) { 413 doAddOp(containerViewId, fragment, null, OP_ADD); 414 return this; 415 } 416 417 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { 418 doAddOp(containerViewId, fragment, tag, OP_ADD); 419 return this; 420 } 421 422 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { 423 fragment.mFragmentManager = mManager; 424 425 if (tag != null) { 426 if (fragment.mTag != null && !tag.equals(fragment.mTag)) { 427 throw new IllegalStateException("Can't change tag of fragment " 428 + fragment + ": was " + fragment.mTag 429 + " now " + tag); 430 } 431 fragment.mTag = tag; 432 } 433 434 if (containerViewId != 0) { 435 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) { 436 throw new IllegalStateException("Can't change container ID of fragment " 437 + fragment + ": was " + fragment.mFragmentId 438 + " now " + containerViewId); 439 } 440 fragment.mContainerId = fragment.mFragmentId = containerViewId; 441 } 442 443 Op op = new Op(); 444 op.cmd = opcmd; 445 op.fragment = fragment; 446 addOp(op); 447 } 448 449 public FragmentTransaction replace(int containerViewId, Fragment fragment) { 450 return replace(containerViewId, fragment, null); 451 } 452 453 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) { 454 if (containerViewId == 0) { 455 throw new IllegalArgumentException("Must use non-zero containerViewId"); 456 } 457 458 doAddOp(containerViewId, fragment, tag, OP_REPLACE); 459 return this; 460 } 461 462 public FragmentTransaction remove(Fragment fragment) { 463 Op op = new Op(); 464 op.cmd = OP_REMOVE; 465 op.fragment = fragment; 466 addOp(op); 467 468 return this; 469 } 470 471 public FragmentTransaction hide(Fragment fragment) { 472 Op op = new Op(); 473 op.cmd = OP_HIDE; 474 op.fragment = fragment; 475 addOp(op); 476 477 return this; 478 } 479 480 public FragmentTransaction show(Fragment fragment) { 481 Op op = new Op(); 482 op.cmd = OP_SHOW; 483 op.fragment = fragment; 484 addOp(op); 485 486 return this; 487 } 488 489 public FragmentTransaction detach(Fragment fragment) { 490 Op op = new Op(); 491 op.cmd = OP_DETACH; 492 op.fragment = fragment; 493 addOp(op); 494 495 return this; 496 } 497 498 public FragmentTransaction attach(Fragment fragment) { 499 Op op = new Op(); 500 op.cmd = OP_ATTACH; 501 op.fragment = fragment; 502 addOp(op); 503 504 return this; 505 } 506 507 public FragmentTransaction setCustomAnimations(int enter, int exit) { 508 return setCustomAnimations(enter, exit, 0, 0); 509 } 510 511 public FragmentTransaction setCustomAnimations(int enter, int exit, 512 int popEnter, int popExit) { 513 mEnterAnim = enter; 514 mExitAnim = exit; 515 mPopEnterAnim = popEnter; 516 mPopExitAnim = popExit; 517 return this; 518 } 519 520 public FragmentTransaction setTransition(int transition) { 521 mTransition = transition; 522 return this; 523 } 524 525 @Override 526 public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) { 527 mSceneRoot = sceneRootId; 528 mCustomTransition = transitionId; 529 return this; 530 } 531 532 @Override 533 public FragmentTransaction setSharedElement(View sharedElement, String name) { 534 if (Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) { 535 String transitionName = FragmentTransitionCompat21.getTransitionName(sharedElement); 536 if (transitionName == null) { 537 throw new IllegalArgumentException("Unique transitionNames are required for all" + 538 " sharedElements"); 539 } 540 mSharedElementSourceNames = new ArrayList<String>(1); 541 mSharedElementSourceNames.add(transitionName); 542 543 mSharedElementTargetNames = new ArrayList<String>(1); 544 mSharedElementTargetNames.add(name); 545 } 546 return this; 547 } 548 549 @Override 550 public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) { 551 if (sharedElements == null || sharedElements.length == 0) { 552 mSharedElementSourceNames = null; 553 mSharedElementTargetNames = null; 554 } else if (Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) { 555 ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length); 556 ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length); 557 for (int i = 0; i < sharedElements.length; i++) { 558 String transitionName = 559 FragmentTransitionCompat21.getTransitionName(sharedElements[i].first); 560 if (transitionName == null) { 561 throw new IllegalArgumentException("Unique transitionNames are required for all" 562 + " sharedElements"); 563 } 564 sourceNames.add(transitionName); 565 targetNames.add(sharedElements[i].second); 566 } 567 mSharedElementSourceNames = sourceNames; 568 mSharedElementTargetNames = targetNames; 569 } 570 return this; 571 } 572 573 public FragmentTransaction setTransitionStyle(int styleRes) { 574 mTransitionStyle = styleRes; 575 return this; 576 } 577 578 public FragmentTransaction addToBackStack(String name) { 579 if (!mAllowAddToBackStack) { 580 throw new IllegalStateException( 581 "This FragmentTransaction is not allowed to be added to the back stack."); 582 } 583 mAddToBackStack = true; 584 mName = name; 585 return this; 586 } 587 588 public boolean isAddToBackStackAllowed() { 589 return mAllowAddToBackStack; 590 } 591 592 public FragmentTransaction disallowAddToBackStack() { 593 if (mAddToBackStack) { 594 throw new IllegalStateException( 595 "This transaction is already being added to the back stack"); 596 } 597 mAllowAddToBackStack = false; 598 return this; 599 } 600 601 public FragmentTransaction setBreadCrumbTitle(int res) { 602 mBreadCrumbTitleRes = res; 603 mBreadCrumbTitleText = null; 604 return this; 605 } 606 607 public FragmentTransaction setBreadCrumbTitle(CharSequence text) { 608 mBreadCrumbTitleRes = 0; 609 mBreadCrumbTitleText = text; 610 return this; 611 } 612 613 public FragmentTransaction setBreadCrumbShortTitle(int res) { 614 mBreadCrumbShortTitleRes = res; 615 mBreadCrumbShortTitleText = null; 616 return this; 617 } 618 619 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) { 620 mBreadCrumbShortTitleRes = 0; 621 mBreadCrumbShortTitleText = text; 622 return this; 623 } 624 625 void bumpBackStackNesting(int amt) { 626 if (!mAddToBackStack) { 627 return; 628 } 629 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this 630 + " by " + amt); 631 Op op = mHead; 632 while (op != null) { 633 if (op.fragment != null) { 634 op.fragment.mBackStackNesting += amt; 635 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " 636 + op.fragment + " to " + op.fragment.mBackStackNesting); 637 } 638 if (op.removed != null) { 639 for (int i=op.removed.size()-1; i>=0; i--) { 640 Fragment r = op.removed.get(i); 641 r.mBackStackNesting += amt; 642 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " 643 + r + " to " + r.mBackStackNesting); 644 } 645 } 646 op = op.next; 647 } 648 } 649 650 public int commit() { 651 return commitInternal(false); 652 } 653 654 public int commitAllowingStateLoss() { 655 return commitInternal(true); 656 } 657 658 int commitInternal(boolean allowStateLoss) { 659 if (mCommitted) throw new IllegalStateException("commit already called"); 660 if (FragmentManagerImpl.DEBUG) { 661 Log.v(TAG, "Commit: " + this); 662 LogWriter logw = new LogWriter(TAG); 663 PrintWriter pw = new PrintWriter(logw); 664 dump(" ", null, pw, null); 665 } 666 mCommitted = true; 667 if (mAddToBackStack) { 668 mIndex = mManager.allocBackStackIndex(this); 669 } else { 670 mIndex = -1; 671 } 672 mManager.enqueueAction(this, allowStateLoss); 673 return mIndex; 674 } 675 676 public void run() { 677 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this); 678 679 if (mAddToBackStack) { 680 if (mIndex < 0) { 681 throw new IllegalStateException("addToBackStack() called after commit()"); 682 } 683 } 684 685 bumpBackStackNesting(1); 686 687 Object state = beginTransition(mSharedElementSourceNames, mSharedElementTargetNames); 688 689 int transitionStyle = state != null ? 0 : mTransitionStyle; 690 int transition = state != null ? 0 : mTransition; 691 Op op = mHead; 692 while (op != null) { 693 int enterAnim = state != null ? 0 : op.enterAnim; 694 int exitAnim = state != null ? 0 : op.exitAnim; 695 switch (op.cmd) { 696 case OP_ADD: { 697 Fragment f = op.fragment; 698 f.mNextAnim = enterAnim; 699 mManager.addFragment(f, false); 700 } break; 701 case OP_REPLACE: { 702 Fragment f = op.fragment; 703 if (mManager.mAdded != null) { 704 for (int i=0; i<mManager.mAdded.size(); i++) { 705 Fragment old = mManager.mAdded.get(i); 706 if (FragmentManagerImpl.DEBUG) Log.v(TAG, 707 "OP_REPLACE: adding=" + f + " old=" + old); 708 if (f == null || old.mContainerId == f.mContainerId) { 709 if (old == f) { 710 op.fragment = f = null; 711 } else { 712 if (op.removed == null) { 713 op.removed = new ArrayList<Fragment>(); 714 } 715 op.removed.add(old); 716 old.mNextAnim = exitAnim; 717 if (mAddToBackStack) { 718 old.mBackStackNesting += 1; 719 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " 720 + old + " to " + old.mBackStackNesting); 721 } 722 mManager.removeFragment(old, transition, transitionStyle); 723 } 724 } 725 } 726 } 727 if (f != null) { 728 f.mNextAnim = enterAnim; 729 mManager.addFragment(f, false); 730 } 731 } break; 732 case OP_REMOVE: { 733 Fragment f = op.fragment; 734 f.mNextAnim = exitAnim; 735 mManager.removeFragment(f, transition, transitionStyle); 736 } break; 737 case OP_HIDE: { 738 Fragment f = op.fragment; 739 f.mNextAnim = exitAnim; 740 mManager.hideFragment(f, transition, transitionStyle); 741 } break; 742 case OP_SHOW: { 743 Fragment f = op.fragment; 744 f.mNextAnim = enterAnim; 745 mManager.showFragment(f, transition, transitionStyle); 746 } break; 747 case OP_DETACH: { 748 Fragment f = op.fragment; 749 f.mNextAnim = exitAnim; 750 mManager.detachFragment(f, transition, transitionStyle); 751 } break; 752 case OP_ATTACH: { 753 Fragment f = op.fragment; 754 f.mNextAnim = enterAnim; 755 mManager.attachFragment(f, transition, transitionStyle); 756 } break; 757 default: { 758 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 759 } 760 } 761 762 op = op.next; 763 } 764 765 mManager.moveToState(mManager.mCurState, transition, transitionStyle, true); 766 767 if (mAddToBackStack) { 768 mManager.addBackStackState(this); 769 } 770 771 if (state != null) { 772 updateTransitionEndState(state, mSharedElementTargetNames); 773 } 774 } 775 776 private Object beginTransition(ArrayList<String> sourceNames, ArrayList<String> targetNames) { 777 Object state = null; 778 if ((Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) 779 && mCustomTransition != 0 && mSceneRoot != 0) { 780 View rootView = mManager.mContainer.findViewById(mSceneRoot); 781 if (!(rootView instanceof ViewGroup)) { 782 throw new IllegalArgumentException("SceneRoot is not a ViewGroup"); 783 } 784 ArrayList<View> hiddenFragmentViews = new ArrayList<View>(); 785 if (mManager.mAdded != null) { 786 for (int i = mManager.mAdded.size() - 1; i >= 0; i--) { 787 Fragment fragment = mManager.mAdded.get(i); 788 if (fragment.mView != null) { 789 if (fragment.mHidden) { 790 hiddenFragmentViews.add(fragment.mView); 791 } 792 } 793 } 794 } 795 state = FragmentTransitionCompat21.beginTransition(mManager.mActivity, 796 mCustomTransition, (ViewGroup) rootView, hiddenFragmentViews, 797 sourceNames, targetNames); 798 } 799 return state; 800 } 801 802 private void updateTransitionEndState(Object stateObj, ArrayList<String> targetNames) { 803 if ((Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) 804 && stateObj != null) { 805 ArrayList<View> hiddenFragmentViews = new ArrayList<View>(); 806 ArrayList<View> shownFragmentViews = new ArrayList<View>(); 807 if (mManager.mAdded != null) { 808 for (int i = mManager.mAdded.size() - 1; i >= 0; i--) { 809 Fragment fragment = mManager.mAdded.get(i); 810 if (fragment.mView != null) { 811 if (fragment.mHidden) { 812 hiddenFragmentViews.add(fragment.mView); 813 } else { 814 shownFragmentViews.add(fragment.mView); 815 } 816 } 817 } 818 } 819 FragmentTransitionCompat21.updateTransitionEndState(mManager.mActivity, 820 shownFragmentViews, hiddenFragmentViews, stateObj, targetNames); 821 } 822 } 823 824 public Object popFromBackStack(boolean doStateMove, Object state) { 825 if (FragmentManagerImpl.DEBUG) { 826 Log.v(TAG, "popFromBackStack: " + this); 827 LogWriter logw = new LogWriter(TAG); 828 PrintWriter pw = new PrintWriter(logw); 829 dump(" ", null, pw, null); 830 } 831 832 if (state == null) { 833 state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames); 834 } else if (Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) { 835 FragmentTransitionCompat21.setNameOverrides(state, mSharedElementTargetNames, 836 mSharedElementSourceNames); 837 } 838 839 bumpBackStackNesting(-1); 840 841 int transitionStyle = state != null ? 0 : mTransitionStyle; 842 int transition = state != null ? 0 : mTransition; 843 Op op = mTail; 844 while (op != null) { 845 int popEnterAnim = state != null ? 0 : op.popEnterAnim; 846 int popExitAnim= state != null ? 0 : op.popExitAnim; 847 switch (op.cmd) { 848 case OP_ADD: { 849 Fragment f = op.fragment; 850 f.mNextAnim = popExitAnim; 851 mManager.removeFragment(f, 852 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 853 } break; 854 case OP_REPLACE: { 855 Fragment f = op.fragment; 856 if (f != null) { 857 f.mNextAnim = popExitAnim; 858 mManager.removeFragment(f, 859 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 860 } 861 if (op.removed != null) { 862 for (int i=0; i<op.removed.size(); i++) { 863 Fragment old = op.removed.get(i); 864 old.mNextAnim = popEnterAnim; 865 mManager.addFragment(old, false); 866 } 867 } 868 } break; 869 case OP_REMOVE: { 870 Fragment f = op.fragment; 871 f.mNextAnim = popEnterAnim; 872 mManager.addFragment(f, false); 873 } break; 874 case OP_HIDE: { 875 Fragment f = op.fragment; 876 f.mNextAnim = popEnterAnim; 877 mManager.showFragment(f, 878 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 879 } break; 880 case OP_SHOW: { 881 Fragment f = op.fragment; 882 f.mNextAnim = popExitAnim; 883 mManager.hideFragment(f, 884 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 885 } break; 886 case OP_DETACH: { 887 Fragment f = op.fragment; 888 f.mNextAnim = popEnterAnim; 889 mManager.attachFragment(f, 890 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 891 } break; 892 case OP_ATTACH: { 893 Fragment f = op.fragment; 894 f.mNextAnim = popEnterAnim; 895 mManager.detachFragment(f, 896 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 897 } break; 898 default: { 899 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 900 } 901 } 902 903 op = op.prev; 904 } 905 906 if (doStateMove) { 907 mManager.moveToState(mManager.mCurState, 908 FragmentManagerImpl.reverseTransit(transition), transitionStyle, true); 909 if (state != null) { 910 updateTransitionEndState(state, mSharedElementSourceNames); 911 state = null; 912 } 913 } 914 915 if (mIndex >= 0) { 916 mManager.freeBackStackIndex(mIndex); 917 mIndex = -1; 918 } 919 return state; 920 } 921 922 public String getName() { 923 return mName; 924 } 925 926 public int getTransition() { 927 return mTransition; 928 } 929 930 public int getTransitionStyle() { 931 return mTransitionStyle; 932 } 933 934 public boolean isEmpty() { 935 return mNumOp == 0; 936 } 937} 938