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