BackStackRecord.java revision d4c3c91dd0757eec9703ef90ea4c5a7ee99f18ca
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.app; 18 19import com.android.internal.util.FastPrintWriter; 20 21import android.graphics.Rect; 22import android.os.Parcel; 23import android.os.Parcelable; 24import android.text.TextUtils; 25import android.transition.Transition; 26import android.transition.TransitionInflater; 27import android.transition.TransitionManager; 28import android.transition.TransitionSet; 29import android.util.ArrayMap; 30import android.util.Log; 31import android.util.LogWriter; 32import android.util.Pair; 33import android.view.View; 34import android.view.ViewGroup; 35 36import java.io.FileDescriptor; 37import java.io.PrintWriter; 38import java.util.ArrayList; 39import java.util.Collection; 40 41final class BackStackState implements Parcelable { 42 final int[] mOps; 43 final int mTransition; 44 final int mTransitionStyle; 45 final int mCustomTransition; 46 final int mSceneRoot; 47 final String mName; 48 final int mIndex; 49 final int mBreadCrumbTitleRes; 50 final CharSequence mBreadCrumbTitleText; 51 final int mBreadCrumbShortTitleRes; 52 final CharSequence mBreadCrumbShortTitleText; 53 final ArrayList<String> mSharedElementSourceNames; 54 final ArrayList<String> mSharedElementTargetNames; 55 56 public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) { 57 int numRemoved = 0; 58 BackStackRecord.Op op = bse.mHead; 59 while (op != null) { 60 if (op.removed != null) { 61 numRemoved += op.removed.size(); 62 } 63 op = op.next; 64 } 65 mOps = new int[bse.mNumOp * 7 + numRemoved]; 66 67 if (!bse.mAddToBackStack) { 68 throw new IllegalStateException("Not on back stack"); 69 } 70 71 op = bse.mHead; 72 int pos = 0; 73 while (op != null) { 74 mOps[pos++] = op.cmd; 75 mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1; 76 mOps[pos++] = op.enterAnim; 77 mOps[pos++] = op.exitAnim; 78 mOps[pos++] = op.popEnterAnim; 79 mOps[pos++] = op.popExitAnim; 80 if (op.removed != null) { 81 final int N = op.removed.size(); 82 mOps[pos++] = N; 83 for (int i = 0; i < N; i++) { 84 mOps[pos++] = op.removed.get(i).mIndex; 85 } 86 } else { 87 mOps[pos++] = 0; 88 } 89 op = op.next; 90 } 91 mTransition = bse.mTransition; 92 mTransitionStyle = bse.mTransitionStyle; 93 mName = bse.mName; 94 mIndex = bse.mIndex; 95 mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes; 96 mBreadCrumbTitleText = bse.mBreadCrumbTitleText; 97 mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes; 98 mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText; 99 mCustomTransition = bse.mCustomTransition; 100 mSceneRoot = bse.mSceneRoot; 101 mSharedElementSourceNames = bse.mSharedElementSourceNames; 102 mSharedElementTargetNames = bse.mSharedElementTargetNames; 103 } 104 105 public BackStackState(Parcel in) { 106 mOps = in.createIntArray(); 107 mTransition = in.readInt(); 108 mTransitionStyle = in.readInt(); 109 mName = in.readString(); 110 mIndex = in.readInt(); 111 mBreadCrumbTitleRes = in.readInt(); 112 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 113 mBreadCrumbShortTitleRes = in.readInt(); 114 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 115 mCustomTransition = in.readInt(); 116 mSceneRoot = in.readInt(); 117 mSharedElementSourceNames = in.createStringArrayList(); 118 mSharedElementTargetNames = in.createStringArrayList(); 119 } 120 121 public BackStackRecord instantiate(FragmentManagerImpl fm) { 122 BackStackRecord bse = new BackStackRecord(fm); 123 int pos = 0; 124 int num = 0; 125 while (pos < mOps.length) { 126 BackStackRecord.Op op = new BackStackRecord.Op(); 127 op.cmd = mOps[pos++]; 128 if (FragmentManagerImpl.DEBUG) { 129 Log.v(FragmentManagerImpl.TAG, 130 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]); 131 } 132 int findex = mOps[pos++]; 133 if (findex >= 0) { 134 Fragment f = fm.mActive.get(findex); 135 op.fragment = f; 136 } else { 137 op.fragment = null; 138 } 139 op.enterAnim = mOps[pos++]; 140 op.exitAnim = mOps[pos++]; 141 op.popEnterAnim = mOps[pos++]; 142 op.popExitAnim = mOps[pos++]; 143 final int N = mOps[pos++]; 144 if (N > 0) { 145 op.removed = new ArrayList<Fragment>(N); 146 for (int i = 0; i < N; i++) { 147 if (FragmentManagerImpl.DEBUG) { 148 Log.v(FragmentManagerImpl.TAG, 149 "Instantiate " + bse + " set remove fragment #" + mOps[pos]); 150 } 151 Fragment r = fm.mActive.get(mOps[pos++]); 152 op.removed.add(r); 153 } 154 } 155 bse.addOp(op); 156 num++; 157 } 158 bse.mTransition = mTransition; 159 bse.mTransitionStyle = mTransitionStyle; 160 bse.mName = mName; 161 bse.mIndex = mIndex; 162 bse.mAddToBackStack = true; 163 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes; 164 bse.mBreadCrumbTitleText = mBreadCrumbTitleText; 165 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes; 166 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText; 167 bse.mCustomTransition = mCustomTransition; 168 bse.mSceneRoot = mSceneRoot; 169 bse.mSharedElementSourceNames = mSharedElementSourceNames; 170 bse.mSharedElementTargetNames = mSharedElementTargetNames; 171 bse.bumpBackStackNesting(1); 172 return bse; 173 } 174 175 public int describeContents() { 176 return 0; 177 } 178 179 public void writeToParcel(Parcel dest, int flags) { 180 dest.writeIntArray(mOps); 181 dest.writeInt(mTransition); 182 dest.writeInt(mTransitionStyle); 183 dest.writeString(mName); 184 dest.writeInt(mIndex); 185 dest.writeInt(mBreadCrumbTitleRes); 186 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0); 187 dest.writeInt(mBreadCrumbShortTitleRes); 188 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0); 189 dest.writeInt(mCustomTransition); 190 dest.writeInt(mSceneRoot); 191 dest.writeStringList(mSharedElementSourceNames); 192 dest.writeStringList(mSharedElementTargetNames); 193 } 194 195 public static final Parcelable.Creator<BackStackState> CREATOR 196 = new Parcelable.Creator<BackStackState>() { 197 public BackStackState createFromParcel(Parcel in) { 198 return new BackStackState(in); 199 } 200 201 public BackStackState[] newArray(int size) { 202 return new BackStackState[size]; 203 } 204 }; 205} 206 207/** 208 * @hide Entry of an operation on the fragment back stack. 209 */ 210final class BackStackRecord extends FragmentTransaction implements 211 FragmentManager.BackStackEntry, Runnable { 212 static final String TAG = FragmentManagerImpl.TAG; 213 214 final FragmentManagerImpl mManager; 215 216 static final int OP_NULL = 0; 217 static final int OP_ADD = 1; 218 static final int OP_REPLACE = 2; 219 static final int OP_REMOVE = 3; 220 static final int OP_HIDE = 4; 221 static final int OP_SHOW = 5; 222 static final int OP_DETACH = 6; 223 static final int OP_ATTACH = 7; 224 225 static final class Op { 226 Op next; 227 Op prev; 228 int cmd; 229 Fragment fragment; 230 int enterAnim; 231 int exitAnim; 232 int popEnterAnim; 233 int popExitAnim; 234 ArrayList<Fragment> removed; 235 } 236 237 Op mHead; 238 Op mTail; 239 int mNumOp; 240 int mEnterAnim; 241 int mExitAnim; 242 int mPopEnterAnim; 243 int mPopExitAnim; 244 int mTransition; 245 int mTransitionStyle; 246 boolean mAddToBackStack; 247 boolean mAllowAddToBackStack = true; 248 String mName; 249 boolean mCommitted; 250 int mIndex = -1; 251 252 int mBreadCrumbTitleRes; 253 CharSequence mBreadCrumbTitleText; 254 int mBreadCrumbShortTitleRes; 255 CharSequence mBreadCrumbShortTitleText; 256 257 int mCustomTransition; 258 int mSceneRoot; 259 ArrayList<String> mSharedElementSourceNames; 260 ArrayList<String> mSharedElementTargetNames; 261 262 @Override 263 public String toString() { 264 StringBuilder sb = new StringBuilder(128); 265 sb.append("BackStackEntry{"); 266 sb.append(Integer.toHexString(System.identityHashCode(this))); 267 if (mIndex >= 0) { 268 sb.append(" #"); 269 sb.append(mIndex); 270 } 271 if (mName != null) { 272 sb.append(" "); 273 sb.append(mName); 274 } 275 sb.append("}"); 276 return sb.toString(); 277 } 278 279 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 280 dump(prefix, writer, true); 281 } 282 283 void dump(String prefix, PrintWriter writer, boolean full) { 284 if (full) { 285 writer.print(prefix); 286 writer.print("mName="); 287 writer.print(mName); 288 writer.print(" mIndex="); 289 writer.print(mIndex); 290 writer.print(" mCommitted="); 291 writer.println(mCommitted); 292 if (mTransition != FragmentTransaction.TRANSIT_NONE) { 293 writer.print(prefix); 294 writer.print("mTransition=#"); 295 writer.print(Integer.toHexString(mTransition)); 296 writer.print(" mTransitionStyle=#"); 297 writer.println(Integer.toHexString(mTransitionStyle)); 298 } 299 if (mEnterAnim != 0 || mExitAnim != 0) { 300 writer.print(prefix); 301 writer.print("mEnterAnim=#"); 302 writer.print(Integer.toHexString(mEnterAnim)); 303 writer.print(" mExitAnim=#"); 304 writer.println(Integer.toHexString(mExitAnim)); 305 } 306 if (mPopEnterAnim != 0 || mPopExitAnim != 0) { 307 writer.print(prefix); 308 writer.print("mPopEnterAnim=#"); 309 writer.print(Integer.toHexString(mPopEnterAnim)); 310 writer.print(" mPopExitAnim=#"); 311 writer.println(Integer.toHexString(mPopExitAnim)); 312 } 313 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) { 314 writer.print(prefix); 315 writer.print("mBreadCrumbTitleRes=#"); 316 writer.print(Integer.toHexString(mBreadCrumbTitleRes)); 317 writer.print(" mBreadCrumbTitleText="); 318 writer.println(mBreadCrumbTitleText); 319 } 320 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) { 321 writer.print(prefix); 322 writer.print("mBreadCrumbShortTitleRes=#"); 323 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes)); 324 writer.print(" mBreadCrumbShortTitleText="); 325 writer.println(mBreadCrumbShortTitleText); 326 } 327 } 328 329 if (mHead != null) { 330 writer.print(prefix); 331 writer.println("Operations:"); 332 String innerPrefix = prefix + " "; 333 Op op = mHead; 334 int num = 0; 335 while (op != null) { 336 String cmdStr; 337 switch (op.cmd) { 338 case OP_NULL: 339 cmdStr = "NULL"; 340 break; 341 case OP_ADD: 342 cmdStr = "ADD"; 343 break; 344 case OP_REPLACE: 345 cmdStr = "REPLACE"; 346 break; 347 case OP_REMOVE: 348 cmdStr = "REMOVE"; 349 break; 350 case OP_HIDE: 351 cmdStr = "HIDE"; 352 break; 353 case OP_SHOW: 354 cmdStr = "SHOW"; 355 break; 356 case OP_DETACH: 357 cmdStr = "DETACH"; 358 break; 359 case OP_ATTACH: 360 cmdStr = "ATTACH"; 361 break; 362 default: 363 cmdStr = "cmd=" + op.cmd; 364 break; 365 } 366 writer.print(prefix); 367 writer.print(" Op #"); 368 writer.print(num); 369 writer.print(": "); 370 writer.print(cmdStr); 371 writer.print(" "); 372 writer.println(op.fragment); 373 if (full) { 374 if (op.enterAnim != 0 || op.exitAnim != 0) { 375 writer.print(innerPrefix); 376 writer.print("enterAnim=#"); 377 writer.print(Integer.toHexString(op.enterAnim)); 378 writer.print(" exitAnim=#"); 379 writer.println(Integer.toHexString(op.exitAnim)); 380 } 381 if (op.popEnterAnim != 0 || op.popExitAnim != 0) { 382 writer.print(innerPrefix); 383 writer.print("popEnterAnim=#"); 384 writer.print(Integer.toHexString(op.popEnterAnim)); 385 writer.print(" popExitAnim=#"); 386 writer.println(Integer.toHexString(op.popExitAnim)); 387 } 388 } 389 if (op.removed != null && op.removed.size() > 0) { 390 for (int i = 0; i < op.removed.size(); i++) { 391 writer.print(innerPrefix); 392 if (op.removed.size() == 1) { 393 writer.print("Removed: "); 394 } else { 395 if (i == 0) { 396 writer.println("Removed:"); 397 } 398 writer.print(innerPrefix); 399 writer.print(" #"); 400 writer.print(i); 401 writer.print(": "); 402 } 403 writer.println(op.removed.get(i)); 404 } 405 } 406 op = op.next; 407 num++; 408 } 409 } 410 } 411 412 public BackStackRecord(FragmentManagerImpl manager) { 413 mManager = manager; 414 } 415 416 public int getId() { 417 return mIndex; 418 } 419 420 public int getBreadCrumbTitleRes() { 421 return mBreadCrumbTitleRes; 422 } 423 424 public int getBreadCrumbShortTitleRes() { 425 return mBreadCrumbShortTitleRes; 426 } 427 428 public CharSequence getBreadCrumbTitle() { 429 if (mBreadCrumbTitleRes != 0) { 430 return mManager.mActivity.getText(mBreadCrumbTitleRes); 431 } 432 return mBreadCrumbTitleText; 433 } 434 435 public CharSequence getBreadCrumbShortTitle() { 436 if (mBreadCrumbShortTitleRes != 0) { 437 return mManager.mActivity.getText(mBreadCrumbShortTitleRes); 438 } 439 return mBreadCrumbShortTitleText; 440 } 441 442 void addOp(Op op) { 443 if (mHead == null) { 444 mHead = mTail = op; 445 } else { 446 op.prev = mTail; 447 mTail.next = op; 448 mTail = op; 449 } 450 op.enterAnim = mEnterAnim; 451 op.exitAnim = mExitAnim; 452 op.popEnterAnim = mPopEnterAnim; 453 op.popExitAnim = mPopExitAnim; 454 mNumOp++; 455 } 456 457 public FragmentTransaction add(Fragment fragment, String tag) { 458 doAddOp(0, fragment, tag, OP_ADD); 459 return this; 460 } 461 462 public FragmentTransaction add(int containerViewId, Fragment fragment) { 463 doAddOp(containerViewId, fragment, null, OP_ADD); 464 return this; 465 } 466 467 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { 468 doAddOp(containerViewId, fragment, tag, OP_ADD); 469 return this; 470 } 471 472 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { 473 fragment.mFragmentManager = mManager; 474 475 if (tag != null) { 476 if (fragment.mTag != null && !tag.equals(fragment.mTag)) { 477 throw new IllegalStateException("Can't change tag of fragment " 478 + fragment + ": was " + fragment.mTag 479 + " now " + tag); 480 } 481 fragment.mTag = tag; 482 } 483 484 if (containerViewId != 0) { 485 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) { 486 throw new IllegalStateException("Can't change container ID of fragment " 487 + fragment + ": was " + fragment.mFragmentId 488 + " now " + containerViewId); 489 } 490 fragment.mContainerId = fragment.mFragmentId = containerViewId; 491 } 492 493 Op op = new Op(); 494 op.cmd = opcmd; 495 op.fragment = fragment; 496 addOp(op); 497 } 498 499 public FragmentTransaction replace(int containerViewId, Fragment fragment) { 500 return replace(containerViewId, fragment, null); 501 } 502 503 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) { 504 if (containerViewId == 0) { 505 throw new IllegalArgumentException("Must use non-zero containerViewId"); 506 } 507 508 doAddOp(containerViewId, fragment, tag, OP_REPLACE); 509 return this; 510 } 511 512 public FragmentTransaction remove(Fragment fragment) { 513 Op op = new Op(); 514 op.cmd = OP_REMOVE; 515 op.fragment = fragment; 516 addOp(op); 517 518 return this; 519 } 520 521 public FragmentTransaction hide(Fragment fragment) { 522 Op op = new Op(); 523 op.cmd = OP_HIDE; 524 op.fragment = fragment; 525 addOp(op); 526 527 return this; 528 } 529 530 public FragmentTransaction show(Fragment fragment) { 531 Op op = new Op(); 532 op.cmd = OP_SHOW; 533 op.fragment = fragment; 534 addOp(op); 535 536 return this; 537 } 538 539 public FragmentTransaction detach(Fragment fragment) { 540 Op op = new Op(); 541 op.cmd = OP_DETACH; 542 op.fragment = fragment; 543 addOp(op); 544 545 return this; 546 } 547 548 public FragmentTransaction attach(Fragment fragment) { 549 Op op = new Op(); 550 op.cmd = OP_ATTACH; 551 op.fragment = fragment; 552 addOp(op); 553 554 return this; 555 } 556 557 public FragmentTransaction setCustomAnimations(int enter, int exit) { 558 return setCustomAnimations(enter, exit, 0, 0); 559 } 560 561 public FragmentTransaction setCustomAnimations(int enter, int exit, 562 int popEnter, int popExit) { 563 mEnterAnim = enter; 564 mExitAnim = exit; 565 mPopEnterAnim = popEnter; 566 mPopExitAnim = popExit; 567 return this; 568 } 569 570 public FragmentTransaction setTransition(int transition) { 571 mTransition = transition; 572 return this; 573 } 574 575 @Override 576 public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) { 577 mSceneRoot = sceneRootId; 578 mCustomTransition = transitionId; 579 return this; 580 } 581 582 @Override 583 public FragmentTransaction setSharedElement(View sharedElement, String name) { 584 String viewName = sharedElement.getViewName(); 585 if (viewName == null) { 586 throw new IllegalArgumentException("Unique viewNames are required for all" + 587 " sharedElements"); 588 } 589 mSharedElementSourceNames = new ArrayList<String>(1); 590 mSharedElementSourceNames.add(viewName); 591 592 mSharedElementTargetNames = new ArrayList<String>(1); 593 mSharedElementTargetNames.add(name); 594 return this; 595 } 596 597 @Override 598 public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) { 599 if (sharedElements == null || sharedElements.length == 0) { 600 mSharedElementSourceNames = null; 601 mSharedElementTargetNames = null; 602 } else { 603 ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length); 604 ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length); 605 for (int i = 0; i < sharedElements.length; i++) { 606 String viewName = sharedElements[i].first.getViewName(); 607 if (viewName == null) { 608 throw new IllegalArgumentException("Unique viewNames are required for all" + 609 " sharedElements"); 610 } 611 sourceNames.add(viewName); 612 targetNames.add(sharedElements[i].second); 613 } 614 mSharedElementSourceNames = sourceNames; 615 mSharedElementTargetNames = targetNames; 616 } 617 return this; 618 } 619 620 public FragmentTransaction setTransitionStyle(int styleRes) { 621 mTransitionStyle = styleRes; 622 return this; 623 } 624 625 public FragmentTransaction addToBackStack(String name) { 626 if (!mAllowAddToBackStack) { 627 throw new IllegalStateException( 628 "This FragmentTransaction is not allowed to be added to the back stack."); 629 } 630 mAddToBackStack = true; 631 mName = name; 632 return this; 633 } 634 635 public boolean isAddToBackStackAllowed() { 636 return mAllowAddToBackStack; 637 } 638 639 public FragmentTransaction disallowAddToBackStack() { 640 if (mAddToBackStack) { 641 throw new IllegalStateException( 642 "This transaction is already being added to the back stack"); 643 } 644 mAllowAddToBackStack = false; 645 return this; 646 } 647 648 public FragmentTransaction setBreadCrumbTitle(int res) { 649 mBreadCrumbTitleRes = res; 650 mBreadCrumbTitleText = null; 651 return this; 652 } 653 654 public FragmentTransaction setBreadCrumbTitle(CharSequence text) { 655 mBreadCrumbTitleRes = 0; 656 mBreadCrumbTitleText = text; 657 return this; 658 } 659 660 public FragmentTransaction setBreadCrumbShortTitle(int res) { 661 mBreadCrumbShortTitleRes = res; 662 mBreadCrumbShortTitleText = null; 663 return this; 664 } 665 666 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) { 667 mBreadCrumbShortTitleRes = 0; 668 mBreadCrumbShortTitleText = text; 669 return this; 670 } 671 672 void bumpBackStackNesting(int amt) { 673 if (!mAddToBackStack) { 674 return; 675 } 676 if (FragmentManagerImpl.DEBUG) { 677 Log.v(TAG, "Bump nesting in " + this 678 + " by " + amt); 679 } 680 Op op = mHead; 681 while (op != null) { 682 if (op.fragment != null) { 683 op.fragment.mBackStackNesting += amt; 684 if (FragmentManagerImpl.DEBUG) { 685 Log.v(TAG, "Bump nesting of " 686 + op.fragment + " to " + op.fragment.mBackStackNesting); 687 } 688 } 689 if (op.removed != null) { 690 for (int i = op.removed.size() - 1; i >= 0; i--) { 691 Fragment r = op.removed.get(i); 692 r.mBackStackNesting += amt; 693 if (FragmentManagerImpl.DEBUG) { 694 Log.v(TAG, "Bump nesting of " 695 + r + " to " + r.mBackStackNesting); 696 } 697 } 698 } 699 op = op.next; 700 } 701 } 702 703 public int commit() { 704 return commitInternal(false); 705 } 706 707 public int commitAllowingStateLoss() { 708 return commitInternal(true); 709 } 710 711 int commitInternal(boolean allowStateLoss) { 712 if (mCommitted) { 713 throw new IllegalStateException("commit already called"); 714 } 715 if (FragmentManagerImpl.DEBUG) { 716 Log.v(TAG, "Commit: " + this); 717 LogWriter logw = new LogWriter(Log.VERBOSE, TAG); 718 PrintWriter pw = new FastPrintWriter(logw, false, 1024); 719 dump(" ", null, pw, null); 720 pw.flush(); 721 } 722 mCommitted = true; 723 if (mAddToBackStack) { 724 mIndex = mManager.allocBackStackIndex(this); 725 } else { 726 mIndex = -1; 727 } 728 mManager.enqueueAction(this, allowStateLoss); 729 return mIndex; 730 } 731 732 public void run() { 733 if (FragmentManagerImpl.DEBUG) { 734 Log.v(TAG, "Run: " + this); 735 } 736 737 if (mAddToBackStack) { 738 if (mIndex < 0) { 739 throw new IllegalStateException("addToBackStack() called after commit()"); 740 } 741 } 742 743 bumpBackStackNesting(1); 744 745 TransitionState state = beginTransition(mSharedElementSourceNames, 746 mSharedElementTargetNames); 747 748 Op op = mHead; 749 while (op != null) { 750 switch (op.cmd) { 751 case OP_ADD: { 752 Fragment f = op.fragment; 753 f.mNextAnim = op.enterAnim; 754 mManager.addFragment(f, false); 755 } 756 break; 757 case OP_REPLACE: { 758 Fragment f = op.fragment; 759 if (mManager.mAdded != null) { 760 for (int i = 0; i < mManager.mAdded.size(); i++) { 761 Fragment old = mManager.mAdded.get(i); 762 if (FragmentManagerImpl.DEBUG) { 763 Log.v(TAG, 764 "OP_REPLACE: adding=" + f + " old=" + old); 765 } 766 if (f == null || old.mContainerId == f.mContainerId) { 767 if (old == f) { 768 op.fragment = f = null; 769 } else { 770 if (op.removed == null) { 771 op.removed = new ArrayList<Fragment>(); 772 } 773 op.removed.add(old); 774 old.mNextAnim = op.exitAnim; 775 if (mAddToBackStack) { 776 old.mBackStackNesting += 1; 777 if (FragmentManagerImpl.DEBUG) { 778 Log.v(TAG, "Bump nesting of " 779 + old + " to " + old.mBackStackNesting); 780 } 781 } 782 mManager.removeFragment(old, mTransition, mTransitionStyle); 783 } 784 } 785 } 786 } 787 if (f != null) { 788 f.mNextAnim = op.enterAnim; 789 mManager.addFragment(f, false); 790 } 791 } 792 break; 793 case OP_REMOVE: { 794 Fragment f = op.fragment; 795 f.mNextAnim = op.exitAnim; 796 mManager.removeFragment(f, mTransition, mTransitionStyle); 797 } 798 break; 799 case OP_HIDE: { 800 Fragment f = op.fragment; 801 f.mNextAnim = op.exitAnim; 802 mManager.hideFragment(f, mTransition, mTransitionStyle); 803 } 804 break; 805 case OP_SHOW: { 806 Fragment f = op.fragment; 807 f.mNextAnim = op.enterAnim; 808 mManager.showFragment(f, mTransition, mTransitionStyle); 809 } 810 break; 811 case OP_DETACH: { 812 Fragment f = op.fragment; 813 f.mNextAnim = op.exitAnim; 814 mManager.detachFragment(f, mTransition, mTransitionStyle); 815 } 816 break; 817 case OP_ATTACH: { 818 Fragment f = op.fragment; 819 f.mNextAnim = op.enterAnim; 820 mManager.attachFragment(f, mTransition, mTransitionStyle); 821 } 822 break; 823 default: { 824 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 825 } 826 } 827 828 op = op.next; 829 } 830 831 mManager.moveToState(mManager.mCurState, mTransition, 832 mTransitionStyle, true); 833 834 if (mAddToBackStack) { 835 mManager.addBackStackState(this); 836 } 837 838 if (state != null) { 839 updateTransitionEndState(state, mSharedElementTargetNames); 840 } 841 } 842 843 private TransitionState beginTransition(ArrayList<String> sourceNames, 844 ArrayList<String> targetNames) { 845 if (mCustomTransition <= 0 || mSceneRoot <= 0) { 846 return null; 847 } 848 View rootView = mManager.mContainer.findViewById(mSceneRoot); 849 if (!(rootView instanceof ViewGroup)) { 850 throw new IllegalArgumentException("SceneRoot is not a ViewGroup"); 851 } 852 TransitionState state = new TransitionState(); 853 // get Transition scene root and create Transitions 854 state.sceneRoot = (ViewGroup) rootView; 855 state.sceneRoot.captureTransitioningViews(state.transitioningViews); 856 857 state.exitTransition = TransitionInflater.from(mManager.mActivity) 858 .inflateTransition(mCustomTransition); 859 state.sharedElementTransition = TransitionInflater.from(mManager.mActivity) 860 .inflateTransition(mCustomTransition); 861 state.enterTransition = TransitionInflater.from(mManager.mActivity) 862 .inflateTransition(mCustomTransition); 863 // Adding a non-existent target view makes sure that the transitions don't target 864 // any views by default. They'll only target the views we tell add. If we don't 865 // add any, then no views will be targeted. 866 View nonExistentView = new View(mManager.mActivity); 867 state.enterTransition.addTarget(nonExistentView); 868 state.exitTransition.addTarget(nonExistentView); 869 state.sharedElementTransition.addTarget(nonExistentView); 870 871 setSharedElementEpicenter(state.enterTransition, state); 872 873 state.excludingTransition = new TransitionSet() 874 .addTransition(state.exitTransition) 875 .addTransition(state.enterTransition); 876 877 if (sourceNames != null) { 878 // Map shared elements. 879 state.sceneRoot.findNamedViews(state.namedViews); 880 state.namedViews.retainAll(sourceNames); 881 View epicenterView = state.namedViews.get(sourceNames.get(0)); 882 if (epicenterView != null) { 883 // The epicenter is only the first shared element. 884 setEpicenter(state.exitTransition, epicenterView); 885 setEpicenter(state.sharedElementTransition, epicenterView); 886 } 887 state.transitioningViews.removeAll(state.namedViews.values()); 888 state.excludingTransition.addTransition(state.sharedElementTransition); 889 addTransitioningViews(state.sharedElementTransition, state.namedViews.values()); 890 } 891 892 // Adds the (maybe) exiting views, not including the shared element. 893 // If some stay, that's ok. 894 addTransitioningViews(state.exitTransition, state.transitioningViews); 895 896 // Prepare for shared element name mapping. This could be chained in the case 897 // of popping several back stack states. 898 state.excludingTransition.setNameOverrides(new ArrayMap<String, String>()); 899 setNameOverrides(state, sourceNames, targetNames); 900 901 // Don't include any subtree in the views that are hidden when capturing the 902 // view hierarchy transitions. They should be as if not there. 903 excludeHiddenFragments(state, true); 904 905 TransitionManager.beginDelayedTransition(state.sceneRoot, state.excludingTransition); 906 return state; 907 } 908 909 private void updateTransitionEndState(TransitionState state, ArrayList<String> names) { 910 // Find all views that are entering. 911 ArrayList<View> enteringViews = new ArrayList<View>(); 912 state.sceneRoot.captureTransitioningViews(enteringViews); 913 enteringViews.removeAll(state.transitioningViews); 914 915 if (names != null) { 916 // find all shared elements. 917 state.namedViews.clear(); 918 state.sceneRoot.findNamedViews(state.namedViews); 919 state.namedViews.retainAll(names); 920 if (!state.namedViews.isEmpty()) { 921 enteringViews.removeAll(state.namedViews.values()); 922 addTransitioningViews(state.sharedElementTransition, state.namedViews.values()); 923 // now we know the epicenter of the entering transition. 924 state.mEnteringEpicenterView = state.namedViews.get(names.get(0)); 925 } 926 } 927 928 // Add all entering views to the enter transition. 929 addTransitioningViews(state.enterTransition, enteringViews); 930 931 // Don't allow capturing state for the newly-hidden fragments. 932 excludeHiddenFragments(state, false); 933 934 // Allow capturing state for the newly-shown fragments 935 includeVisibleFragments(state.excludingTransition); 936 } 937 938 private void addTransitioningViews(Transition transition, Collection<View> views) { 939 if (views.isEmpty()) { 940 // Add a view so that we can modify the valid views at the end of the 941 // fragment transaction. 942 transition.addTarget(new View(mManager.mActivity)); 943 } else { 944 for (View view : views) { 945 transition.addTarget(view); 946 } 947 } 948 } 949 950 private void excludeHiddenFragments(TransitionState state, boolean forceExclude) { 951 if (mManager.mAdded != null) { 952 for (int i = 0; i < mManager.mAdded.size(); i++) { 953 Fragment fragment = mManager.mAdded.get(i); 954 if (fragment.mView != null && fragment.mHidden 955 && (forceExclude || !state.hiddenViews.contains(fragment.mView))) { 956 state.excludingTransition.excludeTarget(fragment.mView, true); 957 state.hiddenViews.add(fragment.mView); 958 } 959 } 960 } 961 if (forceExclude && state.hiddenViews.isEmpty()) { 962 state.excludingTransition.excludeTarget(new View(mManager.mActivity), true); 963 } 964 } 965 966 private void includeVisibleFragments(Transition transition) { 967 if (mManager.mAdded != null) { 968 for (int i = 0; i < mManager.mAdded.size(); i++) { 969 Fragment fragment = mManager.mAdded.get(i); 970 if (fragment.mView != null && !fragment.mHidden) { 971 transition.excludeTarget(fragment.mView, false); 972 } 973 } 974 } 975 } 976 977 private static void setEpicenter(Transition transition, View view) { 978 final Rect epicenter = new Rect(); 979 view.getBoundsOnScreen(epicenter); 980 981 transition.setEpicenterCallback(new Transition.EpicenterCallback() { 982 @Override 983 public Rect onGetEpicenter(Transition transition) { 984 return epicenter; 985 } 986 }); 987 } 988 989 private void setSharedElementEpicenter(Transition transition, final TransitionState state) { 990 transition.setEpicenterCallback(new Transition.EpicenterCallback() { 991 private Rect mEpicenter; 992 993 @Override 994 public Rect onGetEpicenter(Transition transition) { 995 if (mEpicenter == null && state.mEnteringEpicenterView != null) { 996 mEpicenter = new Rect(); 997 state.mEnteringEpicenterView.getBoundsOnScreen(mEpicenter); 998 } 999 return mEpicenter; 1000 } 1001 }); 1002 } 1003 1004 public TransitionState popFromBackStack(boolean doStateMove, TransitionState state) { 1005 if (FragmentManagerImpl.DEBUG) { 1006 Log.v(TAG, "popFromBackStack: " + this); 1007 LogWriter logw = new LogWriter(Log.VERBOSE, TAG); 1008 PrintWriter pw = new FastPrintWriter(logw, false, 1024); 1009 dump(" ", null, pw, null); 1010 pw.flush(); 1011 } 1012 1013 if (state == null) { 1014 state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames); 1015 } else { 1016 setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames); 1017 } 1018 1019 bumpBackStackNesting(-1); 1020 1021 Op op = mTail; 1022 while (op != null) { 1023 switch (op.cmd) { 1024 case OP_ADD: { 1025 Fragment f = op.fragment; 1026 f.mNextAnim = op.popExitAnim; 1027 mManager.removeFragment(f, 1028 FragmentManagerImpl.reverseTransit(mTransition), 1029 mTransitionStyle); 1030 } 1031 break; 1032 case OP_REPLACE: { 1033 Fragment f = op.fragment; 1034 if (f != null) { 1035 f.mNextAnim = op.popExitAnim; 1036 mManager.removeFragment(f, 1037 FragmentManagerImpl.reverseTransit(mTransition), 1038 mTransitionStyle); 1039 } 1040 if (op.removed != null) { 1041 for (int i = 0; i < op.removed.size(); i++) { 1042 Fragment old = op.removed.get(i); 1043 old.mNextAnim = op.popEnterAnim; 1044 mManager.addFragment(old, false); 1045 } 1046 } 1047 } 1048 break; 1049 case OP_REMOVE: { 1050 Fragment f = op.fragment; 1051 f.mNextAnim = op.popEnterAnim; 1052 mManager.addFragment(f, false); 1053 } 1054 break; 1055 case OP_HIDE: { 1056 Fragment f = op.fragment; 1057 f.mNextAnim = op.popEnterAnim; 1058 mManager.showFragment(f, 1059 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1060 } 1061 break; 1062 case OP_SHOW: { 1063 Fragment f = op.fragment; 1064 f.mNextAnim = op.popExitAnim; 1065 mManager.hideFragment(f, 1066 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1067 } 1068 break; 1069 case OP_DETACH: { 1070 Fragment f = op.fragment; 1071 f.mNextAnim = op.popEnterAnim; 1072 mManager.attachFragment(f, 1073 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1074 } 1075 break; 1076 case OP_ATTACH: { 1077 Fragment f = op.fragment; 1078 f.mNextAnim = op.popExitAnim; 1079 mManager.detachFragment(f, 1080 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1081 } 1082 break; 1083 default: { 1084 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 1085 } 1086 } 1087 1088 op = op.prev; 1089 } 1090 1091 if (doStateMove) { 1092 mManager.moveToState(mManager.mCurState, 1093 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true); 1094 if (state != null) { 1095 updateTransitionEndState(state, mSharedElementSourceNames); 1096 state = null; 1097 } 1098 } 1099 1100 if (mIndex >= 0) { 1101 mManager.freeBackStackIndex(mIndex); 1102 mIndex = -1; 1103 } 1104 return state; 1105 } 1106 1107 private static void setNameOverride(Transition transition, String source, String target) { 1108 ArrayMap<String, String> overrides = transition.getNameOverrides(); 1109 for (int index = 0; index < overrides.size(); index++) { 1110 if (source.equals(overrides.valueAt(index))) { 1111 overrides.setValueAt(index, target); 1112 return; 1113 } 1114 } 1115 overrides.put(source, target); 1116 } 1117 1118 private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames, 1119 ArrayList<String> targetNames) { 1120 if (sourceNames != null) { 1121 for (int i = 0; i < sourceNames.size(); i++) { 1122 String source = sourceNames.get(i); 1123 String target = targetNames.get(i); 1124 setNameOverride(state.excludingTransition, source, target); 1125 } 1126 } 1127 } 1128 1129 public String getName() { 1130 return mName; 1131 } 1132 1133 public int getTransition() { 1134 return mTransition; 1135 } 1136 1137 public int getTransitionStyle() { 1138 return mTransitionStyle; 1139 } 1140 1141 public boolean isEmpty() { 1142 return mNumOp == 0; 1143 } 1144 1145 public class TransitionState { 1146 public ArrayList<View> hiddenViews = new ArrayList<View>(); 1147 public ArrayList<View> transitioningViews = new ArrayList<View>(); 1148 public ArrayMap<String, View> namedViews = new ArrayMap<String, View>(); 1149 public Transition exitTransition; 1150 public Transition sharedElementTransition; 1151 public Transition enterTransition; 1152 public TransitionSet excludingTransition; 1153 public ViewGroup sceneRoot; 1154 public View mEnteringEpicenterView; 1155 } 1156} 1157