blob: 3a51aff3792124f263b2e8739f473f954af6988f [file] [log] [blame]
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001/*
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
George Mountd4c3c912014-06-09 12:31:34 -070019import android.graphics.Rect;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070020import android.os.Parcel;
21import android.os.Parcelable;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070022import android.text.TextUtils;
George Mountd4c3c912014-06-09 12:31:34 -070023import android.transition.Transition;
George Mountd4c3c912014-06-09 12:31:34 -070024import android.transition.TransitionManager;
25import android.transition.TransitionSet;
26import android.util.ArrayMap;
Dianne Hackborn445646c2010-06-25 15:52:59 -070027import android.util.Log;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -070028import android.util.LogWriter;
George Mountc03da0e2014-08-22 17:04:02 -070029import android.util.SparseArray;
George Mountd4c3c912014-06-09 12:31:34 -070030import android.view.View;
31import android.view.ViewGroup;
George Mountc03da0e2014-08-22 17:04:02 -070032import android.view.ViewTreeObserver;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070033
George Mounta7245b42016-02-10 17:03:32 -080034import com.android.internal.util.FastPrintWriter;
35
Dianne Hackborn30d71892010-12-11 10:37:55 -080036import java.io.FileDescriptor;
37import java.io.PrintWriter;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070038import java.util.ArrayList;
George Mount7ce5d752014-10-30 16:09:22 -070039import java.util.List;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070040
41final class BackStackState implements Parcelable {
42 final int[] mOps;
43 final int mTransition;
44 final int mTransitionStyle;
45 final String mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -070046 final int mIndex;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070047 final int mBreadCrumbTitleRes;
48 final CharSequence mBreadCrumbTitleText;
49 final int mBreadCrumbShortTitleRes;
50 final CharSequence mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -070051 final ArrayList<String> mSharedElementSourceNames;
52 final ArrayList<String> mSharedElementTargetNames;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070053
54 public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
Dianne Hackborn445646c2010-06-25 15:52:59 -070055 int numRemoved = 0;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070056 BackStackRecord.Op op = bse.mHead;
Dianne Hackborn445646c2010-06-25 15:52:59 -070057 while (op != null) {
George Mountd4c3c912014-06-09 12:31:34 -070058 if (op.removed != null) {
59 numRemoved += op.removed.size();
60 }
Dianne Hackborn445646c2010-06-25 15:52:59 -070061 op = op.next;
62 }
George Mountd4c3c912014-06-09 12:31:34 -070063 mOps = new int[bse.mNumOp * 7 + numRemoved];
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070064
Dianne Hackbornb7a2e472010-08-12 16:20:42 -070065 if (!bse.mAddToBackStack) {
66 throw new IllegalStateException("Not on back stack");
67 }
68
Dianne Hackborn445646c2010-06-25 15:52:59 -070069 op = bse.mHead;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070070 int pos = 0;
71 while (op != null) {
72 mOps[pos++] = op.cmd;
Dianne Hackbornee76efb2012-06-05 10:27:40 -070073 mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070074 mOps[pos++] = op.enterAnim;
75 mOps[pos++] = op.exitAnim;
Chet Haasebc377842011-03-22 11:35:22 -070076 mOps[pos++] = op.popEnterAnim;
77 mOps[pos++] = op.popExitAnim;
Dianne Hackborn445646c2010-06-25 15:52:59 -070078 if (op.removed != null) {
79 final int N = op.removed.size();
80 mOps[pos++] = N;
George Mountd4c3c912014-06-09 12:31:34 -070081 for (int i = 0; i < N; i++) {
Dianne Hackborn445646c2010-06-25 15:52:59 -070082 mOps[pos++] = op.removed.get(i).mIndex;
83 }
84 } else {
85 mOps[pos++] = 0;
86 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070087 op = op.next;
88 }
89 mTransition = bse.mTransition;
90 mTransitionStyle = bse.mTransitionStyle;
91 mName = bse.mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -070092 mIndex = bse.mIndex;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070093 mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
94 mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
95 mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
96 mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -070097 mSharedElementSourceNames = bse.mSharedElementSourceNames;
98 mSharedElementTargetNames = bse.mSharedElementTargetNames;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070099 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700100
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700101 public BackStackState(Parcel in) {
102 mOps = in.createIntArray();
103 mTransition = in.readInt();
104 mTransitionStyle = in.readInt();
105 mName = in.readString();
Dianne Hackborndd913a52010-07-22 12:17:04 -0700106 mIndex = in.readInt();
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700107 mBreadCrumbTitleRes = in.readInt();
108 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
109 mBreadCrumbShortTitleRes = in.readInt();
110 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
George Mountd4c3c912014-06-09 12:31:34 -0700111 mSharedElementSourceNames = in.createStringArrayList();
112 mSharedElementTargetNames = in.createStringArrayList();
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700113 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700114
115 public BackStackRecord instantiate(FragmentManagerImpl fm) {
116 BackStackRecord bse = new BackStackRecord(fm);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700117 int pos = 0;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700118 int num = 0;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700119 while (pos < mOps.length) {
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700120 BackStackRecord.Op op = new BackStackRecord.Op();
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700121 op.cmd = mOps[pos++];
George Mountd4c3c912014-06-09 12:31:34 -0700122 if (FragmentManagerImpl.DEBUG) {
123 Log.v(FragmentManagerImpl.TAG,
124 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
125 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700126 int findex = mOps[pos++];
127 if (findex >= 0) {
128 Fragment f = fm.mActive.get(findex);
129 op.fragment = f;
130 } else {
131 op.fragment = null;
132 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700133 op.enterAnim = mOps[pos++];
134 op.exitAnim = mOps[pos++];
Chet Haasebc377842011-03-22 11:35:22 -0700135 op.popEnterAnim = mOps[pos++];
136 op.popExitAnim = mOps[pos++];
Dianne Hackborn445646c2010-06-25 15:52:59 -0700137 final int N = mOps[pos++];
138 if (N > 0) {
139 op.removed = new ArrayList<Fragment>(N);
George Mountd4c3c912014-06-09 12:31:34 -0700140 for (int i = 0; i < N; i++) {
141 if (FragmentManagerImpl.DEBUG) {
142 Log.v(FragmentManagerImpl.TAG,
143 "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
144 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700145 Fragment r = fm.mActive.get(mOps[pos++]);
146 op.removed.add(r);
Dianne Hackborn445646c2010-06-25 15:52:59 -0700147 }
148 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700149 bse.addOp(op);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700150 num++;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700151 }
152 bse.mTransition = mTransition;
153 bse.mTransitionStyle = mTransitionStyle;
154 bse.mName = mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -0700155 bse.mIndex = mIndex;
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700156 bse.mAddToBackStack = true;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700157 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
158 bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
159 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
160 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -0700161 bse.mSharedElementSourceNames = mSharedElementSourceNames;
162 bse.mSharedElementTargetNames = mSharedElementTargetNames;
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700163 bse.bumpBackStackNesting(1);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700164 return bse;
165 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700166
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700167 public int describeContents() {
168 return 0;
169 }
170
171 public void writeToParcel(Parcel dest, int flags) {
172 dest.writeIntArray(mOps);
173 dest.writeInt(mTransition);
174 dest.writeInt(mTransitionStyle);
175 dest.writeString(mName);
Dianne Hackborndd913a52010-07-22 12:17:04 -0700176 dest.writeInt(mIndex);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700177 dest.writeInt(mBreadCrumbTitleRes);
178 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
179 dest.writeInt(mBreadCrumbShortTitleRes);
180 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
George Mountd4c3c912014-06-09 12:31:34 -0700181 dest.writeStringList(mSharedElementSourceNames);
182 dest.writeStringList(mSharedElementTargetNames);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700183 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700184
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700185 public static final Parcelable.Creator<BackStackState> CREATOR
186 = new Parcelable.Creator<BackStackState>() {
187 public BackStackState createFromParcel(Parcel in) {
188 return new BackStackState(in);
189 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700190
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700191 public BackStackState[] newArray(int size) {
192 return new BackStackState[size];
193 }
194 };
195}
196
197/**
198 * @hide Entry of an operation on the fragment back stack.
199 */
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700200final class BackStackRecord extends FragmentTransaction implements
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700201 FragmentManager.BackStackEntry, Runnable {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700202 static final String TAG = FragmentManagerImpl.TAG;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700203
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700204 final FragmentManagerImpl mManager;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700205
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700206 static final int OP_NULL = 0;
207 static final int OP_ADD = 1;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700208 static final int OP_REPLACE = 2;
209 static final int OP_REMOVE = 3;
210 static final int OP_HIDE = 4;
211 static final int OP_SHOW = 5;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700212 static final int OP_DETACH = 6;
213 static final int OP_ATTACH = 7;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700214
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700215 static final class Op {
216 Op next;
217 Op prev;
218 int cmd;
219 Fragment fragment;
220 int enterAnim;
221 int exitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700222 int popEnterAnim;
223 int popExitAnim;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700224 ArrayList<Fragment> removed;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700225 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700226
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700227 Op mHead;
228 Op mTail;
229 int mNumOp;
230 int mEnterAnim;
231 int mExitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700232 int mPopEnterAnim;
233 int mPopExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700234 int mTransition;
235 int mTransitionStyle;
236 boolean mAddToBackStack;
Adam Powell0c24a552010-11-03 16:44:11 -0700237 boolean mAllowAddToBackStack = true;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700238 String mName;
239 boolean mCommitted;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700240 int mIndex = -1;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700241
242 int mBreadCrumbTitleRes;
243 CharSequence mBreadCrumbTitleText;
244 int mBreadCrumbShortTitleRes;
245 CharSequence mBreadCrumbShortTitleText;
246
George Mountd4c3c912014-06-09 12:31:34 -0700247 ArrayList<String> mSharedElementSourceNames;
248 ArrayList<String> mSharedElementTargetNames;
249
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700250 @Override
251 public String toString() {
252 StringBuilder sb = new StringBuilder(128);
253 sb.append("BackStackEntry{");
254 sb.append(Integer.toHexString(System.identityHashCode(this)));
255 if (mIndex >= 0) {
256 sb.append(" #");
257 sb.append(mIndex);
258 }
259 if (mName != null) {
260 sb.append(" ");
261 sb.append(mName);
262 }
263 sb.append("}");
264 return sb.toString();
265 }
266
Dianne Hackborn30d71892010-12-11 10:37:55 -0800267 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700268 dump(prefix, writer, true);
269 }
270
271 void dump(String prefix, PrintWriter writer, boolean full) {
272 if (full) {
George Mountd4c3c912014-06-09 12:31:34 -0700273 writer.print(prefix);
274 writer.print("mName=");
275 writer.print(mName);
276 writer.print(" mIndex=");
277 writer.print(mIndex);
278 writer.print(" mCommitted=");
279 writer.println(mCommitted);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700280 if (mTransition != FragmentTransaction.TRANSIT_NONE) {
George Mountd4c3c912014-06-09 12:31:34 -0700281 writer.print(prefix);
282 writer.print("mTransition=#");
283 writer.print(Integer.toHexString(mTransition));
284 writer.print(" mTransitionStyle=#");
285 writer.println(Integer.toHexString(mTransitionStyle));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700286 }
George Mountd4c3c912014-06-09 12:31:34 -0700287 if (mEnterAnim != 0 || mExitAnim != 0) {
288 writer.print(prefix);
289 writer.print("mEnterAnim=#");
290 writer.print(Integer.toHexString(mEnterAnim));
291 writer.print(" mExitAnim=#");
292 writer.println(Integer.toHexString(mExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700293 }
George Mountd4c3c912014-06-09 12:31:34 -0700294 if (mPopEnterAnim != 0 || mPopExitAnim != 0) {
295 writer.print(prefix);
296 writer.print("mPopEnterAnim=#");
297 writer.print(Integer.toHexString(mPopEnterAnim));
298 writer.print(" mPopExitAnim=#");
299 writer.println(Integer.toHexString(mPopExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700300 }
301 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700302 writer.print(prefix);
303 writer.print("mBreadCrumbTitleRes=#");
304 writer.print(Integer.toHexString(mBreadCrumbTitleRes));
305 writer.print(" mBreadCrumbTitleText=");
306 writer.println(mBreadCrumbTitleText);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700307 }
308 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700309 writer.print(prefix);
310 writer.print("mBreadCrumbShortTitleRes=#");
311 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
312 writer.print(" mBreadCrumbShortTitleText=");
313 writer.println(mBreadCrumbShortTitleText);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700314 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800315 }
316
317 if (mHead != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700318 writer.print(prefix);
319 writer.println("Operations:");
Dianne Hackborn30d71892010-12-11 10:37:55 -0800320 String innerPrefix = prefix + " ";
321 Op op = mHead;
322 int num = 0;
323 while (op != null) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700324 String cmdStr;
325 switch (op.cmd) {
George Mountd4c3c912014-06-09 12:31:34 -0700326 case OP_NULL:
327 cmdStr = "NULL";
328 break;
329 case OP_ADD:
330 cmdStr = "ADD";
331 break;
332 case OP_REPLACE:
333 cmdStr = "REPLACE";
334 break;
335 case OP_REMOVE:
336 cmdStr = "REMOVE";
337 break;
338 case OP_HIDE:
339 cmdStr = "HIDE";
340 break;
341 case OP_SHOW:
342 cmdStr = "SHOW";
343 break;
344 case OP_DETACH:
345 cmdStr = "DETACH";
346 break;
347 case OP_ATTACH:
348 cmdStr = "ATTACH";
349 break;
350 default:
351 cmdStr = "cmd=" + op.cmd;
352 break;
Dianne Hackborn30d71892010-12-11 10:37:55 -0800353 }
George Mountd4c3c912014-06-09 12:31:34 -0700354 writer.print(prefix);
355 writer.print(" Op #");
356 writer.print(num);
357 writer.print(": ");
358 writer.print(cmdStr);
359 writer.print(" ");
360 writer.println(op.fragment);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700361 if (full) {
362 if (op.enterAnim != 0 || op.exitAnim != 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700363 writer.print(innerPrefix);
364 writer.print("enterAnim=#");
365 writer.print(Integer.toHexString(op.enterAnim));
366 writer.print(" exitAnim=#");
367 writer.println(Integer.toHexString(op.exitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700368 }
369 if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700370 writer.print(innerPrefix);
371 writer.print("popEnterAnim=#");
372 writer.print(Integer.toHexString(op.popEnterAnim));
373 writer.print(" popExitAnim=#");
374 writer.println(Integer.toHexString(op.popExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700375 }
Chet Haasebc377842011-03-22 11:35:22 -0700376 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800377 if (op.removed != null && op.removed.size() > 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700378 for (int i = 0; i < op.removed.size(); i++) {
Dianne Hackbornd2835932010-12-13 16:28:46 -0800379 writer.print(innerPrefix);
380 if (op.removed.size() == 1) {
381 writer.print("Removed: ");
382 } else {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700383 if (i == 0) {
384 writer.println("Removed:");
385 }
George Mountd4c3c912014-06-09 12:31:34 -0700386 writer.print(innerPrefix);
387 writer.print(" #");
388 writer.print(i);
389 writer.print(": ");
Dianne Hackbornd2835932010-12-13 16:28:46 -0800390 }
391 writer.println(op.removed.get(i));
Dianne Hackborn30d71892010-12-11 10:37:55 -0800392 }
393 }
394 op = op.next;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700395 num++;
Dianne Hackborn30d71892010-12-11 10:37:55 -0800396 }
397 }
398 }
399
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700400 public BackStackRecord(FragmentManagerImpl manager) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700401 mManager = manager;
402 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700403
404 public int getId() {
405 return mIndex;
406 }
407
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800408 public int getBreadCrumbTitleRes() {
409 return mBreadCrumbTitleRes;
410 }
411
412 public int getBreadCrumbShortTitleRes() {
413 return mBreadCrumbShortTitleRes;
414 }
415
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700416 public CharSequence getBreadCrumbTitle() {
417 if (mBreadCrumbTitleRes != 0) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700418 return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700419 }
420 return mBreadCrumbTitleText;
421 }
422
423 public CharSequence getBreadCrumbShortTitle() {
424 if (mBreadCrumbShortTitleRes != 0) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700425 return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700426 }
427 return mBreadCrumbShortTitleText;
428 }
429
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700430 void addOp(Op op) {
431 if (mHead == null) {
432 mHead = mTail = op;
433 } else {
434 op.prev = mTail;
435 mTail.next = op;
436 mTail = op;
437 }
438 op.enterAnim = mEnterAnim;
439 op.exitAnim = mExitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700440 op.popEnterAnim = mPopEnterAnim;
441 op.popExitAnim = mPopExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700442 mNumOp++;
443 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700444
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700445 public FragmentTransaction add(Fragment fragment, String tag) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700446 doAddOp(0, fragment, tag, OP_ADD);
447 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700448 }
449
450 public FragmentTransaction add(int containerViewId, Fragment fragment) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700451 doAddOp(containerViewId, fragment, null, OP_ADD);
452 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700453 }
454
455 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700456 doAddOp(containerViewId, fragment, tag, OP_ADD);
457 return this;
458 }
459
460 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
Dianne Hackborn3e449ce2010-09-11 20:52:31 -0700461 fragment.mFragmentManager = mManager;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700462
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700463 if (tag != null) {
464 if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
465 throw new IllegalStateException("Can't change tag of fragment "
466 + fragment + ": was " + fragment.mTag
467 + " now " + tag);
468 }
469 fragment.mTag = tag;
470 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700471
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700472 if (containerViewId != 0) {
Adam Powelle01f5952016-02-23 15:25:42 -0800473 if (containerViewId == View.NO_ID) {
474 throw new IllegalArgumentException("Can't add fragment "
475 + fragment + " with tag " + tag + " to container view with no id");
476 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700477 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
478 throw new IllegalStateException("Can't change container ID of fragment "
479 + fragment + ": was " + fragment.mFragmentId
480 + " now " + containerViewId);
481 }
482 fragment.mContainerId = fragment.mFragmentId = containerViewId;
483 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700484
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700485 Op op = new Op();
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700486 op.cmd = opcmd;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700487 op.fragment = fragment;
488 addOp(op);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700489 }
490
491 public FragmentTransaction replace(int containerViewId, Fragment fragment) {
492 return replace(containerViewId, fragment, null);
493 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700494
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700495 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
496 if (containerViewId == 0) {
497 throw new IllegalArgumentException("Must use non-zero containerViewId");
498 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700499
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700500 doAddOp(containerViewId, fragment, tag, OP_REPLACE);
501 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700502 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700503
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700504 public FragmentTransaction remove(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700505 Op op = new Op();
506 op.cmd = OP_REMOVE;
507 op.fragment = fragment;
508 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700509
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700510 return this;
511 }
512
513 public FragmentTransaction hide(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700514 Op op = new Op();
515 op.cmd = OP_HIDE;
516 op.fragment = fragment;
517 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700518
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700519 return this;
520 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700521
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700522 public FragmentTransaction show(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700523 Op op = new Op();
524 op.cmd = OP_SHOW;
525 op.fragment = fragment;
526 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700527
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700528 return this;
529 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700530
Dianne Hackborn47c41562011-04-15 19:00:20 -0700531 public FragmentTransaction detach(Fragment fragment) {
Dianne Hackborn47c41562011-04-15 19:00:20 -0700532 Op op = new Op();
533 op.cmd = OP_DETACH;
534 op.fragment = fragment;
535 addOp(op);
536
537 return this;
538 }
539
540 public FragmentTransaction attach(Fragment fragment) {
Dianne Hackborn47c41562011-04-15 19:00:20 -0700541 Op op = new Op();
542 op.cmd = OP_ATTACH;
543 op.fragment = fragment;
544 addOp(op);
545
546 return this;
547 }
548
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700549 public FragmentTransaction setCustomAnimations(int enter, int exit) {
Chet Haasebc377842011-03-22 11:35:22 -0700550 return setCustomAnimations(enter, exit, 0, 0);
551 }
552
553 public FragmentTransaction setCustomAnimations(int enter, int exit,
554 int popEnter, int popExit) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700555 mEnterAnim = enter;
556 mExitAnim = exit;
Chet Haasebc377842011-03-22 11:35:22 -0700557 mPopEnterAnim = popEnter;
558 mPopExitAnim = popExit;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700559 return this;
560 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700561
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700562 public FragmentTransaction setTransition(int transition) {
563 mTransition = transition;
564 return this;
565 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700566
George Mountd4c3c912014-06-09 12:31:34 -0700567 @Override
George Mount448bace2014-08-18 16:27:43 -0700568 public FragmentTransaction addSharedElement(View sharedElement, String name) {
569 String transitionName = sharedElement.getTransitionName();
570 if (transitionName == null) {
571 throw new IllegalArgumentException("Unique transitionNames are required for all" +
572 " sharedElements");
573 }
574 if (mSharedElementSourceNames == null) {
575 mSharedElementSourceNames = new ArrayList<String>();
576 mSharedElementTargetNames = new ArrayList<String>();
577 }
578 mSharedElementSourceNames.add(transitionName);
579 mSharedElementTargetNames.add(name);
580 return this;
581 }
582
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700583 public FragmentTransaction setTransitionStyle(int styleRes) {
584 mTransitionStyle = styleRes;
585 return this;
586 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700587
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700588 public FragmentTransaction addToBackStack(String name) {
Adam Powell0c24a552010-11-03 16:44:11 -0700589 if (!mAllowAddToBackStack) {
590 throw new IllegalStateException(
591 "This FragmentTransaction is not allowed to be added to the back stack.");
592 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700593 mAddToBackStack = true;
594 mName = name;
595 return this;
596 }
597
Adam Powell0c24a552010-11-03 16:44:11 -0700598 public boolean isAddToBackStackAllowed() {
599 return mAllowAddToBackStack;
600 }
601
602 public FragmentTransaction disallowAddToBackStack() {
603 if (mAddToBackStack) {
604 throw new IllegalStateException(
605 "This transaction is already being added to the back stack");
606 }
607 mAllowAddToBackStack = false;
608 return this;
609 }
610
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700611 public FragmentTransaction setBreadCrumbTitle(int res) {
612 mBreadCrumbTitleRes = res;
613 mBreadCrumbTitleText = null;
614 return this;
615 }
616
617 public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
618 mBreadCrumbTitleRes = 0;
619 mBreadCrumbTitleText = text;
620 return this;
621 }
622
623 public FragmentTransaction setBreadCrumbShortTitle(int res) {
624 mBreadCrumbShortTitleRes = res;
625 mBreadCrumbShortTitleText = null;
626 return this;
627 }
628
629 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
630 mBreadCrumbShortTitleRes = 0;
631 mBreadCrumbShortTitleText = text;
632 return this;
633 }
634
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700635 void bumpBackStackNesting(int amt) {
636 if (!mAddToBackStack) {
637 return;
638 }
George Mountd4c3c912014-06-09 12:31:34 -0700639 if (FragmentManagerImpl.DEBUG) {
640 Log.v(TAG, "Bump nesting in " + this
641 + " by " + amt);
642 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700643 Op op = mHead;
644 while (op != null) {
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700645 if (op.fragment != null) {
646 op.fragment.mBackStackNesting += amt;
George Mountd4c3c912014-06-09 12:31:34 -0700647 if (FragmentManagerImpl.DEBUG) {
648 Log.v(TAG, "Bump nesting of "
649 + op.fragment + " to " + op.fragment.mBackStackNesting);
650 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700651 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700652 if (op.removed != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700653 for (int i = op.removed.size() - 1; i >= 0; i--) {
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700654 Fragment r = op.removed.get(i);
655 r.mBackStackNesting += amt;
George Mountd4c3c912014-06-09 12:31:34 -0700656 if (FragmentManagerImpl.DEBUG) {
657 Log.v(TAG, "Bump nesting of "
658 + r + " to " + r.mBackStackNesting);
659 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700660 }
661 }
662 op = op.next;
663 }
664 }
665
Dianne Hackborndd913a52010-07-22 12:17:04 -0700666 public int commit() {
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700667 return commitInternal(false);
668 }
669
670 public int commitAllowingStateLoss() {
671 return commitInternal(true);
672 }
George Mountd4c3c912014-06-09 12:31:34 -0700673
Adam Powell8585ed62016-02-04 15:38:20 -0800674 @Override
675 public void commitNow() {
676 disallowAddToBackStack();
677 mManager.execSingleAction(this, false);
678 }
679
680 @Override
681 public void commitNowAllowingStateLoss() {
682 disallowAddToBackStack();
683 mManager.execSingleAction(this, true);
684 }
685
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700686 int commitInternal(boolean allowStateLoss) {
George Mountd4c3c912014-06-09 12:31:34 -0700687 if (mCommitted) {
688 throw new IllegalStateException("commit already called");
689 }
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700690 if (FragmentManagerImpl.DEBUG) {
691 Log.v(TAG, "Commit: " + this);
692 LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700693 PrintWriter pw = new FastPrintWriter(logw, false, 1024);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700694 dump(" ", null, pw, null);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700695 pw.flush();
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700696 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700697 mCommitted = true;
Dianne Hackborndd913a52010-07-22 12:17:04 -0700698 if (mAddToBackStack) {
699 mIndex = mManager.allocBackStackIndex(this);
700 } else {
701 mIndex = -1;
702 }
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700703 mManager.enqueueAction(this, allowStateLoss);
Dianne Hackborndd913a52010-07-22 12:17:04 -0700704 return mIndex;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700705 }
George Mountd4c3c912014-06-09 12:31:34 -0700706
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700707 public void run() {
George Mountd4c3c912014-06-09 12:31:34 -0700708 if (FragmentManagerImpl.DEBUG) {
709 Log.v(TAG, "Run: " + this);
710 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700711
Dianne Hackborndd913a52010-07-22 12:17:04 -0700712 if (mAddToBackStack) {
713 if (mIndex < 0) {
714 throw new IllegalStateException("addToBackStack() called after commit()");
715 }
716 }
717
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700718 bumpBackStackNesting(1);
719
George Mounta7245b42016-02-10 17:03:32 -0800720 if (mManager.mCurState >= Fragment.CREATED) {
721 SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
722 SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
723 calculateFragments(firstOutFragments, lastInFragments);
724 beginTransition(firstOutFragments, lastInFragments, false);
725 }
George Mountd4c3c912014-06-09 12:31:34 -0700726
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700727 Op op = mHead;
728 while (op != null) {
729 switch (op.cmd) {
730 case OP_ADD: {
731 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700732 f.mNextAnim = op.enterAnim;
733 mManager.addFragment(f, false);
George Mountd4c3c912014-06-09 12:31:34 -0700734 }
735 break;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700736 case OP_REPLACE: {
737 Fragment f = op.fragment;
Eddie Ringle22369d52014-06-29 16:43:28 -0400738 int containerId = f.mContainerId;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700739 if (mManager.mAdded != null) {
Aurimas Liutikas61fa2d42016-01-05 15:58:29 -0800740 for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700741 Fragment old = mManager.mAdded.get(i);
George Mountd4c3c912014-06-09 12:31:34 -0700742 if (FragmentManagerImpl.DEBUG) {
743 Log.v(TAG,
744 "OP_REPLACE: adding=" + f + " old=" + old);
745 }
Eddie Ringle22369d52014-06-29 16:43:28 -0400746 if (old.mContainerId == containerId) {
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700747 if (old == f) {
748 op.fragment = f = null;
749 } else {
750 if (op.removed == null) {
751 op.removed = new ArrayList<Fragment>();
752 }
753 op.removed.add(old);
754 old.mNextAnim = op.exitAnim;
755 if (mAddToBackStack) {
756 old.mBackStackNesting += 1;
George Mountd4c3c912014-06-09 12:31:34 -0700757 if (FragmentManagerImpl.DEBUG) {
758 Log.v(TAG, "Bump nesting of "
759 + old + " to " + old.mBackStackNesting);
760 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700761 }
762 mManager.removeFragment(old, mTransition, mTransitionStyle);
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700763 }
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700764 }
765 }
766 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700767 if (f != null) {
768 f.mNextAnim = op.enterAnim;
769 mManager.addFragment(f, false);
770 }
George Mountd4c3c912014-06-09 12:31:34 -0700771 }
772 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700773 case OP_REMOVE: {
774 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700775 f.mNextAnim = op.exitAnim;
776 mManager.removeFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700777 }
778 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700779 case OP_HIDE: {
780 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700781 f.mNextAnim = op.exitAnim;
782 mManager.hideFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700783 }
784 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700785 case OP_SHOW: {
786 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700787 f.mNextAnim = op.enterAnim;
788 mManager.showFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700789 }
790 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700791 case OP_DETACH: {
792 Fragment f = op.fragment;
793 f.mNextAnim = op.exitAnim;
794 mManager.detachFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700795 }
796 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700797 case OP_ATTACH: {
798 Fragment f = op.fragment;
799 f.mNextAnim = op.enterAnim;
800 mManager.attachFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700801 }
802 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700803 default: {
804 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
805 }
806 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700807
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700808 op = op.next;
809 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700810
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700811 mManager.moveToState(mManager.mCurState, mTransition,
812 mTransitionStyle, true);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700813
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700814 if (mAddToBackStack) {
815 mManager.addBackStackState(this);
816 }
817 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700818
George Mount233c97c2015-12-04 18:00:55 +0000819 private static void setFirstOut(SparseArray<Fragment> firstOutFragments,
820 SparseArray<Fragment> lastInFragments, Fragment fragment) {
George Mountc03da0e2014-08-22 17:04:02 -0700821 if (fragment != null) {
822 int containerId = fragment.mContainerId;
George Mount233c97c2015-12-04 18:00:55 +0000823 if (containerId != 0 && !fragment.isHidden()) {
824 if (fragment.isAdded() && fragment.getView() != null
825 && firstOutFragments.get(containerId) == null) {
826 firstOutFragments.put(containerId, fragment);
827 }
828 if (lastInFragments.get(containerId) == fragment) {
829 lastInFragments.remove(containerId);
830 }
George Mountc03da0e2014-08-22 17:04:02 -0700831 }
George Mountd4c3c912014-06-09 12:31:34 -0700832 }
George Mountc03da0e2014-08-22 17:04:02 -0700833 }
George Mountd4c3c912014-06-09 12:31:34 -0700834
George Mount233c97c2015-12-04 18:00:55 +0000835 private void setLastIn(SparseArray<Fragment> firstOutFragments,
836 SparseArray<Fragment> lastInFragments, Fragment fragment) {
George Mountc03da0e2014-08-22 17:04:02 -0700837 if (fragment != null) {
838 int containerId = fragment.mContainerId;
839 if (containerId != 0) {
George Mount233c97c2015-12-04 18:00:55 +0000840 if (!fragment.isAdded()) {
841 lastInFragments.put(containerId, fragment);
842 }
843 if (firstOutFragments.get(containerId) == fragment) {
844 firstOutFragments.remove(containerId);
845 }
George Mountc03da0e2014-08-22 17:04:02 -0700846 }
George Mounta7245b42016-02-10 17:03:32 -0800847 /**
848 * Ensure that fragments that are entering are at least at the CREATED state
849 * so that they may load Transitions using TransitionInflater.
850 */
851 if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED) {
852 mManager.makeActive(fragment);
853 mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
854 }
George Mountc03da0e2014-08-22 17:04:02 -0700855 }
856 }
857
858 /**
859 * Finds the first removed fragment and last added fragments when going forward.
860 * If none of the fragments have transitions, then both lists will be empty.
861 *
862 * @param firstOutFragments The list of first fragments to be removed, keyed on the
863 * container ID. This list will be modified by the method.
864 * @param lastInFragments The list of last fragments to be added, keyed on the
865 * container ID. This list will be modified by the method.
866 */
867 private void calculateFragments(SparseArray<Fragment> firstOutFragments,
868 SparseArray<Fragment> lastInFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700869 if (!mManager.mContainer.onHasView()) {
George Mount0b26e4d2014-09-17 16:36:42 -0700870 return; // nothing to see, so no transitions
871 }
George Mountc03da0e2014-08-22 17:04:02 -0700872 Op op = mHead;
873 while (op != null) {
874 switch (op.cmd) {
875 case OP_ADD:
George Mount233c97c2015-12-04 18:00:55 +0000876 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700877 break;
878 case OP_REPLACE: {
879 Fragment f = op.fragment;
880 if (mManager.mAdded != null) {
881 for (int i = 0; i < mManager.mAdded.size(); i++) {
882 Fragment old = mManager.mAdded.get(i);
883 if (f == null || old.mContainerId == f.mContainerId) {
884 if (old == f) {
885 f = null;
George Mount233c97c2015-12-04 18:00:55 +0000886 lastInFragments.remove(old.mContainerId);
George Mountc03da0e2014-08-22 17:04:02 -0700887 } else {
George Mount233c97c2015-12-04 18:00:55 +0000888 setFirstOut(firstOutFragments, lastInFragments, old);
George Mountc03da0e2014-08-22 17:04:02 -0700889 }
890 }
891 }
892 }
George Mount233c97c2015-12-04 18:00:55 +0000893 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700894 break;
895 }
896 case OP_REMOVE:
George Mount233c97c2015-12-04 18:00:55 +0000897 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700898 break;
899 case OP_HIDE:
George Mount233c97c2015-12-04 18:00:55 +0000900 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700901 break;
902 case OP_SHOW:
George Mount233c97c2015-12-04 18:00:55 +0000903 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700904 break;
905 case OP_DETACH:
George Mount233c97c2015-12-04 18:00:55 +0000906 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700907 break;
908 case OP_ATTACH:
George Mount233c97c2015-12-04 18:00:55 +0000909 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700910 break;
911 }
912
913 op = op.next;
914 }
George Mountc03da0e2014-08-22 17:04:02 -0700915 }
916
917 /**
918 * Finds the first removed fragment and last added fragments when popping the back stack.
919 * If none of the fragments have transitions, then both lists will be empty.
920 *
921 * @param firstOutFragments The list of first fragments to be removed, keyed on the
922 * container ID. This list will be modified by the method.
923 * @param lastInFragments The list of last fragments to be added, keyed on the
924 * container ID. This list will be modified by the method.
925 */
926 public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
927 SparseArray<Fragment> lastInFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700928 if (!mManager.mContainer.onHasView()) {
George Mount0b26e4d2014-09-17 16:36:42 -0700929 return; // nothing to see, so no transitions
930 }
George Mount233c97c2015-12-04 18:00:55 +0000931 Op op = mTail;
George Mountc03da0e2014-08-22 17:04:02 -0700932 while (op != null) {
933 switch (op.cmd) {
934 case OP_ADD:
George Mount233c97c2015-12-04 18:00:55 +0000935 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700936 break;
937 case OP_REPLACE:
938 if (op.removed != null) {
939 for (int i = op.removed.size() - 1; i >= 0; i--) {
George Mount233c97c2015-12-04 18:00:55 +0000940 setLastIn(firstOutFragments, lastInFragments, op.removed.get(i));
George Mountc03da0e2014-08-22 17:04:02 -0700941 }
942 }
George Mount233c97c2015-12-04 18:00:55 +0000943 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700944 break;
945 case OP_REMOVE:
George Mount233c97c2015-12-04 18:00:55 +0000946 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700947 break;
948 case OP_HIDE:
George Mount233c97c2015-12-04 18:00:55 +0000949 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700950 break;
951 case OP_SHOW:
George Mount233c97c2015-12-04 18:00:55 +0000952 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700953 break;
954 case OP_DETACH:
George Mount233c97c2015-12-04 18:00:55 +0000955 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700956 break;
957 case OP_ATTACH:
George Mount233c97c2015-12-04 18:00:55 +0000958 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700959 break;
960 }
961
George Mount233c97c2015-12-04 18:00:55 +0000962 op = op.prev;
George Mountc03da0e2014-08-22 17:04:02 -0700963 }
George Mountc03da0e2014-08-22 17:04:02 -0700964 }
965
966 /**
967 * When custom fragment transitions are used, this sets up the state for each transition
968 * and begins the transition. A different transition is started for each fragment container
969 * and consists of up to 3 different transitions: the exit transition, a shared element
970 * transition and an enter transition.
971 *
972 * <p>The exit transition operates against the leaf nodes of the first fragment
973 * with a view that was removed. If no such fragment was removed, then no exit
974 * transition is executed. The exit transition comes from the outgoing fragment.</p>
975 *
976 * <p>The enter transition operates against the last fragment that was added. If
977 * that fragment does not have a view or no fragment was added, then no enter
978 * transition is executed. The enter transition comes from the incoming fragment.</p>
979 *
980 * <p>The shared element transition operates against all views and comes either
981 * from the outgoing fragment or the incoming fragment, depending on whether this
982 * is going forward or popping the back stack. When going forward, the incoming
983 * fragment's enter shared element transition is used, but when going back, the
984 * outgoing fragment's return shared element transition is used. Shared element
985 * transitions only operate if there is both an incoming and outgoing fragment.</p>
986 *
987 * @param firstOutFragments The list of first fragments to be removed, keyed on the
988 * container ID.
989 * @param lastInFragments The list of last fragments to be added, keyed on the
990 * container ID.
991 * @param isBack true if this is popping the back stack or false if this is a
992 * forward operation.
993 * @return The TransitionState used to complete the operation of the transition
George Mountecfd0072014-09-07 10:06:25 -0700994 * in {@link #setNameOverrides(android.app.BackStackRecord.TransitionState, java.util.ArrayList,
995 * java.util.ArrayList)}.
George Mountc03da0e2014-08-22 17:04:02 -0700996 */
997 private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
998 SparseArray<Fragment> lastInFragments, boolean isBack) {
999 TransitionState state = new TransitionState();
1000
George Mountd4c3c912014-06-09 12:31:34 -07001001 // Adding a non-existent target view makes sure that the transitions don't target
1002 // any views by default. They'll only target the views we tell add. If we don't
1003 // add any, then no views will be targeted.
Todd Kennedya5fc6f02015-04-14 18:22:54 -07001004 state.nonExistentView = new View(mManager.mHost.getContext());
George Mountd4c3c912014-06-09 12:31:34 -07001005
George Mountc03da0e2014-08-22 17:04:02 -07001006 // Go over all leaving fragments.
1007 for (int i = 0; i < firstOutFragments.size(); i++) {
1008 int containerId = firstOutFragments.keyAt(i);
1009 configureTransitions(containerId, state, isBack, firstOutFragments,
George Mountecfd0072014-09-07 10:06:25 -07001010 lastInFragments);
George Mountd4c3c912014-06-09 12:31:34 -07001011 }
1012
George Mountc03da0e2014-08-22 17:04:02 -07001013 // Now go over all entering fragments that didn't have a leaving fragment.
1014 for (int i = 0; i < lastInFragments.size(); i++) {
1015 int containerId = lastInFragments.keyAt(i);
1016 if (firstOutFragments.get(containerId) == null) {
1017 configureTransitions(containerId, state, isBack, firstOutFragments,
George Mountecfd0072014-09-07 10:06:25 -07001018 lastInFragments);
George Mountc03da0e2014-08-22 17:04:02 -07001019 }
1020 }
George Mountd4c3c912014-06-09 12:31:34 -07001021 return state;
1022 }
1023
George Mountecfd0072014-09-07 10:06:25 -07001024 private static Transition cloneTransition(Transition transition) {
1025 if (transition != null) {
1026 transition = transition.clone();
1027 }
1028 return transition;
1029 }
1030
George Mountc03da0e2014-08-22 17:04:02 -07001031 private static Transition getEnterTransition(Fragment inFragment, boolean isBack) {
1032 if (inFragment == null) {
1033 return null;
1034 }
George Mountecfd0072014-09-07 10:06:25 -07001035 return cloneTransition(isBack ? inFragment.getReenterTransition() :
1036 inFragment.getEnterTransition());
George Mountc03da0e2014-08-22 17:04:02 -07001037 }
George Mountd4c3c912014-06-09 12:31:34 -07001038
George Mountc03da0e2014-08-22 17:04:02 -07001039 private static Transition getExitTransition(Fragment outFragment, boolean isBack) {
1040 if (outFragment == null) {
1041 return null;
1042 }
George Mountecfd0072014-09-07 10:06:25 -07001043 return cloneTransition(isBack ? outFragment.getReturnTransition() :
1044 outFragment.getExitTransition());
George Mountc03da0e2014-08-22 17:04:02 -07001045 }
1046
George Mountc855f7d2015-06-10 15:00:17 -07001047 private static TransitionSet getSharedElementTransition(Fragment inFragment,
1048 Fragment outFragment, boolean isBack) {
George Mountc03da0e2014-08-22 17:04:02 -07001049 if (inFragment == null || outFragment == null) {
1050 return null;
1051 }
George Mountc855f7d2015-06-10 15:00:17 -07001052 Transition transition = cloneTransition(isBack
1053 ? outFragment.getSharedElementReturnTransition()
1054 : inFragment.getSharedElementEnterTransition());
1055 if (transition == null) {
1056 return null;
1057 }
1058 TransitionSet transitionSet = new TransitionSet();
1059 transitionSet.addTransition(transition);
1060 return transitionSet;
George Mountc03da0e2014-08-22 17:04:02 -07001061 }
1062
George Mountecfd0072014-09-07 10:06:25 -07001063 private static ArrayList<View> captureExitingViews(Transition exitTransition,
George Mount7ce5d752014-10-30 16:09:22 -07001064 Fragment outFragment, ArrayMap<String, View> namedViews, View nonExistentView) {
George Mountecfd0072014-09-07 10:06:25 -07001065 ArrayList<View> viewList = null;
George Mountc03da0e2014-08-22 17:04:02 -07001066 if (exitTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001067 viewList = new ArrayList<View>();
George Mountc03da0e2014-08-22 17:04:02 -07001068 View root = outFragment.getView();
George Mountc03da0e2014-08-22 17:04:02 -07001069 root.captureTransitioningViews(viewList);
George Mount27bc8aa2014-09-14 16:48:21 -07001070 if (namedViews != null) {
1071 viewList.removeAll(namedViews.values());
1072 }
George Mount7ce5d752014-10-30 16:09:22 -07001073 if (!viewList.isEmpty()) {
1074 viewList.add(nonExistentView);
1075 addTargets(exitTransition, viewList);
1076 }
George Mountc03da0e2014-08-22 17:04:02 -07001077 }
George Mountecfd0072014-09-07 10:06:25 -07001078 return viewList;
George Mountc03da0e2014-08-22 17:04:02 -07001079 }
1080
1081 private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
George Mountecfd0072014-09-07 10:06:25 -07001082 boolean isBack) {
1083 ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001084 if (mSharedElementSourceNames != null) {
1085 outFragment.getView().findNamedViews(namedViews);
1086 if (isBack) {
1087 namedViews.retainAll(mSharedElementTargetNames);
1088 } else {
1089 namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
George Mountecfd0072014-09-07 10:06:25 -07001090 namedViews);
George Mountd4c3c912014-06-09 12:31:34 -07001091 }
1092 }
1093
George Mountc03da0e2014-08-22 17:04:02 -07001094 if (isBack) {
George Mount65580562014-08-29 08:15:48 -07001095 outFragment.mEnterTransitionCallback.onMapSharedElements(
George Mountc03da0e2014-08-22 17:04:02 -07001096 mSharedElementTargetNames, namedViews);
1097 setBackNameOverrides(state, namedViews, false);
George Mountd4c3c912014-06-09 12:31:34 -07001098 } else {
George Mount65580562014-08-29 08:15:48 -07001099 outFragment.mExitTransitionCallback.onMapSharedElements(
George Mountc03da0e2014-08-22 17:04:02 -07001100 mSharedElementTargetNames, namedViews);
1101 setNameOverrides(state, namedViews, false);
1102 }
1103
1104 return namedViews;
1105 }
1106
1107 /**
1108 * Prepares the enter transition by adding a non-existent view to the transition's target list
1109 * and setting it epicenter callback. By adding a non-existent view to the target list,
1110 * we can prevent any view from being targeted at the beginning of the transition.
1111 * We will add to the views before the end state of the transition is captured so that the
1112 * views will appear. At the start of the transition, we clear the list of targets so that
1113 * we can restore the state of the transition and use it again.
George Mountecfd0072014-09-07 10:06:25 -07001114 *
1115 * <p>The shared element transition maps its shared elements immediately prior to
1116 * capturing the final state of the Transition.</p>
George Mountc03da0e2014-08-22 17:04:02 -07001117 */
George Mountecfd0072014-09-07 10:06:25 -07001118 private ArrayList<View> addTransitionTargets(final TransitionState state,
George Mountc855f7d2015-06-10 15:00:17 -07001119 final Transition enterTransition, final TransitionSet sharedElementTransition,
George Mount61f83312015-09-09 16:10:29 -07001120 final Transition exitTransition, final Transition overallTransition,
1121 final View container, final Fragment inFragment, final Fragment outFragment,
George Mount2deeaa32014-09-19 10:02:02 -07001122 final ArrayList<View> hiddenFragmentViews, final boolean isBack,
1123 final ArrayList<View> sharedElementTargets) {
George Mountecfd0072014-09-07 10:06:25 -07001124 if (enterTransition == null && sharedElementTransition == null &&
1125 overallTransition == null) {
1126 return null;
1127 }
1128 final ArrayList<View> enteringViews = new ArrayList<View>();
1129 container.getViewTreeObserver().addOnPreDrawListener(
1130 new ViewTreeObserver.OnPreDrawListener() {
1131 @Override
1132 public boolean onPreDraw() {
1133 container.getViewTreeObserver().removeOnPreDrawListener(this);
1134
1135 // Don't include any newly-hidden fragments in the transition.
George Mount13d70322015-07-21 14:30:24 -07001136 if (inFragment != null) {
1137 excludeHiddenFragments(hiddenFragmentViews, inFragment.mContainerId,
1138 overallTransition);
1139 }
George Mountecfd0072014-09-07 10:06:25 -07001140
George Mount27bc8aa2014-09-14 16:48:21 -07001141 ArrayMap<String, View> namedViews = null;
George Mountecfd0072014-09-07 10:06:25 -07001142 if (sharedElementTransition != null) {
George Mount27bc8aa2014-09-14 16:48:21 -07001143 namedViews = mapSharedElementsIn(state, isBack, inFragment);
George Mount2deeaa32014-09-19 10:02:02 -07001144 removeTargets(sharedElementTransition, sharedElementTargets);
George Mount61f83312015-09-09 16:10:29 -07001145 // keep the nonExistentView as excluded so the list doesn't get emptied
1146 sharedElementTargets.remove(state.nonExistentView);
1147 excludeViews(exitTransition, sharedElementTransition,
1148 sharedElementTargets, false);
1149 excludeViews(enterTransition, sharedElementTransition,
1150 sharedElementTargets, false);
1151
George Mountc855f7d2015-06-10 15:00:17 -07001152 setSharedElementTargets(sharedElementTransition,
1153 state.nonExistentView, namedViews, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001154
1155 setEpicenterIn(namedViews, state);
1156
1157 callSharedElementEnd(state, inFragment, outFragment, isBack,
1158 namedViews);
1159 }
1160
1161 if (enterTransition != null) {
George Mountec3364c2015-06-03 15:47:44 -07001162 enterTransition.removeTarget(state.nonExistentView);
George Mountc03da0e2014-08-22 17:04:02 -07001163 View view = inFragment.getView();
1164 if (view != null) {
1165 view.captureTransitioningViews(enteringViews);
George Mount27bc8aa2014-09-14 16:48:21 -07001166 if (namedViews != null) {
1167 enteringViews.removeAll(namedViews.values());
1168 }
George Mount7ce5d752014-10-30 16:09:22 -07001169 enteringViews.add(state.nonExistentView);
1170 // We added this earlier to prevent any views being targeted.
George Mountecfd0072014-09-07 10:06:25 -07001171 addTargets(enterTransition, enteringViews);
George Mountc03da0e2014-08-22 17:04:02 -07001172 }
George Mountecfd0072014-09-07 10:06:25 -07001173 setSharedElementEpicenter(enterTransition, state);
George Mountc03da0e2014-08-22 17:04:02 -07001174 }
George Mount61f83312015-09-09 16:10:29 -07001175
1176 excludeViews(exitTransition, enterTransition, enteringViews, true);
1177 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
1178 true);
1179 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
1180 true);
George Mountecfd0072014-09-07 10:06:25 -07001181 return true;
1182 }
1183 });
1184 return enteringViews;
1185 }
1186
1187 private void callSharedElementEnd(TransitionState state, Fragment inFragment,
1188 Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) {
1189 SharedElementCallback sharedElementCallback = isBack ?
1190 outFragment.mEnterTransitionCallback :
1191 inFragment.mEnterTransitionCallback;
1192 ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
1193 ArrayList<View> views = new ArrayList<View>(namedViews.values());
1194 sharedElementCallback.onSharedElementEnd(names, views, null);
1195 }
1196
1197 private void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) {
1198 if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
1199 // now we know the epicenter of the entering transition.
1200 View epicenter = namedViews
1201 .get(mSharedElementTargetNames.get(0));
1202 if (epicenter != null) {
1203 state.enteringEpicenterView = epicenter;
1204 }
George Mountc03da0e2014-08-22 17:04:02 -07001205 }
1206 }
1207
George Mountecfd0072014-09-07 10:06:25 -07001208 private ArrayMap<String, View> mapSharedElementsIn(TransitionState state,
1209 boolean isBack, Fragment inFragment) {
1210 // Now map the shared elements in the incoming fragment
1211 ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack);
1212
1213 // remap shared elements and set the name mapping used
1214 // in the shared element transition.
1215 if (isBack) {
1216 inFragment.mExitTransitionCallback.onMapSharedElements(
1217 mSharedElementTargetNames, namedViews);
1218 setBackNameOverrides(state, namedViews, true);
1219 } else {
1220 inFragment.mEnterTransitionCallback.onMapSharedElements(
1221 mSharedElementTargetNames, namedViews);
1222 setNameOverrides(state, namedViews, true);
1223 }
1224 return namedViews;
1225 }
1226
George Mountc03da0e2014-08-22 17:04:02 -07001227 private static Transition mergeTransitions(Transition enterTransition,
1228 Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
1229 boolean isBack) {
1230 boolean overlap = true;
George Mountec3364c2015-06-03 15:47:44 -07001231 if (enterTransition != null && exitTransition != null && inFragment != null) {
George Mountc03da0e2014-08-22 17:04:02 -07001232 overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
1233 inFragment.getAllowEnterTransitionOverlap();
1234 }
1235
George Mount27bc8aa2014-09-14 16:48:21 -07001236 // Wrap the transitions. Explicit targets like in enter and exit will cause the
1237 // views to be targeted regardless of excluded views. If that happens, then the
1238 // excluded fragments views (hidden fragments) will still be in the transition.
1239
George Mountc03da0e2014-08-22 17:04:02 -07001240 Transition transition;
1241 if (overlap) {
George Mount27bc8aa2014-09-14 16:48:21 -07001242 // Regular transition -- do it all together
George Mount2deeaa32014-09-19 10:02:02 -07001243 TransitionSet transitionSet = new TransitionSet();
1244 if (enterTransition != null) {
1245 transitionSet.addTransition(enterTransition);
George Mount27bc8aa2014-09-14 16:48:21 -07001246 }
George Mount2deeaa32014-09-19 10:02:02 -07001247 if (exitTransition != null) {
1248 transitionSet.addTransition(exitTransition);
1249 }
1250 if (sharedElementTransition != null) {
1251 transitionSet.addTransition(sharedElementTransition);
1252 }
1253 transition = transitionSet;
George Mountc03da0e2014-08-22 17:04:02 -07001254 } else {
George Mount27bc8aa2014-09-14 16:48:21 -07001255 // First do exit, then enter, but allow shared element transition to happen
1256 // during both.
1257 Transition staggered = null;
1258 if (exitTransition != null && enterTransition != null) {
1259 staggered = new TransitionSet()
1260 .addTransition(exitTransition)
1261 .addTransition(enterTransition)
1262 .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
1263 } else if (exitTransition != null) {
1264 staggered = exitTransition;
1265 } else if (enterTransition != null) {
1266 staggered = enterTransition;
1267 }
1268 if (sharedElementTransition != null) {
1269 TransitionSet together = new TransitionSet();
1270 if (staggered != null) {
1271 together.addTransition(staggered);
1272 }
1273 together.addTransition(sharedElementTransition);
1274 transition = together;
1275 } else {
1276 transition = staggered;
1277 }
George Mountc03da0e2014-08-22 17:04:02 -07001278 }
1279 return transition;
1280 }
1281
1282 /**
1283 * Configures custom transitions for a specific fragment container.
1284 *
1285 * @param containerId The container ID of the fragments to configure the transition for.
George Mountecfd0072014-09-07 10:06:25 -07001286 * @param state The Transition State keeping track of the executing transitions.
George Mountc03da0e2014-08-22 17:04:02 -07001287 * @param firstOutFragments The list of first fragments to be removed, keyed on the
1288 * container ID.
1289 * @param lastInFragments The list of last fragments to be added, keyed on the
1290 * container ID.
1291 * @param isBack true if this is popping the back stack or false if this is a
1292 * forward operation.
George Mountc03da0e2014-08-22 17:04:02 -07001293 */
1294 private void configureTransitions(int containerId, TransitionState state, boolean isBack,
George Mountecfd0072014-09-07 10:06:25 -07001295 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -07001296 ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
George Mountc03da0e2014-08-22 17:04:02 -07001297 if (sceneRoot != null) {
1298 Fragment inFragment = lastInFragments.get(containerId);
1299 Fragment outFragment = firstOutFragments.get(containerId);
1300
1301 Transition enterTransition = getEnterTransition(inFragment, isBack);
George Mountc855f7d2015-06-10 15:00:17 -07001302 TransitionSet sharedElementTransition =
1303 getSharedElementTransition(inFragment, outFragment, isBack);
George Mountc03da0e2014-08-22 17:04:02 -07001304 Transition exitTransition = getExitTransition(outFragment, isBack);
George Mountc03da0e2014-08-22 17:04:02 -07001305
George Mountecfd0072014-09-07 10:06:25 -07001306 if (enterTransition == null && sharedElementTransition == null &&
1307 exitTransition == null) {
1308 return; // no transitions!
1309 }
George Mount27bc8aa2014-09-14 16:48:21 -07001310 if (enterTransition != null) {
1311 enterTransition.addTarget(state.nonExistentView);
George Mountc03da0e2014-08-22 17:04:02 -07001312 }
George Mountecfd0072014-09-07 10:06:25 -07001313 ArrayMap<String, View> namedViews = null;
George Mount2deeaa32014-09-19 10:02:02 -07001314 ArrayList<View> sharedElementTargets = new ArrayList<View>();
George Mountecfd0072014-09-07 10:06:25 -07001315 if (sharedElementTransition != null) {
1316 namedViews = remapSharedElements(state, outFragment, isBack);
George Mountc855f7d2015-06-10 15:00:17 -07001317 setSharedElementTargets(sharedElementTransition,
1318 state.nonExistentView, namedViews, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001319
1320 // Notify the start of the transition.
1321 SharedElementCallback callback = isBack ?
1322 outFragment.mEnterTransitionCallback :
1323 inFragment.mEnterTransitionCallback;
1324 ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
1325 ArrayList<View> views = new ArrayList<View>(namedViews.values());
1326 callback.onSharedElementStart(names, views, null);
1327 }
George Mountc03da0e2014-08-22 17:04:02 -07001328
George Mount27bc8aa2014-09-14 16:48:21 -07001329 ArrayList<View> exitingViews = captureExitingViews(exitTransition, outFragment,
George Mount7ce5d752014-10-30 16:09:22 -07001330 namedViews, state.nonExistentView);
George Mount27bc8aa2014-09-14 16:48:21 -07001331 if (exitingViews == null || exitingViews.isEmpty()) {
1332 exitTransition = null;
1333 }
George Mount61f83312015-09-09 16:10:29 -07001334 excludeViews(enterTransition, exitTransition, exitingViews, true);
1335 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
1336 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
George Mount27bc8aa2014-09-14 16:48:21 -07001337
George Mountc03da0e2014-08-22 17:04:02 -07001338 // Set the epicenter of the exit transition
George Mount27bc8aa2014-09-14 16:48:21 -07001339 if (mSharedElementTargetNames != null && namedViews != null) {
George Mountc03da0e2014-08-22 17:04:02 -07001340 View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
1341 if (epicenterView != null) {
George Mount27bc8aa2014-09-14 16:48:21 -07001342 if (exitTransition != null) {
1343 setEpicenter(exitTransition, epicenterView);
1344 }
1345 if (sharedElementTransition != null) {
1346 setEpicenter(sharedElementTransition, epicenterView);
1347 }
George Mountc03da0e2014-08-22 17:04:02 -07001348 }
1349 }
1350
George Mountc03da0e2014-08-22 17:04:02 -07001351 Transition transition = mergeTransitions(enterTransition, exitTransition,
1352 sharedElementTransition, inFragment, isBack);
1353
1354 if (transition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001355 ArrayList<View> hiddenFragments = new ArrayList<View>();
1356 ArrayList<View> enteringViews = addTransitionTargets(state, enterTransition,
George Mount61f83312015-09-09 16:10:29 -07001357 sharedElementTransition, exitTransition, transition, sceneRoot, inFragment,
George Mountc855f7d2015-06-10 15:00:17 -07001358 outFragment, hiddenFragments, isBack, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001359
George Mountc03da0e2014-08-22 17:04:02 -07001360 transition.setNameOverrides(state.nameOverrides);
1361 // We want to exclude hidden views later, so we need a non-null list in the
1362 // transition now.
1363 transition.excludeTarget(state.nonExistentView, true);
1364 // Now exclude all currently hidden fragments.
George Mountecfd0072014-09-07 10:06:25 -07001365 excludeHiddenFragments(hiddenFragments, containerId, transition);
George Mountc03da0e2014-08-22 17:04:02 -07001366 TransitionManager.beginDelayedTransition(sceneRoot, transition);
George Mountecfd0072014-09-07 10:06:25 -07001367 // Remove the view targeting after the transition starts
1368 removeTargetedViewsFromTransitions(sceneRoot, state.nonExistentView,
1369 enterTransition, enteringViews, exitTransition, exitingViews,
George Mountc855f7d2015-06-10 15:00:17 -07001370 sharedElementTransition, sharedElementTargets, transition,
1371 hiddenFragments);
George Mountd4c3c912014-06-09 12:31:34 -07001372 }
1373 }
1374 }
1375
George Mountc03da0e2014-08-22 17:04:02 -07001376 /**
George Mountc855f7d2015-06-10 15:00:17 -07001377 * Finds all children of the shared elements and sets the wrapping TransitionSet
1378 * targets to point to those. It also limits transitions that have no targets to the
1379 * specific shared elements. This allows developers to target child views of the
1380 * shared elements specifically, but this doesn't happen by default.
1381 */
1382 private static void setSharedElementTargets(TransitionSet transition,
1383 View nonExistentView, ArrayMap<String, View> namedViews,
1384 ArrayList<View> sharedElementTargets) {
1385 sharedElementTargets.clear();
1386 sharedElementTargets.addAll(namedViews.values());
1387
1388 final List<View> views = transition.getTargets();
1389 views.clear();
1390 final int count = sharedElementTargets.size();
1391 for (int i = 0; i < count; i++) {
1392 final View view = sharedElementTargets.get(i);
1393 bfsAddViewChildren(views, view);
1394 }
1395 sharedElementTargets.add(nonExistentView);
1396 addTargets(transition, sharedElementTargets);
1397 }
1398
1399 /**
1400 * Uses a breadth-first scheme to add startView and all of its children to views.
1401 * It won't add a child if it is already in views.
1402 */
1403 private static void bfsAddViewChildren(final List<View> views, final View startView) {
1404 final int startIndex = views.size();
1405 if (containedBeforeIndex(views, startView, startIndex)) {
1406 return; // This child is already in the list, so all its children are also.
1407 }
1408 views.add(startView);
1409 for (int index = startIndex; index < views.size(); index++) {
1410 final View view = views.get(index);
1411 if (view instanceof ViewGroup) {
1412 ViewGroup viewGroup = (ViewGroup) view;
1413 final int childCount = viewGroup.getChildCount();
1414 for (int childIndex = 0; childIndex < childCount; childIndex++) {
1415 final View child = viewGroup.getChildAt(childIndex);
1416 if (!containedBeforeIndex(views, child, startIndex)) {
1417 views.add(child);
1418 }
1419 }
1420 }
1421 }
1422 }
1423
1424 /**
1425 * Does a linear search through views for view, limited to maxIndex.
1426 */
1427 private static boolean containedBeforeIndex(final List<View> views, final View view,
1428 final int maxIndex) {
1429 for (int i = 0; i < maxIndex; i++) {
1430 if (views.get(i) == view) {
1431 return true;
1432 }
1433 }
1434 return false;
1435 }
1436
George Mount61f83312015-09-09 16:10:29 -07001437 private static void excludeViews(Transition transition, Transition fromTransition,
1438 ArrayList<View> views, boolean exclude) {
1439 if (transition != null) {
1440 final int viewCount = fromTransition == null ? 0 : views.size();
1441 for (int i = 0; i < viewCount; i++) {
1442 transition.excludeTarget(views.get(i), exclude);
1443 }
1444 }
1445 }
1446
George Mountc855f7d2015-06-10 15:00:17 -07001447 /**
George Mountecfd0072014-09-07 10:06:25 -07001448 * After the transition has started, remove all targets that we added to the transitions
1449 * so that the transitions are left in a clean state.
1450 */
1451 private void removeTargetedViewsFromTransitions(
1452 final ViewGroup sceneRoot, final View nonExistingView,
1453 final Transition enterTransition, final ArrayList<View> enteringViews,
1454 final Transition exitTransition, final ArrayList<View> exitingViews,
George Mount2deeaa32014-09-19 10:02:02 -07001455 final Transition sharedElementTransition, final ArrayList<View> sharedElementTargets,
George Mountecfd0072014-09-07 10:06:25 -07001456 final Transition overallTransition, final ArrayList<View> hiddenViews) {
George Mount2deeaa32014-09-19 10:02:02 -07001457 if (overallTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001458 sceneRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
1459 @Override
1460 public boolean onPreDraw() {
1461 sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
1462 if (enterTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001463 removeTargets(enterTransition, enteringViews);
George Mount61f83312015-09-09 16:10:29 -07001464 excludeViews(enterTransition, exitTransition, exitingViews, false);
1465 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
1466 false);
George Mountecfd0072014-09-07 10:06:25 -07001467 }
1468 if (exitTransition != null) {
1469 removeTargets(exitTransition, exitingViews);
George Mount61f83312015-09-09 16:10:29 -07001470 excludeViews(exitTransition, enterTransition, enteringViews, false);
1471 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
1472 false);
George Mountecfd0072014-09-07 10:06:25 -07001473 }
George Mount2deeaa32014-09-19 10:02:02 -07001474 if (sharedElementTransition != null) {
1475 removeTargets(sharedElementTransition, sharedElementTargets);
1476 }
George Mountecfd0072014-09-07 10:06:25 -07001477 int numViews = hiddenViews.size();
1478 for (int i = 0; i < numViews; i++) {
1479 overallTransition.excludeTarget(hiddenViews.get(i), false);
1480 }
1481 overallTransition.excludeTarget(nonExistingView, false);
1482 return true;
1483 }
1484 });
1485 }
1486 }
1487
George Mount7ce5d752014-10-30 16:09:22 -07001488 /**
1489 * This method removes the views from transitions that target ONLY those views.
1490 * The views list should match those added in addTargets and should contain
1491 * one view that is not in the view hierarchy (state.nonExistentView).
1492 */
1493 public static void removeTargets(Transition transition, ArrayList<View> views) {
1494 if (transition instanceof TransitionSet) {
1495 TransitionSet set = (TransitionSet) transition;
1496 int numTransitions = set.getTransitionCount();
1497 for (int i = 0; i < numTransitions; i++) {
1498 Transition child = set.getTransitionAt(i);
1499 removeTargets(child, views);
1500 }
1501 } else if (!hasSimpleTarget(transition)) {
1502 List<View> targets = transition.getTargets();
1503 if (targets != null && targets.size() == views.size() &&
1504 targets.containsAll(views)) {
1505 // We have an exact match. We must have added these earlier in addTargets
1506 for (int i = views.size() - 1; i >= 0; i--) {
1507 transition.removeTarget(views.get(i));
1508 }
1509 }
George Mountecfd0072014-09-07 10:06:25 -07001510 }
1511 }
1512
George Mount7ce5d752014-10-30 16:09:22 -07001513 /**
1514 * This method adds views as targets to the transition, but only if the transition
1515 * doesn't already have a target. It is best for views to contain one View object
1516 * that does not exist in the view hierarchy (state.nonExistentView) so that
1517 * when they are removed later, a list match will suffice to remove the targets.
1518 * Otherwise, if you happened to have targeted the exact views for the transition,
1519 * the removeTargets call will remove them unexpectedly.
1520 */
1521 public static void addTargets(Transition transition, ArrayList<View> views) {
1522 if (transition instanceof TransitionSet) {
1523 TransitionSet set = (TransitionSet) transition;
1524 int numTransitions = set.getTransitionCount();
1525 for (int i = 0; i < numTransitions; i++) {
1526 Transition child = set.getTransitionAt(i);
1527 addTargets(child, views);
1528 }
1529 } else if (!hasSimpleTarget(transition)) {
1530 List<View> targets = transition.getTargets();
1531 if (isNullOrEmpty(targets)) {
1532 // We can just add the target views
1533 int numViews = views.size();
1534 for (int i = 0; i < numViews; i++) {
1535 transition.addTarget(views.get(i));
1536 }
1537 }
George Mountecfd0072014-09-07 10:06:25 -07001538 }
1539 }
1540
George Mount7ce5d752014-10-30 16:09:22 -07001541 private static boolean hasSimpleTarget(Transition transition) {
1542 return !isNullOrEmpty(transition.getTargetIds()) ||
1543 !isNullOrEmpty(transition.getTargetNames()) ||
1544 !isNullOrEmpty(transition.getTargetTypes());
1545 }
1546
1547 private static boolean isNullOrEmpty(List list) {
1548 return list == null || list.isEmpty();
1549 }
1550
George Mountecfd0072014-09-07 10:06:25 -07001551 /**
George Mountc03da0e2014-08-22 17:04:02 -07001552 * Remaps a name-to-View map, substituting different names for keys.
1553 *
1554 * @param inMap A list of keys found in the map, in the order in toGoInMap
1555 * @param toGoInMap A list of keys to use for the new map, in the order of inMap
1556 * @param namedViews The current mapping
George Mountecfd0072014-09-07 10:06:25 -07001557 * @return a new Map after it has been mapped with the new names as keys.
George Mountc03da0e2014-08-22 17:04:02 -07001558 */
1559 private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
George Mountecfd0072014-09-07 10:06:25 -07001560 ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) {
1561 ArrayMap<String, View> remappedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001562 if (!namedViews.isEmpty()) {
1563 int numKeys = inMap.size();
1564 for (int i = 0; i < numKeys; i++) {
1565 View view = namedViews.get(inMap.get(i));
George Mountecfd0072014-09-07 10:06:25 -07001566
George Mountc03da0e2014-08-22 17:04:02 -07001567 if (view != null) {
George Mountecfd0072014-09-07 10:06:25 -07001568 remappedViews.put(toGoInMap.get(i), view);
George Mountd4c3c912014-06-09 12:31:34 -07001569 }
1570 }
1571 }
George Mountecfd0072014-09-07 10:06:25 -07001572 return remappedViews;
George Mountc03da0e2014-08-22 17:04:02 -07001573 }
1574
1575 /**
George Mountecfd0072014-09-07 10:06:25 -07001576 * Maps shared elements to views in the entering fragment.
George Mountc03da0e2014-08-22 17:04:02 -07001577 *
1578 * @param state The transition State as returned from {@link #beginTransition(
1579 * android.util.SparseArray, android.util.SparseArray, boolean)}.
George Mountecfd0072014-09-07 10:06:25 -07001580 * @param inFragment The last fragment to be added.
George Mountc03da0e2014-08-22 17:04:02 -07001581 * @param isBack true if this is popping the back stack or false if this is a
1582 * forward operation.
1583 */
George Mountecfd0072014-09-07 10:06:25 -07001584 private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state,
1585 Fragment inFragment, boolean isBack) {
1586 ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001587 View root = inFragment.getView();
1588 if (root != null) {
1589 if (mSharedElementSourceNames != null) {
1590 root.findNamedViews(namedViews);
1591 if (isBack) {
1592 namedViews = remapNames(mSharedElementSourceNames,
George Mountecfd0072014-09-07 10:06:25 -07001593 mSharedElementTargetNames, namedViews);
George Mountc03da0e2014-08-22 17:04:02 -07001594 } else {
1595 namedViews.retainAll(mSharedElementTargetNames);
1596 }
1597 }
1598 }
1599 return namedViews;
1600 }
1601
George Mountecfd0072014-09-07 10:06:25 -07001602 private void excludeHiddenFragments(final ArrayList<View> hiddenFragmentViews, int containerId,
George Mountc03da0e2014-08-22 17:04:02 -07001603 Transition transition) {
1604 if (mManager.mAdded != null) {
1605 for (int i = 0; i < mManager.mAdded.size(); i++) {
1606 Fragment fragment = mManager.mAdded.get(i);
1607 if (fragment.mView != null && fragment.mContainer != null &&
1608 fragment.mContainerId == containerId) {
1609 if (fragment.mHidden) {
George Mountecfd0072014-09-07 10:06:25 -07001610 if (!hiddenFragmentViews.contains(fragment.mView)) {
George Mountc03da0e2014-08-22 17:04:02 -07001611 transition.excludeTarget(fragment.mView, true);
George Mountecfd0072014-09-07 10:06:25 -07001612 hiddenFragmentViews.add(fragment.mView);
George Mountc03da0e2014-08-22 17:04:02 -07001613 }
1614 } else {
1615 transition.excludeTarget(fragment.mView, false);
George Mountecfd0072014-09-07 10:06:25 -07001616 hiddenFragmentViews.remove(fragment.mView);
George Mountc03da0e2014-08-22 17:04:02 -07001617 }
1618 }
1619 }
George Mountd4c3c912014-06-09 12:31:34 -07001620 }
1621 }
1622
George Mountd4c3c912014-06-09 12:31:34 -07001623 private static void setEpicenter(Transition transition, View view) {
1624 final Rect epicenter = new Rect();
1625 view.getBoundsOnScreen(epicenter);
1626
1627 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1628 @Override
1629 public Rect onGetEpicenter(Transition transition) {
1630 return epicenter;
1631 }
1632 });
1633 }
1634
1635 private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
1636 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1637 private Rect mEpicenter;
1638
1639 @Override
1640 public Rect onGetEpicenter(Transition transition) {
George Mountc03da0e2014-08-22 17:04:02 -07001641 if (mEpicenter == null && state.enteringEpicenterView != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001642 mEpicenter = new Rect();
George Mountc03da0e2014-08-22 17:04:02 -07001643 state.enteringEpicenterView.getBoundsOnScreen(mEpicenter);
George Mountd4c3c912014-06-09 12:31:34 -07001644 }
1645 return mEpicenter;
1646 }
1647 });
1648 }
1649
George Mountc03da0e2014-08-22 17:04:02 -07001650 public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
1651 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001652 if (FragmentManagerImpl.DEBUG) {
1653 Log.v(TAG, "popFromBackStack: " + this);
1654 LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001655 PrintWriter pw = new FastPrintWriter(logw, false, 1024);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001656 dump(" ", null, pw, null);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001657 pw.flush();
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001658 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001659
George Mounta7245b42016-02-10 17:03:32 -08001660 if (mManager.mCurState >= Fragment.CREATED) {
1661 if (state == null) {
1662 if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
1663 state = beginTransition(firstOutFragments, lastInFragments, true);
1664 }
1665 } else if (!doStateMove) {
1666 setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
George Mountc03da0e2014-08-22 17:04:02 -07001667 }
George Mountd4c3c912014-06-09 12:31:34 -07001668 }
1669
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001670 bumpBackStackNesting(-1);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001671
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001672 Op op = mTail;
1673 while (op != null) {
1674 switch (op.cmd) {
1675 case OP_ADD: {
1676 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001677 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001678 mManager.removeFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001679 FragmentManagerImpl.reverseTransit(mTransition),
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001680 mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001681 }
1682 break;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001683 case OP_REPLACE: {
1684 Fragment f = op.fragment;
Dianne Hackbornee76efb2012-06-05 10:27:40 -07001685 if (f != null) {
1686 f.mNextAnim = op.popExitAnim;
1687 mManager.removeFragment(f,
1688 FragmentManagerImpl.reverseTransit(mTransition),
1689 mTransitionStyle);
1690 }
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001691 if (op.removed != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001692 for (int i = 0; i < op.removed.size(); i++) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001693 Fragment old = op.removed.get(i);
Chet Haasebc377842011-03-22 11:35:22 -07001694 old.mNextAnim = op.popEnterAnim;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001695 mManager.addFragment(old, false);
1696 }
1697 }
George Mountd4c3c912014-06-09 12:31:34 -07001698 }
1699 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001700 case OP_REMOVE: {
1701 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001702 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001703 mManager.addFragment(f, false);
George Mountd4c3c912014-06-09 12:31:34 -07001704 }
1705 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001706 case OP_HIDE: {
1707 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001708 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001709 mManager.showFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001710 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001711 }
1712 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001713 case OP_SHOW: {
1714 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001715 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001716 mManager.hideFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001717 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001718 }
1719 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001720 case OP_DETACH: {
1721 Fragment f = op.fragment;
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001722 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001723 mManager.attachFragment(f,
1724 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001725 }
1726 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001727 case OP_ATTACH: {
1728 Fragment f = op.fragment;
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001729 f.mNextAnim = op.popExitAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001730 mManager.detachFragment(f,
1731 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001732 }
1733 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001734 default: {
1735 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
1736 }
1737 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001738
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001739 op = op.prev;
1740 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001741
Dianne Hackborn5f36c962010-08-26 15:54:17 -07001742 if (doStateMove) {
1743 mManager.moveToState(mManager.mCurState,
1744 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
George Mountecfd0072014-09-07 10:06:25 -07001745 state = null;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001746 }
Dianne Hackborndd913a52010-07-22 12:17:04 -07001747
1748 if (mIndex >= 0) {
1749 mManager.freeBackStackIndex(mIndex);
1750 mIndex = -1;
1751 }
George Mountd4c3c912014-06-09 12:31:34 -07001752 return state;
1753 }
1754
George Mountc03da0e2014-08-22 17:04:02 -07001755 private static void setNameOverride(ArrayMap<String, String> overrides,
1756 String source, String target) {
1757 if (source != null && target != null && !source.equals(target)) {
1758 for (int index = 0; index < overrides.size(); index++) {
1759 if (source.equals(overrides.valueAt(index))) {
1760 overrides.setValueAt(index, target);
1761 return;
1762 }
George Mountd4c3c912014-06-09 12:31:34 -07001763 }
George Mountc03da0e2014-08-22 17:04:02 -07001764 overrides.put(source, target);
George Mountd4c3c912014-06-09 12:31:34 -07001765 }
George Mountd4c3c912014-06-09 12:31:34 -07001766 }
1767
1768 private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
1769 ArrayList<String> targetNames) {
George Mount13d70322015-07-21 14:30:24 -07001770 if (sourceNames != null && targetNames != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001771 for (int i = 0; i < sourceNames.size(); i++) {
1772 String source = sourceNames.get(i);
1773 String target = targetNames.get(i);
George Mountc03da0e2014-08-22 17:04:02 -07001774 setNameOverride(state.nameOverrides, source, target);
1775 }
1776 }
1777 }
1778
1779 private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1780 boolean isEnd) {
George Mount13d70322015-07-21 14:30:24 -07001781 int targetCount = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
1782 int sourceCount = mSharedElementSourceNames == null ? 0 : mSharedElementSourceNames.size();
1783 final int count = Math.min(targetCount, sourceCount);
George Mountc03da0e2014-08-22 17:04:02 -07001784 for (int i = 0; i < count; i++) {
1785 String source = mSharedElementSourceNames.get(i);
1786 String originalTarget = mSharedElementTargetNames.get(i);
George Mountecfd0072014-09-07 10:06:25 -07001787 View view = namedViews.get(originalTarget);
1788 if (view != null) {
1789 String target = view.getTransitionName();
1790 if (isEnd) {
1791 setNameOverride(state.nameOverrides, source, target);
1792 } else {
1793 setNameOverride(state.nameOverrides, target, source);
1794 }
George Mountc03da0e2014-08-22 17:04:02 -07001795 }
1796 }
1797 }
1798
1799 private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1800 boolean isEnd) {
George Mountec3364c2015-06-03 15:47:44 -07001801 int count = namedViews == null ? 0 : namedViews.size();
George Mountc03da0e2014-08-22 17:04:02 -07001802 for (int i = 0; i < count; i++) {
1803 String source = namedViews.keyAt(i);
1804 String target = namedViews.valueAt(i).getTransitionName();
1805 if (isEnd) {
1806 setNameOverride(state.nameOverrides, source, target);
1807 } else {
1808 setNameOverride(state.nameOverrides, target, source);
George Mountd4c3c912014-06-09 12:31:34 -07001809 }
1810 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001811 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001812
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001813 public String getName() {
1814 return mName;
1815 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001816
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001817 public int getTransition() {
1818 return mTransition;
1819 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001820
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001821 public int getTransitionStyle() {
1822 return mTransitionStyle;
1823 }
Adam Powell2b6230e2010-09-07 17:55:25 -07001824
1825 public boolean isEmpty() {
1826 return mNumOp == 0;
1827 }
George Mountd4c3c912014-06-09 12:31:34 -07001828
1829 public class TransitionState {
George Mountc03da0e2014-08-22 17:04:02 -07001830 public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
George Mountc03da0e2014-08-22 17:04:02 -07001831 public View enteringEpicenterView;
1832 public View nonExistentView;
George Mountd4c3c912014-06-09 12:31:34 -07001833 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001834}