BackStackRecord.java revision 6558056e8fccc32f9e1dc59e46d09f8d916b7538
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.TransitionManager; 27import android.transition.TransitionSet; 28import android.transition.TransitionUtils; 29import android.util.ArrayMap; 30import android.util.Log; 31import android.util.LogWriter; 32import android.util.Pair; 33import android.util.SparseArray; 34import android.view.View; 35import android.view.ViewGroup; 36import android.view.ViewTreeObserver; 37 38import java.io.FileDescriptor; 39import java.io.PrintWriter; 40import java.util.ArrayList; 41import java.util.Collection; 42 43final class BackStackState implements Parcelable { 44 final int[] mOps; 45 final int mTransition; 46 final int mTransitionStyle; 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 mSharedElementSourceNames = bse.mSharedElementSourceNames; 100 mSharedElementTargetNames = bse.mSharedElementTargetNames; 101 } 102 103 public BackStackState(Parcel in) { 104 mOps = in.createIntArray(); 105 mTransition = in.readInt(); 106 mTransitionStyle = in.readInt(); 107 mName = in.readString(); 108 mIndex = in.readInt(); 109 mBreadCrumbTitleRes = in.readInt(); 110 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 111 mBreadCrumbShortTitleRes = in.readInt(); 112 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 113 mSharedElementSourceNames = in.createStringArrayList(); 114 mSharedElementTargetNames = in.createStringArrayList(); 115 } 116 117 public BackStackRecord instantiate(FragmentManagerImpl fm) { 118 BackStackRecord bse = new BackStackRecord(fm); 119 int pos = 0; 120 int num = 0; 121 while (pos < mOps.length) { 122 BackStackRecord.Op op = new BackStackRecord.Op(); 123 op.cmd = mOps[pos++]; 124 if (FragmentManagerImpl.DEBUG) { 125 Log.v(FragmentManagerImpl.TAG, 126 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]); 127 } 128 int findex = mOps[pos++]; 129 if (findex >= 0) { 130 Fragment f = fm.mActive.get(findex); 131 op.fragment = f; 132 } else { 133 op.fragment = null; 134 } 135 op.enterAnim = mOps[pos++]; 136 op.exitAnim = mOps[pos++]; 137 op.popEnterAnim = mOps[pos++]; 138 op.popExitAnim = mOps[pos++]; 139 final int N = mOps[pos++]; 140 if (N > 0) { 141 op.removed = new ArrayList<Fragment>(N); 142 for (int i = 0; i < N; i++) { 143 if (FragmentManagerImpl.DEBUG) { 144 Log.v(FragmentManagerImpl.TAG, 145 "Instantiate " + bse + " set remove fragment #" + mOps[pos]); 146 } 147 Fragment r = fm.mActive.get(mOps[pos++]); 148 op.removed.add(r); 149 } 150 } 151 bse.addOp(op); 152 num++; 153 } 154 bse.mTransition = mTransition; 155 bse.mTransitionStyle = mTransitionStyle; 156 bse.mName = mName; 157 bse.mIndex = mIndex; 158 bse.mAddToBackStack = true; 159 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes; 160 bse.mBreadCrumbTitleText = mBreadCrumbTitleText; 161 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes; 162 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText; 163 bse.mSharedElementSourceNames = mSharedElementSourceNames; 164 bse.mSharedElementTargetNames = mSharedElementTargetNames; 165 bse.bumpBackStackNesting(1); 166 return bse; 167 } 168 169 public int describeContents() { 170 return 0; 171 } 172 173 public void writeToParcel(Parcel dest, int flags) { 174 dest.writeIntArray(mOps); 175 dest.writeInt(mTransition); 176 dest.writeInt(mTransitionStyle); 177 dest.writeString(mName); 178 dest.writeInt(mIndex); 179 dest.writeInt(mBreadCrumbTitleRes); 180 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0); 181 dest.writeInt(mBreadCrumbShortTitleRes); 182 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0); 183 dest.writeStringList(mSharedElementSourceNames); 184 dest.writeStringList(mSharedElementTargetNames); 185 } 186 187 public static final Parcelable.Creator<BackStackState> CREATOR 188 = new Parcelable.Creator<BackStackState>() { 189 public BackStackState createFromParcel(Parcel in) { 190 return new BackStackState(in); 191 } 192 193 public BackStackState[] newArray(int size) { 194 return new BackStackState[size]; 195 } 196 }; 197} 198 199/** 200 * @hide Entry of an operation on the fragment back stack. 201 */ 202final class BackStackRecord extends FragmentTransaction implements 203 FragmentManager.BackStackEntry, Runnable { 204 static final String TAG = FragmentManagerImpl.TAG; 205 206 final FragmentManagerImpl mManager; 207 208 static final int OP_NULL = 0; 209 static final int OP_ADD = 1; 210 static final int OP_REPLACE = 2; 211 static final int OP_REMOVE = 3; 212 static final int OP_HIDE = 4; 213 static final int OP_SHOW = 5; 214 static final int OP_DETACH = 6; 215 static final int OP_ATTACH = 7; 216 217 static final class Op { 218 Op next; 219 Op prev; 220 int cmd; 221 Fragment fragment; 222 int enterAnim; 223 int exitAnim; 224 int popEnterAnim; 225 int popExitAnim; 226 ArrayList<Fragment> removed; 227 } 228 229 Op mHead; 230 Op mTail; 231 int mNumOp; 232 int mEnterAnim; 233 int mExitAnim; 234 int mPopEnterAnim; 235 int mPopExitAnim; 236 int mTransition; 237 int mTransitionStyle; 238 boolean mAddToBackStack; 239 boolean mAllowAddToBackStack = true; 240 String mName; 241 boolean mCommitted; 242 int mIndex = -1; 243 244 int mBreadCrumbTitleRes; 245 CharSequence mBreadCrumbTitleText; 246 int mBreadCrumbShortTitleRes; 247 CharSequence mBreadCrumbShortTitleText; 248 249 ArrayList<String> mSharedElementSourceNames; 250 ArrayList<String> mSharedElementTargetNames; 251 252 @Override 253 public String toString() { 254 StringBuilder sb = new StringBuilder(128); 255 sb.append("BackStackEntry{"); 256 sb.append(Integer.toHexString(System.identityHashCode(this))); 257 if (mIndex >= 0) { 258 sb.append(" #"); 259 sb.append(mIndex); 260 } 261 if (mName != null) { 262 sb.append(" "); 263 sb.append(mName); 264 } 265 sb.append("}"); 266 return sb.toString(); 267 } 268 269 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 270 dump(prefix, writer, true); 271 } 272 273 void dump(String prefix, PrintWriter writer, boolean full) { 274 if (full) { 275 writer.print(prefix); 276 writer.print("mName="); 277 writer.print(mName); 278 writer.print(" mIndex="); 279 writer.print(mIndex); 280 writer.print(" mCommitted="); 281 writer.println(mCommitted); 282 if (mTransition != FragmentTransaction.TRANSIT_NONE) { 283 writer.print(prefix); 284 writer.print("mTransition=#"); 285 writer.print(Integer.toHexString(mTransition)); 286 writer.print(" mTransitionStyle=#"); 287 writer.println(Integer.toHexString(mTransitionStyle)); 288 } 289 if (mEnterAnim != 0 || mExitAnim != 0) { 290 writer.print(prefix); 291 writer.print("mEnterAnim=#"); 292 writer.print(Integer.toHexString(mEnterAnim)); 293 writer.print(" mExitAnim=#"); 294 writer.println(Integer.toHexString(mExitAnim)); 295 } 296 if (mPopEnterAnim != 0 || mPopExitAnim != 0) { 297 writer.print(prefix); 298 writer.print("mPopEnterAnim=#"); 299 writer.print(Integer.toHexString(mPopEnterAnim)); 300 writer.print(" mPopExitAnim=#"); 301 writer.println(Integer.toHexString(mPopExitAnim)); 302 } 303 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) { 304 writer.print(prefix); 305 writer.print("mBreadCrumbTitleRes=#"); 306 writer.print(Integer.toHexString(mBreadCrumbTitleRes)); 307 writer.print(" mBreadCrumbTitleText="); 308 writer.println(mBreadCrumbTitleText); 309 } 310 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) { 311 writer.print(prefix); 312 writer.print("mBreadCrumbShortTitleRes=#"); 313 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes)); 314 writer.print(" mBreadCrumbShortTitleText="); 315 writer.println(mBreadCrumbShortTitleText); 316 } 317 } 318 319 if (mHead != null) { 320 writer.print(prefix); 321 writer.println("Operations:"); 322 String innerPrefix = prefix + " "; 323 Op op = mHead; 324 int num = 0; 325 while (op != null) { 326 String cmdStr; 327 switch (op.cmd) { 328 case OP_NULL: 329 cmdStr = "NULL"; 330 break; 331 case OP_ADD: 332 cmdStr = "ADD"; 333 break; 334 case OP_REPLACE: 335 cmdStr = "REPLACE"; 336 break; 337 case OP_REMOVE: 338 cmdStr = "REMOVE"; 339 break; 340 case OP_HIDE: 341 cmdStr = "HIDE"; 342 break; 343 case OP_SHOW: 344 cmdStr = "SHOW"; 345 break; 346 case OP_DETACH: 347 cmdStr = "DETACH"; 348 break; 349 case OP_ATTACH: 350 cmdStr = "ATTACH"; 351 break; 352 default: 353 cmdStr = "cmd=" + op.cmd; 354 break; 355 } 356 writer.print(prefix); 357 writer.print(" Op #"); 358 writer.print(num); 359 writer.print(": "); 360 writer.print(cmdStr); 361 writer.print(" "); 362 writer.println(op.fragment); 363 if (full) { 364 if (op.enterAnim != 0 || op.exitAnim != 0) { 365 writer.print(innerPrefix); 366 writer.print("enterAnim=#"); 367 writer.print(Integer.toHexString(op.enterAnim)); 368 writer.print(" exitAnim=#"); 369 writer.println(Integer.toHexString(op.exitAnim)); 370 } 371 if (op.popEnterAnim != 0 || op.popExitAnim != 0) { 372 writer.print(innerPrefix); 373 writer.print("popEnterAnim=#"); 374 writer.print(Integer.toHexString(op.popEnterAnim)); 375 writer.print(" popExitAnim=#"); 376 writer.println(Integer.toHexString(op.popExitAnim)); 377 } 378 } 379 if (op.removed != null && op.removed.size() > 0) { 380 for (int i = 0; i < op.removed.size(); i++) { 381 writer.print(innerPrefix); 382 if (op.removed.size() == 1) { 383 writer.print("Removed: "); 384 } else { 385 if (i == 0) { 386 writer.println("Removed:"); 387 } 388 writer.print(innerPrefix); 389 writer.print(" #"); 390 writer.print(i); 391 writer.print(": "); 392 } 393 writer.println(op.removed.get(i)); 394 } 395 } 396 op = op.next; 397 num++; 398 } 399 } 400 } 401 402 public BackStackRecord(FragmentManagerImpl manager) { 403 mManager = manager; 404 } 405 406 public int getId() { 407 return mIndex; 408 } 409 410 public int getBreadCrumbTitleRes() { 411 return mBreadCrumbTitleRes; 412 } 413 414 public int getBreadCrumbShortTitleRes() { 415 return mBreadCrumbShortTitleRes; 416 } 417 418 public CharSequence getBreadCrumbTitle() { 419 if (mBreadCrumbTitleRes != 0) { 420 return mManager.mActivity.getText(mBreadCrumbTitleRes); 421 } 422 return mBreadCrumbTitleText; 423 } 424 425 public CharSequence getBreadCrumbShortTitle() { 426 if (mBreadCrumbShortTitleRes != 0) { 427 return mManager.mActivity.getText(mBreadCrumbShortTitleRes); 428 } 429 return mBreadCrumbShortTitleText; 430 } 431 432 void addOp(Op op) { 433 if (mHead == null) { 434 mHead = mTail = op; 435 } else { 436 op.prev = mTail; 437 mTail.next = op; 438 mTail = op; 439 } 440 op.enterAnim = mEnterAnim; 441 op.exitAnim = mExitAnim; 442 op.popEnterAnim = mPopEnterAnim; 443 op.popExitAnim = mPopExitAnim; 444 mNumOp++; 445 } 446 447 public FragmentTransaction add(Fragment fragment, String tag) { 448 doAddOp(0, fragment, tag, OP_ADD); 449 return this; 450 } 451 452 public FragmentTransaction add(int containerViewId, Fragment fragment) { 453 doAddOp(containerViewId, fragment, null, OP_ADD); 454 return this; 455 } 456 457 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { 458 doAddOp(containerViewId, fragment, tag, OP_ADD); 459 return this; 460 } 461 462 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { 463 fragment.mFragmentManager = mManager; 464 465 if (tag != null) { 466 if (fragment.mTag != null && !tag.equals(fragment.mTag)) { 467 throw new IllegalStateException("Can't change tag of fragment " 468 + fragment + ": was " + fragment.mTag 469 + " now " + tag); 470 } 471 fragment.mTag = tag; 472 } 473 474 if (containerViewId != 0) { 475 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) { 476 throw new IllegalStateException("Can't change container ID of fragment " 477 + fragment + ": was " + fragment.mFragmentId 478 + " now " + containerViewId); 479 } 480 fragment.mContainerId = fragment.mFragmentId = containerViewId; 481 } 482 483 Op op = new Op(); 484 op.cmd = opcmd; 485 op.fragment = fragment; 486 addOp(op); 487 } 488 489 public FragmentTransaction replace(int containerViewId, Fragment fragment) { 490 return replace(containerViewId, fragment, null); 491 } 492 493 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) { 494 if (containerViewId == 0) { 495 throw new IllegalArgumentException("Must use non-zero containerViewId"); 496 } 497 498 doAddOp(containerViewId, fragment, tag, OP_REPLACE); 499 return this; 500 } 501 502 public FragmentTransaction remove(Fragment fragment) { 503 Op op = new Op(); 504 op.cmd = OP_REMOVE; 505 op.fragment = fragment; 506 addOp(op); 507 508 return this; 509 } 510 511 public FragmentTransaction hide(Fragment fragment) { 512 Op op = new Op(); 513 op.cmd = OP_HIDE; 514 op.fragment = fragment; 515 addOp(op); 516 517 return this; 518 } 519 520 public FragmentTransaction show(Fragment fragment) { 521 Op op = new Op(); 522 op.cmd = OP_SHOW; 523 op.fragment = fragment; 524 addOp(op); 525 526 return this; 527 } 528 529 public FragmentTransaction detach(Fragment fragment) { 530 Op op = new Op(); 531 op.cmd = OP_DETACH; 532 op.fragment = fragment; 533 addOp(op); 534 535 return this; 536 } 537 538 public FragmentTransaction attach(Fragment fragment) { 539 Op op = new Op(); 540 op.cmd = OP_ATTACH; 541 op.fragment = fragment; 542 addOp(op); 543 544 return this; 545 } 546 547 public FragmentTransaction setCustomAnimations(int enter, int exit) { 548 return setCustomAnimations(enter, exit, 0, 0); 549 } 550 551 public FragmentTransaction setCustomAnimations(int enter, int exit, 552 int popEnter, int popExit) { 553 mEnterAnim = enter; 554 mExitAnim = exit; 555 mPopEnterAnim = popEnter; 556 mPopExitAnim = popExit; 557 return this; 558 } 559 560 public FragmentTransaction setTransition(int transition) { 561 mTransition = transition; 562 return this; 563 } 564 565 @Override 566 public FragmentTransaction addSharedElement(View sharedElement, String name) { 567 String transitionName = sharedElement.getTransitionName(); 568 if (transitionName == null) { 569 throw new IllegalArgumentException("Unique transitionNames are required for all" + 570 " sharedElements"); 571 } 572 if (mSharedElementSourceNames == null) { 573 mSharedElementSourceNames = new ArrayList<String>(); 574 mSharedElementTargetNames = new ArrayList<String>(); 575 } 576 mSharedElementSourceNames.add(transitionName); 577 mSharedElementTargetNames.add(name); 578 return this; 579 } 580 581 /** TODO: remove this */ 582 @Override 583 public FragmentTransaction setSharedElement(View sharedElement, String name) { 584 String transitionName = sharedElement.getTransitionName(); 585 if (transitionName == null) { 586 throw new IllegalArgumentException("Unique transitionNames are required for all" + 587 " sharedElements"); 588 } 589 mSharedElementSourceNames = new ArrayList<String>(1); 590 mSharedElementSourceNames.add(transitionName); 591 592 mSharedElementTargetNames = new ArrayList<String>(1); 593 mSharedElementTargetNames.add(name); 594 return this; 595 } 596 597 /** TODO: remove this */ 598 @Override 599 public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) { 600 if (sharedElements == null || sharedElements.length == 0) { 601 mSharedElementSourceNames = null; 602 mSharedElementTargetNames = null; 603 } else { 604 ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length); 605 ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length); 606 for (int i = 0; i < sharedElements.length; i++) { 607 String transitionName = sharedElements[i].first.getTransitionName(); 608 if (transitionName == null) { 609 throw new IllegalArgumentException("Unique transitionNames are required for all" 610 + " sharedElements"); 611 } 612 sourceNames.add(transitionName); 613 targetNames.add(sharedElements[i].second); 614 } 615 mSharedElementSourceNames = sourceNames; 616 mSharedElementTargetNames = targetNames; 617 } 618 return this; 619 } 620 621 public FragmentTransaction setTransitionStyle(int styleRes) { 622 mTransitionStyle = styleRes; 623 return this; 624 } 625 626 public FragmentTransaction addToBackStack(String name) { 627 if (!mAllowAddToBackStack) { 628 throw new IllegalStateException( 629 "This FragmentTransaction is not allowed to be added to the back stack."); 630 } 631 mAddToBackStack = true; 632 mName = name; 633 return this; 634 } 635 636 public boolean isAddToBackStackAllowed() { 637 return mAllowAddToBackStack; 638 } 639 640 public FragmentTransaction disallowAddToBackStack() { 641 if (mAddToBackStack) { 642 throw new IllegalStateException( 643 "This transaction is already being added to the back stack"); 644 } 645 mAllowAddToBackStack = false; 646 return this; 647 } 648 649 public FragmentTransaction setBreadCrumbTitle(int res) { 650 mBreadCrumbTitleRes = res; 651 mBreadCrumbTitleText = null; 652 return this; 653 } 654 655 public FragmentTransaction setBreadCrumbTitle(CharSequence text) { 656 mBreadCrumbTitleRes = 0; 657 mBreadCrumbTitleText = text; 658 return this; 659 } 660 661 public FragmentTransaction setBreadCrumbShortTitle(int res) { 662 mBreadCrumbShortTitleRes = res; 663 mBreadCrumbShortTitleText = null; 664 return this; 665 } 666 667 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) { 668 mBreadCrumbShortTitleRes = 0; 669 mBreadCrumbShortTitleText = text; 670 return this; 671 } 672 673 void bumpBackStackNesting(int amt) { 674 if (!mAddToBackStack) { 675 return; 676 } 677 if (FragmentManagerImpl.DEBUG) { 678 Log.v(TAG, "Bump nesting in " + this 679 + " by " + amt); 680 } 681 Op op = mHead; 682 while (op != null) { 683 if (op.fragment != null) { 684 op.fragment.mBackStackNesting += amt; 685 if (FragmentManagerImpl.DEBUG) { 686 Log.v(TAG, "Bump nesting of " 687 + op.fragment + " to " + op.fragment.mBackStackNesting); 688 } 689 } 690 if (op.removed != null) { 691 for (int i = op.removed.size() - 1; i >= 0; i--) { 692 Fragment r = op.removed.get(i); 693 r.mBackStackNesting += amt; 694 if (FragmentManagerImpl.DEBUG) { 695 Log.v(TAG, "Bump nesting of " 696 + r + " to " + r.mBackStackNesting); 697 } 698 } 699 } 700 op = op.next; 701 } 702 } 703 704 public int commit() { 705 return commitInternal(false); 706 } 707 708 public int commitAllowingStateLoss() { 709 return commitInternal(true); 710 } 711 712 int commitInternal(boolean allowStateLoss) { 713 if (mCommitted) { 714 throw new IllegalStateException("commit already called"); 715 } 716 if (FragmentManagerImpl.DEBUG) { 717 Log.v(TAG, "Commit: " + this); 718 LogWriter logw = new LogWriter(Log.VERBOSE, TAG); 719 PrintWriter pw = new FastPrintWriter(logw, false, 1024); 720 dump(" ", null, pw, null); 721 pw.flush(); 722 } 723 mCommitted = true; 724 if (mAddToBackStack) { 725 mIndex = mManager.allocBackStackIndex(this); 726 } else { 727 mIndex = -1; 728 } 729 mManager.enqueueAction(this, allowStateLoss); 730 return mIndex; 731 } 732 733 public void run() { 734 if (FragmentManagerImpl.DEBUG) { 735 Log.v(TAG, "Run: " + this); 736 } 737 738 if (mAddToBackStack) { 739 if (mIndex < 0) { 740 throw new IllegalStateException("addToBackStack() called after commit()"); 741 } 742 } 743 744 bumpBackStackNesting(1); 745 746 SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>(); 747 SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>(); 748 749 calculateFragments(firstOutFragments, lastInFragments); 750 751 TransitionState state = null; 752 if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) { 753 state = beginTransition(firstOutFragments, lastInFragments, false); 754 } 755 756 Op op = mHead; 757 while (op != null) { 758 switch (op.cmd) { 759 case OP_ADD: { 760 Fragment f = op.fragment; 761 f.mNextAnim = op.enterAnim; 762 mManager.addFragment(f, false); 763 } 764 break; 765 case OP_REPLACE: { 766 Fragment f = op.fragment; 767 if (mManager.mAdded != null) { 768 for (int i = 0; i < mManager.mAdded.size(); i++) { 769 Fragment old = mManager.mAdded.get(i); 770 if (FragmentManagerImpl.DEBUG) { 771 Log.v(TAG, 772 "OP_REPLACE: adding=" + f + " old=" + old); 773 } 774 if (f == null || old.mContainerId == f.mContainerId) { 775 if (old == f) { 776 op.fragment = f = null; 777 } else { 778 if (op.removed == null) { 779 op.removed = new ArrayList<Fragment>(); 780 } 781 op.removed.add(old); 782 old.mNextAnim = op.exitAnim; 783 if (mAddToBackStack) { 784 old.mBackStackNesting += 1; 785 if (FragmentManagerImpl.DEBUG) { 786 Log.v(TAG, "Bump nesting of " 787 + old + " to " + old.mBackStackNesting); 788 } 789 } 790 mManager.removeFragment(old, mTransition, mTransitionStyle); 791 } 792 } 793 } 794 } 795 if (f != null) { 796 f.mNextAnim = op.enterAnim; 797 mManager.addFragment(f, false); 798 } 799 } 800 break; 801 case OP_REMOVE: { 802 Fragment f = op.fragment; 803 f.mNextAnim = op.exitAnim; 804 mManager.removeFragment(f, mTransition, mTransitionStyle); 805 } 806 break; 807 case OP_HIDE: { 808 Fragment f = op.fragment; 809 f.mNextAnim = op.exitAnim; 810 mManager.hideFragment(f, mTransition, mTransitionStyle); 811 } 812 break; 813 case OP_SHOW: { 814 Fragment f = op.fragment; 815 f.mNextAnim = op.enterAnim; 816 mManager.showFragment(f, mTransition, mTransitionStyle); 817 } 818 break; 819 case OP_DETACH: { 820 Fragment f = op.fragment; 821 f.mNextAnim = op.exitAnim; 822 mManager.detachFragment(f, mTransition, mTransitionStyle); 823 } 824 break; 825 case OP_ATTACH: { 826 Fragment f = op.fragment; 827 f.mNextAnim = op.enterAnim; 828 mManager.attachFragment(f, mTransition, mTransitionStyle); 829 } 830 break; 831 default: { 832 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 833 } 834 } 835 836 op = op.next; 837 } 838 839 mManager.moveToState(mManager.mCurState, mTransition, 840 mTransitionStyle, true); 841 842 if (mAddToBackStack) { 843 mManager.addBackStackState(this); 844 } 845 846 if (state != null) { 847 updateTransitionEndState(state, firstOutFragments, lastInFragments, false); 848 } 849 } 850 851 private static void setFirstOut(SparseArray<Fragment> fragments, Fragment fragment) { 852 if (fragment != null) { 853 int containerId = fragment.mContainerId; 854 if (containerId != 0 && !fragment.isHidden() && fragment.isAdded() && 855 fragment.getView() != null && fragments.get(containerId) == null) { 856 fragments.put(containerId, fragment); 857 } 858 } 859 } 860 861 private void setLastIn(SparseArray<Fragment> fragments, Fragment fragment) { 862 if (fragment != null) { 863 int containerId = fragment.mContainerId; 864 if (containerId != 0) { 865 fragments.put(containerId, fragment); 866 } 867 } 868 } 869 870 /** 871 * Finds the first removed fragment and last added fragments when going forward. 872 * If none of the fragments have transitions, then both lists will be empty. 873 * 874 * @param firstOutFragments The list of first fragments to be removed, keyed on the 875 * container ID. This list will be modified by the method. 876 * @param lastInFragments The list of last fragments to be added, keyed on the 877 * container ID. This list will be modified by the method. 878 */ 879 private void calculateFragments(SparseArray<Fragment> firstOutFragments, 880 SparseArray<Fragment> lastInFragments) { 881 Op op = mHead; 882 while (op != null) { 883 switch (op.cmd) { 884 case OP_ADD: 885 setLastIn(lastInFragments, op.fragment); 886 break; 887 case OP_REPLACE: { 888 Fragment f = op.fragment; 889 if (mManager.mAdded != null) { 890 for (int i = 0; i < mManager.mAdded.size(); i++) { 891 Fragment old = mManager.mAdded.get(i); 892 if (f == null || old.mContainerId == f.mContainerId) { 893 if (old == f) { 894 f = null; 895 } else { 896 setFirstOut(firstOutFragments, old); 897 } 898 } 899 } 900 } 901 setLastIn(lastInFragments, f); 902 break; 903 } 904 case OP_REMOVE: 905 setFirstOut(firstOutFragments, op.fragment); 906 break; 907 case OP_HIDE: 908 setFirstOut(firstOutFragments, op.fragment); 909 break; 910 case OP_SHOW: 911 setLastIn(lastInFragments, op.fragment); 912 break; 913 case OP_DETACH: 914 setFirstOut(firstOutFragments, op.fragment); 915 break; 916 case OP_ATTACH: 917 setLastIn(lastInFragments, op.fragment); 918 break; 919 } 920 921 op = op.next; 922 } 923 924 if (!haveTransitions(firstOutFragments, lastInFragments, false)) { 925 firstOutFragments.clear(); 926 lastInFragments.clear(); 927 } 928 } 929 930 /** 931 * @return true if custom transitions exist on any fragment in firstOutFragments or 932 * lastInFragments or false otherwise. 933 */ 934 private static boolean haveTransitions(SparseArray<Fragment> firstOutFragments, 935 SparseArray<Fragment> lastInFragments, boolean isBack) { 936 for (int i = firstOutFragments.size() - 1; i >= 0; i--) { 937 Fragment f = firstOutFragments.valueAt(i); 938 if (isBack) { 939 if (f.getReturnTransition() != null || 940 f.getSharedElementReturnTransition() != null) { 941 return true; 942 } 943 } else if (f.getExitTransition() != null) { 944 return true; 945 } 946 } 947 948 for (int i = lastInFragments.size() - 1; i >= 0; i--) { 949 Fragment f = lastInFragments.valueAt(i); 950 if (isBack) { 951 if (f.getReenterTransition() != null) { 952 return true; 953 } 954 } else if (f.getEnterTransition() != null || 955 f.getSharedElementEnterTransition() != null) { 956 return true; 957 } 958 } 959 return false; 960 } 961 962 /** 963 * Finds the first removed fragment and last added fragments when popping the back stack. 964 * If none of the fragments have transitions, then both lists will be empty. 965 * 966 * @param firstOutFragments The list of first fragments to be removed, keyed on the 967 * container ID. This list will be modified by the method. 968 * @param lastInFragments The list of last fragments to be added, keyed on the 969 * container ID. This list will be modified by the method. 970 */ 971 public void calculateBackFragments(SparseArray<Fragment> firstOutFragments, 972 SparseArray<Fragment> lastInFragments) { 973 Op op = mHead; 974 while (op != null) { 975 switch (op.cmd) { 976 case OP_ADD: 977 setFirstOut(firstOutFragments, op.fragment); 978 break; 979 case OP_REPLACE: 980 if (op.removed != null) { 981 for (int i = op.removed.size() - 1; i >= 0; i--) { 982 setLastIn(lastInFragments, op.removed.get(i)); 983 } 984 } 985 setFirstOut(firstOutFragments, op.fragment); 986 break; 987 case OP_REMOVE: 988 setLastIn(lastInFragments, op.fragment); 989 break; 990 case OP_HIDE: 991 setLastIn(lastInFragments, op.fragment); 992 break; 993 case OP_SHOW: 994 setFirstOut(firstOutFragments, op.fragment); 995 break; 996 case OP_DETACH: 997 setLastIn(lastInFragments, op.fragment); 998 break; 999 case OP_ATTACH: 1000 setFirstOut(firstOutFragments, op.fragment); 1001 break; 1002 } 1003 1004 op = op.next; 1005 } 1006 1007 if (!haveTransitions(firstOutFragments, lastInFragments, true)) { 1008 firstOutFragments.clear(); 1009 lastInFragments.clear(); 1010 } 1011 } 1012 1013 /** 1014 * When custom fragment transitions are used, this sets up the state for each transition 1015 * and begins the transition. A different transition is started for each fragment container 1016 * and consists of up to 3 different transitions: the exit transition, a shared element 1017 * transition and an enter transition. 1018 * 1019 * <p>The exit transition operates against the leaf nodes of the first fragment 1020 * with a view that was removed. If no such fragment was removed, then no exit 1021 * transition is executed. The exit transition comes from the outgoing fragment.</p> 1022 * 1023 * <p>The enter transition operates against the last fragment that was added. If 1024 * that fragment does not have a view or no fragment was added, then no enter 1025 * transition is executed. The enter transition comes from the incoming fragment.</p> 1026 * 1027 * <p>The shared element transition operates against all views and comes either 1028 * from the outgoing fragment or the incoming fragment, depending on whether this 1029 * is going forward or popping the back stack. When going forward, the incoming 1030 * fragment's enter shared element transition is used, but when going back, the 1031 * outgoing fragment's return shared element transition is used. Shared element 1032 * transitions only operate if there is both an incoming and outgoing fragment.</p> 1033 * 1034 * @param firstOutFragments The list of first fragments to be removed, keyed on the 1035 * container ID. 1036 * @param lastInFragments The list of last fragments to be added, keyed on the 1037 * container ID. 1038 * @param isBack true if this is popping the back stack or false if this is a 1039 * forward operation. 1040 * @return The TransitionState used to complete the operation of the transition 1041 * in {@link #updateTransitionEndState(android.app.BackStackRecord.TransitionState, 1042 * android.util.SparseArray, android.util.SparseArray, boolean)}. 1043 */ 1044 private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments, 1045 SparseArray<Fragment> lastInFragments, boolean isBack) { 1046 TransitionState state = new TransitionState(); 1047 1048 // Adding a non-existent target view makes sure that the transitions don't target 1049 // any views by default. They'll only target the views we tell add. If we don't 1050 // add any, then no views will be targeted. 1051 state.nonExistentView = new View(mManager.mActivity); 1052 1053 ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>(); 1054 ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>(); 1055 ArrayList<String> tempNames = new ArrayList<String>(); 1056 ArrayList<View> tempViewList = new ArrayList<View>(); 1057 1058 // Go over all leaving fragments. 1059 for (int i = 0; i < firstOutFragments.size(); i++) { 1060 int containerId = firstOutFragments.keyAt(i); 1061 configureTransitions(containerId, state, isBack, firstOutFragments, 1062 lastInFragments, tempViews1, tempViews2, tempNames, tempViewList); 1063 } 1064 1065 // Now go over all entering fragments that didn't have a leaving fragment. 1066 for (int i = 0; i < lastInFragments.size(); i++) { 1067 int containerId = lastInFragments.keyAt(i); 1068 if (firstOutFragments.get(containerId) == null) { 1069 configureTransitions(containerId, state, isBack, firstOutFragments, 1070 lastInFragments, tempViews1, tempViews2, tempNames, tempViewList); 1071 } 1072 } 1073 1074 if (state.overallTransitions.size() == 0) { 1075 state = null; 1076 } 1077 return state; 1078 } 1079 1080 private static Transition getEnterTransition(Fragment inFragment, boolean isBack) { 1081 if (inFragment == null) { 1082 return null; 1083 } 1084 return isBack ? inFragment.getReenterTransition() : inFragment.getEnterTransition(); 1085 } 1086 1087 private static Transition getExitTransition(Fragment outFragment, boolean isBack) { 1088 if (outFragment == null) { 1089 return null; 1090 } 1091 return isBack ? outFragment.getReturnTransition() : outFragment.getExitTransition(); 1092 } 1093 1094 private static Transition getSharedElementTransition(Fragment inFragment, Fragment outFragment, 1095 boolean isBack) { 1096 if (inFragment == null || outFragment == null) { 1097 return null; 1098 } 1099 return isBack ? outFragment.getSharedElementReturnTransition() : 1100 inFragment.getSharedElementEnterTransition(); 1101 } 1102 1103 private static Transition captureExitingViews(Transition exitTransition, Fragment outFragment, 1104 ArrayList<View> viewList) { 1105 if (exitTransition != null) { 1106 View root = outFragment.getView(); 1107 viewList.clear(); 1108 root.captureTransitioningViews(viewList); 1109 if (viewList.isEmpty()) { 1110 exitTransition = null; 1111 } else { 1112 addTransitioningViews(exitTransition, viewList); 1113 } 1114 } 1115 return exitTransition; 1116 } 1117 1118 private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment, 1119 ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) { 1120 if (mSharedElementSourceNames != null) { 1121 outFragment.getView().findNamedViews(namedViews); 1122 if (isBack) { 1123 namedViews.retainAll(mSharedElementTargetNames); 1124 } else { 1125 namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames, 1126 namedViews, tempViews2); 1127 } 1128 } 1129 1130 if (isBack) { 1131 outFragment.mEnterTransitionCallback.onMapSharedElements( 1132 mSharedElementTargetNames, namedViews); 1133 setBackNameOverrides(state, namedViews, false); 1134 } else { 1135 outFragment.mExitTransitionCallback.onMapSharedElements( 1136 mSharedElementTargetNames, namedViews); 1137 setNameOverrides(state, namedViews, false); 1138 } 1139 1140 return namedViews; 1141 } 1142 1143 /** 1144 * Prepares the enter transition by adding a non-existent view to the transition's target list 1145 * and setting it epicenter callback. By adding a non-existent view to the target list, 1146 * we can prevent any view from being targeted at the beginning of the transition. 1147 * We will add to the views before the end state of the transition is captured so that the 1148 * views will appear. At the start of the transition, we clear the list of targets so that 1149 * we can restore the state of the transition and use it again. 1150 */ 1151 private void prepareEnterTransition(TransitionState state, final Transition enterTransition, 1152 final View container, final Fragment inFragment) { 1153 if (enterTransition != null) { 1154 final ArrayList<View> enteringViews = new ArrayList<View>(); 1155 final View nonExistentView = state.nonExistentView; 1156 enterTransition.addTarget(state.nonExistentView); 1157 enterTransition.addListener(new Transition.TransitionListenerAdapter() { 1158 @Override 1159 public void onTransitionStart(Transition transition) { 1160 transition.removeListener(this); 1161 transition.removeTarget(nonExistentView); 1162 int numViews = enteringViews.size(); 1163 for (int i = 0; i < numViews; i++) { 1164 transition.removeTarget(enteringViews.get(i)); 1165 } 1166 } 1167 }); 1168 container.getViewTreeObserver().addOnPreDrawListener( 1169 new ViewTreeObserver.OnPreDrawListener() { 1170 @Override 1171 public boolean onPreDraw() { 1172 container.getViewTreeObserver().removeOnPreDrawListener(this); 1173 View view = inFragment.getView(); 1174 if (view != null) { 1175 view.captureTransitioningViews(enteringViews); 1176 addTransitioningViews(enterTransition, enteringViews); 1177 } 1178 return true; 1179 } 1180 }); 1181 setSharedElementEpicenter(enterTransition, state); 1182 } 1183 } 1184 1185 private static Transition mergeTransitions(Transition enterTransition, 1186 Transition exitTransition, Transition sharedElementTransition, Fragment inFragment, 1187 boolean isBack) { 1188 boolean overlap = true; 1189 if (enterTransition != null && exitTransition != null) { 1190 overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() : 1191 inFragment.getAllowEnterTransitionOverlap(); 1192 } 1193 1194 Transition transition; 1195 if (overlap) { 1196 transition = TransitionUtils.mergeTransitions(enterTransition, exitTransition, 1197 sharedElementTransition); 1198 } else { 1199 TransitionSet staggered = new TransitionSet() 1200 .addTransition(exitTransition) 1201 .addTransition(enterTransition) 1202 .setOrdering(TransitionSet.ORDERING_SEQUENTIAL); 1203 transition = TransitionUtils.mergeTransitions(staggered, sharedElementTransition); 1204 } 1205 return transition; 1206 } 1207 1208 /** 1209 * Configures custom transitions for a specific fragment container. 1210 * 1211 * @param containerId The container ID of the fragments to configure the transition for. 1212 * @param state The Transition State to be shared with {@link #updateTransitionEndState( 1213 * android.app.BackStackRecord.TransitionState, android.util.SparseArray, 1214 * android.util.SparseArray, boolean)} later. 1215 * @param firstOutFragments The list of first fragments to be removed, keyed on the 1216 * container ID. 1217 * @param lastInFragments The list of last fragments to be added, keyed on the 1218 * container ID. 1219 * @param isBack true if this is popping the back stack or false if this is a 1220 * forward operation. 1221 * @param tempViews1 A temporary mapping of names to Views, used to avoid allocation 1222 * inside a loop. 1223 * @param tempViews2 A temporary mapping of names to Views, used to avoid allocation 1224 * inside a loop. 1225 * @param tempNames A temporary list of Strings, used to avoid allocation inside a loop. 1226 * @param tempViewList A temporary list of Views, used to avoid allocation inside a loop. 1227 */ 1228 private void configureTransitions(int containerId, TransitionState state, boolean isBack, 1229 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments, 1230 ArrayMap<String, View> tempViews1, ArrayMap<String, View> tempViews2, 1231 ArrayList<String> tempNames, ArrayList<View> tempViewList) { 1232 ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId); 1233 if (sceneRoot != null) { 1234 Fragment inFragment = lastInFragments.get(containerId); 1235 Fragment outFragment = firstOutFragments.get(containerId); 1236 1237 Transition enterTransition = getEnterTransition(inFragment, isBack); 1238 Transition sharedElementTransition = getSharedElementTransition(inFragment, outFragment, 1239 isBack); 1240 Transition exitTransition = getExitTransition(outFragment, isBack); 1241 exitTransition = captureExitingViews(exitTransition, outFragment, tempViewList); 1242 1243 ArrayMap<String, View> namedViews = tempViews1; 1244 namedViews.clear(); 1245 if (sharedElementTransition != null) { 1246 namedViews = remapSharedElements(state, 1247 outFragment, namedViews, tempViews2, isBack); 1248 } 1249 1250 // Notify the start of the transition. 1251 SharedElementCallback callback = isBack ? 1252 outFragment.mEnterTransitionCallback : 1253 inFragment.mEnterTransitionCallback; 1254 tempNames.clear(); 1255 tempNames.addAll(namedViews.keySet()); 1256 tempViewList.clear(); 1257 tempViewList.addAll(namedViews.values()); 1258 callback.onSharedElementStart(tempNames, tempViewList, null); 1259 1260 // Set the epicenter of the exit transition 1261 if (mSharedElementTargetNames != null && exitTransition != null) { 1262 View epicenterView = namedViews.get(mSharedElementTargetNames.get(0)); 1263 if (epicenterView != null) { 1264 setEpicenter(exitTransition, epicenterView); 1265 } 1266 } 1267 1268 prepareEnterTransition(state, enterTransition, sceneRoot, inFragment); 1269 1270 Transition transition = mergeTransitions(enterTransition, exitTransition, 1271 sharedElementTransition, inFragment, isBack); 1272 1273 if (transition != null) { 1274 state.overallTransitions.put(containerId, transition); 1275 transition.setNameOverrides(state.nameOverrides); 1276 // We want to exclude hidden views later, so we need a non-null list in the 1277 // transition now. 1278 transition.excludeTarget(state.nonExistentView, true); 1279 // Now exclude all currently hidden fragments. 1280 excludeHiddenFragments(state, containerId, transition); 1281 cleanupHiddenFragments(transition, state); 1282 TransitionManager.beginDelayedTransition(sceneRoot, transition); 1283 } 1284 } 1285 } 1286 1287 /** 1288 * Remaps a name-to-View map, substituting different names for keys. 1289 * 1290 * @param inMap A list of keys found in the map, in the order in toGoInMap 1291 * @param toGoInMap A list of keys to use for the new map, in the order of inMap 1292 * @param namedViews The current mapping 1293 * @param tempMap A temporary mapping that will be filled with the new values. 1294 * @return tempMap after it has been mapped with the new names as keys. 1295 */ 1296 private static ArrayMap<String, View> remapNames(ArrayList<String> inMap, 1297 ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews, 1298 ArrayMap<String, View> tempMap) { 1299 tempMap.clear(); 1300 if (!namedViews.isEmpty()) { 1301 int numKeys = inMap.size(); 1302 for (int i = 0; i < numKeys; i++) { 1303 View view = namedViews.get(inMap.get(i)); 1304 if (view != null) { 1305 tempMap.put(toGoInMap.get(i), view); 1306 } 1307 } 1308 } 1309 return tempMap; 1310 } 1311 1312 /** 1313 * After making all fragment changes, this updates the custom transitions to take into 1314 * account the entering views and any remapping. 1315 * 1316 * @param state The transition State as returned from {@link #beginTransition( 1317 * android.util.SparseArray, android.util.SparseArray, boolean)}. 1318 * @param outFragments The list of first fragments to be removed, keyed on the 1319 * container ID. 1320 * @param inFragments The list of last fragments to be added, keyed on the 1321 * container ID. 1322 * @param isBack true if this is popping the back stack or false if this is a 1323 * forward operation. 1324 */ 1325 private void updateTransitionEndState(TransitionState state, SparseArray<Fragment> outFragments, 1326 SparseArray<Fragment> inFragments, boolean isBack) { 1327 ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>(); 1328 ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>(); 1329 ArrayList<String> tempNames = new ArrayList<String>(); 1330 ArrayList<View> tempViews = new ArrayList<View>(); 1331 1332 int numInFragments = inFragments.size(); 1333 for (int i = 0; i < numInFragments; i++) { 1334 Fragment inFragment = inFragments.valueAt(i); 1335 tempViews1.clear(); 1336 ArrayMap<String, View> namedViews = mapEnteringSharedElements(inFragment, tempViews1, 1337 tempViews2, isBack); 1338 // remap shared elements and set the name mapping used in the shared element transition. 1339 if (isBack) { 1340 inFragment.mExitTransitionCallback.onMapSharedElements( 1341 mSharedElementTargetNames, namedViews); 1342 setBackNameOverrides(state, namedViews, true); 1343 } else { 1344 inFragment.mEnterTransitionCallback.onMapSharedElements( 1345 mSharedElementTargetNames, namedViews); 1346 setNameOverrides(state, namedViews, true); 1347 } 1348 1349 if (mSharedElementTargetNames != null && !namedViews.isEmpty()) { 1350 // now we know the epicenter of the entering transition. 1351 View epicenter = namedViews.get(mSharedElementTargetNames.get(0)); 1352 if (epicenter != null) { 1353 state.enteringEpicenterView = epicenter; 1354 } 1355 } 1356 1357 int containerId = inFragments.keyAt(i); 1358 SharedElementCallback sharedElementCallback = isBack ? 1359 outFragments.get(containerId).mEnterTransitionCallback : 1360 inFragment.mEnterTransitionCallback; 1361 tempNames.clear(); 1362 tempNames.addAll(namedViews.keySet()); 1363 tempViews.clear(); 1364 tempViews.addAll(namedViews.values()); 1365 sharedElementCallback.onSharedElementEnd(tempNames, tempViews, null); 1366 } 1367 1368 // Don't include any newly-hidden fragments in the transition. 1369 excludeHiddenFragments(state); 1370 } 1371 1372 private ArrayMap<String, View> mapEnteringSharedElements(Fragment inFragment, 1373 ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) { 1374 View root = inFragment.getView(); 1375 if (root != null) { 1376 if (mSharedElementSourceNames != null) { 1377 root.findNamedViews(namedViews); 1378 if (isBack) { 1379 namedViews = remapNames(mSharedElementSourceNames, 1380 mSharedElementTargetNames, namedViews, tempViews2); 1381 } else { 1382 namedViews.retainAll(mSharedElementTargetNames); 1383 } 1384 } 1385 } 1386 return namedViews; 1387 } 1388 1389 private static void cleanupHiddenFragments(Transition transition, TransitionState state) { 1390 final ArrayList<View> hiddenViews = state.hiddenFragmentViews; 1391 transition.addListener(new Transition.TransitionListenerAdapter() { 1392 @Override 1393 public void onTransitionStart(Transition transition) { 1394 transition.removeListener(this); 1395 int numViews = hiddenViews.size(); 1396 for (int i = 0; i < numViews; i++) { 1397 transition.excludeTarget(hiddenViews.get(i), false); 1398 } 1399 } 1400 }); 1401 } 1402 1403 private void excludeHiddenFragments(TransitionState state, int containerId, 1404 Transition transition) { 1405 if (mManager.mAdded != null) { 1406 for (int i = 0; i < mManager.mAdded.size(); i++) { 1407 Fragment fragment = mManager.mAdded.get(i); 1408 if (fragment.mView != null && fragment.mContainer != null && 1409 fragment.mContainerId == containerId) { 1410 if (fragment.mHidden) { 1411 if (!state.hiddenFragmentViews.contains(fragment.mView)) { 1412 transition.excludeTarget(fragment.mView, true); 1413 state.hiddenFragmentViews.add(fragment.mView); 1414 } 1415 } else { 1416 transition.excludeTarget(fragment.mView, false); 1417 state.hiddenFragmentViews.remove(fragment.mView); 1418 } 1419 } 1420 } 1421 } 1422 } 1423 1424 private void excludeHiddenFragments(TransitionState state) { 1425 int numTransitions = state.overallTransitions.size(); 1426 for (int i = 0; i < numTransitions; i++) { 1427 Transition transition = state.overallTransitions.valueAt(i); 1428 int containerId = state.overallTransitions.keyAt(i); 1429 excludeHiddenFragments(state, containerId, transition); 1430 } 1431 } 1432 1433 private static void addTransitioningViews(Transition transition, final Collection<View> views) { 1434 for (View view : views) { 1435 transition.addTarget(view); 1436 } 1437 1438 transition.addListener(new Transition.TransitionListenerAdapter() { 1439 @Override 1440 public void onTransitionStart(Transition transition) { 1441 transition.removeListener(this); 1442 for (View view : views) { 1443 transition.removeTarget(view); 1444 } 1445 } 1446 }); 1447 } 1448 1449 private static void setEpicenter(Transition transition, View view) { 1450 final Rect epicenter = new Rect(); 1451 view.getBoundsOnScreen(epicenter); 1452 1453 transition.setEpicenterCallback(new Transition.EpicenterCallback() { 1454 @Override 1455 public Rect onGetEpicenter(Transition transition) { 1456 return epicenter; 1457 } 1458 }); 1459 } 1460 1461 private void setSharedElementEpicenter(Transition transition, final TransitionState state) { 1462 transition.setEpicenterCallback(new Transition.EpicenterCallback() { 1463 private Rect mEpicenter; 1464 1465 @Override 1466 public Rect onGetEpicenter(Transition transition) { 1467 if (mEpicenter == null && state.enteringEpicenterView != null) { 1468 mEpicenter = new Rect(); 1469 state.enteringEpicenterView.getBoundsOnScreen(mEpicenter); 1470 } 1471 return mEpicenter; 1472 } 1473 }); 1474 } 1475 1476 public TransitionState popFromBackStack(boolean doStateMove, TransitionState state, 1477 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) { 1478 if (FragmentManagerImpl.DEBUG) { 1479 Log.v(TAG, "popFromBackStack: " + this); 1480 LogWriter logw = new LogWriter(Log.VERBOSE, TAG); 1481 PrintWriter pw = new FastPrintWriter(logw, false, 1024); 1482 dump(" ", null, pw, null); 1483 pw.flush(); 1484 } 1485 1486 if (state == null) { 1487 if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) { 1488 state = beginTransition(firstOutFragments, lastInFragments, true); 1489 } 1490 } else if (!doStateMove) { 1491 setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames); 1492 } 1493 1494 bumpBackStackNesting(-1); 1495 1496 Op op = mTail; 1497 while (op != null) { 1498 switch (op.cmd) { 1499 case OP_ADD: { 1500 Fragment f = op.fragment; 1501 f.mNextAnim = op.popExitAnim; 1502 mManager.removeFragment(f, 1503 FragmentManagerImpl.reverseTransit(mTransition), 1504 mTransitionStyle); 1505 } 1506 break; 1507 case OP_REPLACE: { 1508 Fragment f = op.fragment; 1509 if (f != null) { 1510 f.mNextAnim = op.popExitAnim; 1511 mManager.removeFragment(f, 1512 FragmentManagerImpl.reverseTransit(mTransition), 1513 mTransitionStyle); 1514 } 1515 if (op.removed != null) { 1516 for (int i = 0; i < op.removed.size(); i++) { 1517 Fragment old = op.removed.get(i); 1518 old.mNextAnim = op.popEnterAnim; 1519 mManager.addFragment(old, false); 1520 } 1521 } 1522 } 1523 break; 1524 case OP_REMOVE: { 1525 Fragment f = op.fragment; 1526 f.mNextAnim = op.popEnterAnim; 1527 mManager.addFragment(f, false); 1528 } 1529 break; 1530 case OP_HIDE: { 1531 Fragment f = op.fragment; 1532 f.mNextAnim = op.popEnterAnim; 1533 mManager.showFragment(f, 1534 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1535 } 1536 break; 1537 case OP_SHOW: { 1538 Fragment f = op.fragment; 1539 f.mNextAnim = op.popExitAnim; 1540 mManager.hideFragment(f, 1541 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1542 } 1543 break; 1544 case OP_DETACH: { 1545 Fragment f = op.fragment; 1546 f.mNextAnim = op.popEnterAnim; 1547 mManager.attachFragment(f, 1548 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1549 } 1550 break; 1551 case OP_ATTACH: { 1552 Fragment f = op.fragment; 1553 f.mNextAnim = op.popExitAnim; 1554 mManager.detachFragment(f, 1555 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); 1556 } 1557 break; 1558 default: { 1559 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 1560 } 1561 } 1562 1563 op = op.prev; 1564 } 1565 1566 if (doStateMove) { 1567 mManager.moveToState(mManager.mCurState, 1568 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true); 1569 if (state != null) { 1570 updateTransitionEndState(state, firstOutFragments, lastInFragments, true); 1571 state = null; 1572 } 1573 } 1574 1575 if (mIndex >= 0) { 1576 mManager.freeBackStackIndex(mIndex); 1577 mIndex = -1; 1578 } 1579 return state; 1580 } 1581 1582 private static void setNameOverride(ArrayMap<String, String> overrides, 1583 String source, String target) { 1584 if (source != null && target != null && !source.equals(target)) { 1585 for (int index = 0; index < overrides.size(); index++) { 1586 if (source.equals(overrides.valueAt(index))) { 1587 overrides.setValueAt(index, target); 1588 return; 1589 } 1590 } 1591 overrides.put(source, target); 1592 } 1593 } 1594 1595 private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames, 1596 ArrayList<String> targetNames) { 1597 if (sourceNames != null) { 1598 for (int i = 0; i < sourceNames.size(); i++) { 1599 String source = sourceNames.get(i); 1600 String target = targetNames.get(i); 1601 setNameOverride(state.nameOverrides, source, target); 1602 } 1603 } 1604 } 1605 1606 private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews, 1607 boolean isEnd) { 1608 int count = mSharedElementTargetNames.size(); 1609 for (int i = 0; i < count; i++) { 1610 String source = mSharedElementSourceNames.get(i); 1611 String originalTarget = mSharedElementTargetNames.get(i); 1612 String target = namedViews.get(originalTarget).getTransitionName(); 1613 if (isEnd) { 1614 setNameOverride(state.nameOverrides, source, target); 1615 } else { 1616 setNameOverride(state.nameOverrides, target, source); 1617 } 1618 } 1619 } 1620 1621 private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews, 1622 boolean isEnd) { 1623 int count = namedViews.size(); 1624 for (int i = 0; i < count; i++) { 1625 String source = namedViews.keyAt(i); 1626 String target = namedViews.valueAt(i).getTransitionName(); 1627 if (isEnd) { 1628 setNameOverride(state.nameOverrides, source, target); 1629 } else { 1630 setNameOverride(state.nameOverrides, target, source); 1631 } 1632 } 1633 } 1634 1635 public String getName() { 1636 return mName; 1637 } 1638 1639 public int getTransition() { 1640 return mTransition; 1641 } 1642 1643 public int getTransitionStyle() { 1644 return mTransitionStyle; 1645 } 1646 1647 public boolean isEmpty() { 1648 return mNumOp == 0; 1649 } 1650 1651 public class TransitionState { 1652 public SparseArray<Transition> overallTransitions = new SparseArray<Transition>(); 1653 public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>(); 1654 public ArrayList<View> hiddenFragmentViews = new ArrayList<View>(); 1655 1656 public View enteringEpicenterView; 1657 public View nonExistentView; 1658 } 1659} 1660