blob: 4b0dfc7e0a8b87ae387d217ace18539df380bdfe [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 com.android.internal.util.FastPrintWriter;
20
21import android.graphics.Rect;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070022import android.os.Parcel;
23import android.os.Parcelable;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070024import android.text.TextUtils;
George Mountd4c3c912014-06-09 12:31:34 -070025import android.transition.Transition;
George Mountd4c3c912014-06-09 12:31:34 -070026import android.transition.TransitionManager;
27import android.transition.TransitionSet;
28import android.util.ArrayMap;
Dianne Hackborn445646c2010-06-25 15:52:59 -070029import android.util.Log;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -070030import android.util.LogWriter;
George Mountc03da0e2014-08-22 17:04:02 -070031import android.util.SparseArray;
George Mountd4c3c912014-06-09 12:31:34 -070032import android.view.View;
33import android.view.ViewGroup;
George Mountc03da0e2014-08-22 17:04:02 -070034import android.view.ViewTreeObserver;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070035
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 Mountc03da0e2014-08-22 17:04:02 -0700720 SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
721 SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
George Mountc03da0e2014-08-22 17:04:02 -0700722 calculateFragments(firstOutFragments, lastInFragments);
George Mountecfd0072014-09-07 10:06:25 -0700723 beginTransition(firstOutFragments, lastInFragments, false);
George Mountd4c3c912014-06-09 12:31:34 -0700724
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700725 Op op = mHead;
726 while (op != null) {
727 switch (op.cmd) {
728 case OP_ADD: {
729 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700730 f.mNextAnim = op.enterAnim;
731 mManager.addFragment(f, false);
George Mountd4c3c912014-06-09 12:31:34 -0700732 }
733 break;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700734 case OP_REPLACE: {
735 Fragment f = op.fragment;
Eddie Ringle22369d52014-06-29 16:43:28 -0400736 int containerId = f.mContainerId;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700737 if (mManager.mAdded != null) {
Aurimas Liutikas61fa2d42016-01-05 15:58:29 -0800738 for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700739 Fragment old = mManager.mAdded.get(i);
George Mountd4c3c912014-06-09 12:31:34 -0700740 if (FragmentManagerImpl.DEBUG) {
741 Log.v(TAG,
742 "OP_REPLACE: adding=" + f + " old=" + old);
743 }
Eddie Ringle22369d52014-06-29 16:43:28 -0400744 if (old.mContainerId == containerId) {
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700745 if (old == f) {
746 op.fragment = f = null;
747 } else {
748 if (op.removed == null) {
749 op.removed = new ArrayList<Fragment>();
750 }
751 op.removed.add(old);
752 old.mNextAnim = op.exitAnim;
753 if (mAddToBackStack) {
754 old.mBackStackNesting += 1;
George Mountd4c3c912014-06-09 12:31:34 -0700755 if (FragmentManagerImpl.DEBUG) {
756 Log.v(TAG, "Bump nesting of "
757 + old + " to " + old.mBackStackNesting);
758 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700759 }
760 mManager.removeFragment(old, mTransition, mTransitionStyle);
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700761 }
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700762 }
763 }
764 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700765 if (f != null) {
766 f.mNextAnim = op.enterAnim;
767 mManager.addFragment(f, false);
768 }
George Mountd4c3c912014-06-09 12:31:34 -0700769 }
770 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700771 case OP_REMOVE: {
772 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700773 f.mNextAnim = op.exitAnim;
774 mManager.removeFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700775 }
776 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700777 case OP_HIDE: {
778 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700779 f.mNextAnim = op.exitAnim;
780 mManager.hideFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700781 }
782 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700783 case OP_SHOW: {
784 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700785 f.mNextAnim = op.enterAnim;
786 mManager.showFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700787 }
788 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700789 case OP_DETACH: {
790 Fragment f = op.fragment;
791 f.mNextAnim = op.exitAnim;
792 mManager.detachFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700793 }
794 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700795 case OP_ATTACH: {
796 Fragment f = op.fragment;
797 f.mNextAnim = op.enterAnim;
798 mManager.attachFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700799 }
800 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700801 default: {
802 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
803 }
804 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700805
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700806 op = op.next;
807 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700808
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700809 mManager.moveToState(mManager.mCurState, mTransition,
810 mTransitionStyle, true);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700811
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700812 if (mAddToBackStack) {
813 mManager.addBackStackState(this);
814 }
815 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700816
George Mount233c97c2015-12-04 18:00:55 +0000817 private static void setFirstOut(SparseArray<Fragment> firstOutFragments,
818 SparseArray<Fragment> lastInFragments, Fragment fragment) {
George Mountc03da0e2014-08-22 17:04:02 -0700819 if (fragment != null) {
820 int containerId = fragment.mContainerId;
George Mount233c97c2015-12-04 18:00:55 +0000821 if (containerId != 0 && !fragment.isHidden()) {
822 if (fragment.isAdded() && fragment.getView() != null
823 && firstOutFragments.get(containerId) == null) {
824 firstOutFragments.put(containerId, fragment);
825 }
826 if (lastInFragments.get(containerId) == fragment) {
827 lastInFragments.remove(containerId);
828 }
George Mountc03da0e2014-08-22 17:04:02 -0700829 }
George Mountd4c3c912014-06-09 12:31:34 -0700830 }
George Mountc03da0e2014-08-22 17:04:02 -0700831 }
George Mountd4c3c912014-06-09 12:31:34 -0700832
George Mount233c97c2015-12-04 18:00:55 +0000833 private void setLastIn(SparseArray<Fragment> firstOutFragments,
834 SparseArray<Fragment> lastInFragments, Fragment fragment) {
George Mountc03da0e2014-08-22 17:04:02 -0700835 if (fragment != null) {
836 int containerId = fragment.mContainerId;
837 if (containerId != 0) {
George Mount233c97c2015-12-04 18:00:55 +0000838 if (!fragment.isAdded()) {
839 lastInFragments.put(containerId, fragment);
840 }
841 if (firstOutFragments.get(containerId) == fragment) {
842 firstOutFragments.remove(containerId);
843 }
George Mountc03da0e2014-08-22 17:04:02 -0700844 }
845 }
846 }
847
848 /**
849 * Finds the first removed fragment and last added fragments when going forward.
850 * If none of the fragments have transitions, then both lists will be empty.
851 *
852 * @param firstOutFragments The list of first fragments to be removed, keyed on the
853 * container ID. This list will be modified by the method.
854 * @param lastInFragments The list of last fragments to be added, keyed on the
855 * container ID. This list will be modified by the method.
856 */
857 private void calculateFragments(SparseArray<Fragment> firstOutFragments,
858 SparseArray<Fragment> lastInFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700859 if (!mManager.mContainer.onHasView()) {
George Mount0b26e4d2014-09-17 16:36:42 -0700860 return; // nothing to see, so no transitions
861 }
George Mountc03da0e2014-08-22 17:04:02 -0700862 Op op = mHead;
863 while (op != null) {
864 switch (op.cmd) {
865 case OP_ADD:
George Mount233c97c2015-12-04 18:00:55 +0000866 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700867 break;
868 case OP_REPLACE: {
869 Fragment f = op.fragment;
870 if (mManager.mAdded != null) {
871 for (int i = 0; i < mManager.mAdded.size(); i++) {
872 Fragment old = mManager.mAdded.get(i);
873 if (f == null || old.mContainerId == f.mContainerId) {
874 if (old == f) {
875 f = null;
George Mount233c97c2015-12-04 18:00:55 +0000876 lastInFragments.remove(old.mContainerId);
George Mountc03da0e2014-08-22 17:04:02 -0700877 } else {
George Mount233c97c2015-12-04 18:00:55 +0000878 setFirstOut(firstOutFragments, lastInFragments, old);
George Mountc03da0e2014-08-22 17:04:02 -0700879 }
880 }
881 }
882 }
George Mount233c97c2015-12-04 18:00:55 +0000883 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700884 break;
885 }
886 case OP_REMOVE:
George Mount233c97c2015-12-04 18:00:55 +0000887 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700888 break;
889 case OP_HIDE:
George Mount233c97c2015-12-04 18:00:55 +0000890 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700891 break;
892 case OP_SHOW:
George Mount233c97c2015-12-04 18:00:55 +0000893 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700894 break;
895 case OP_DETACH:
George Mount233c97c2015-12-04 18:00:55 +0000896 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700897 break;
898 case OP_ATTACH:
George Mount233c97c2015-12-04 18:00:55 +0000899 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700900 break;
901 }
902
903 op = op.next;
904 }
George Mountc03da0e2014-08-22 17:04:02 -0700905 }
906
907 /**
908 * Finds the first removed fragment and last added fragments when popping the back stack.
909 * If none of the fragments have transitions, then both lists will be empty.
910 *
911 * @param firstOutFragments The list of first fragments to be removed, keyed on the
912 * container ID. This list will be modified by the method.
913 * @param lastInFragments The list of last fragments to be added, keyed on the
914 * container ID. This list will be modified by the method.
915 */
916 public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
917 SparseArray<Fragment> lastInFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700918 if (!mManager.mContainer.onHasView()) {
George Mount0b26e4d2014-09-17 16:36:42 -0700919 return; // nothing to see, so no transitions
920 }
George Mount233c97c2015-12-04 18:00:55 +0000921 Op op = mTail;
George Mountc03da0e2014-08-22 17:04:02 -0700922 while (op != null) {
923 switch (op.cmd) {
924 case OP_ADD:
George Mount233c97c2015-12-04 18:00:55 +0000925 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700926 break;
927 case OP_REPLACE:
928 if (op.removed != null) {
929 for (int i = op.removed.size() - 1; i >= 0; i--) {
George Mount233c97c2015-12-04 18:00:55 +0000930 setLastIn(firstOutFragments, lastInFragments, op.removed.get(i));
George Mountc03da0e2014-08-22 17:04:02 -0700931 }
932 }
George Mount233c97c2015-12-04 18:00:55 +0000933 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700934 break;
935 case OP_REMOVE:
George Mount233c97c2015-12-04 18:00:55 +0000936 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700937 break;
938 case OP_HIDE:
George Mount233c97c2015-12-04 18:00:55 +0000939 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700940 break;
941 case OP_SHOW:
George Mount233c97c2015-12-04 18:00:55 +0000942 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700943 break;
944 case OP_DETACH:
George Mount233c97c2015-12-04 18:00:55 +0000945 setLastIn(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700946 break;
947 case OP_ATTACH:
George Mount233c97c2015-12-04 18:00:55 +0000948 setFirstOut(firstOutFragments, lastInFragments, op.fragment);
George Mountc03da0e2014-08-22 17:04:02 -0700949 break;
950 }
951
George Mount233c97c2015-12-04 18:00:55 +0000952 op = op.prev;
George Mountc03da0e2014-08-22 17:04:02 -0700953 }
George Mountc03da0e2014-08-22 17:04:02 -0700954 }
955
956 /**
957 * When custom fragment transitions are used, this sets up the state for each transition
958 * and begins the transition. A different transition is started for each fragment container
959 * and consists of up to 3 different transitions: the exit transition, a shared element
960 * transition and an enter transition.
961 *
962 * <p>The exit transition operates against the leaf nodes of the first fragment
963 * with a view that was removed. If no such fragment was removed, then no exit
964 * transition is executed. The exit transition comes from the outgoing fragment.</p>
965 *
966 * <p>The enter transition operates against the last fragment that was added. If
967 * that fragment does not have a view or no fragment was added, then no enter
968 * transition is executed. The enter transition comes from the incoming fragment.</p>
969 *
970 * <p>The shared element transition operates against all views and comes either
971 * from the outgoing fragment or the incoming fragment, depending on whether this
972 * is going forward or popping the back stack. When going forward, the incoming
973 * fragment's enter shared element transition is used, but when going back, the
974 * outgoing fragment's return shared element transition is used. Shared element
975 * transitions only operate if there is both an incoming and outgoing fragment.</p>
976 *
977 * @param firstOutFragments The list of first fragments to be removed, keyed on the
978 * container ID.
979 * @param lastInFragments The list of last fragments to be added, keyed on the
980 * container ID.
981 * @param isBack true if this is popping the back stack or false if this is a
982 * forward operation.
983 * @return The TransitionState used to complete the operation of the transition
George Mountecfd0072014-09-07 10:06:25 -0700984 * in {@link #setNameOverrides(android.app.BackStackRecord.TransitionState, java.util.ArrayList,
985 * java.util.ArrayList)}.
George Mountc03da0e2014-08-22 17:04:02 -0700986 */
987 private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
988 SparseArray<Fragment> lastInFragments, boolean isBack) {
George Mount128690c2015-12-03 14:43:33 -0800989 ensureFragmentsAreInitialized(lastInFragments);
George Mountc03da0e2014-08-22 17:04:02 -0700990 TransitionState state = new TransitionState();
991
George Mountd4c3c912014-06-09 12:31:34 -0700992 // Adding a non-existent target view makes sure that the transitions don't target
993 // any views by default. They'll only target the views we tell add. If we don't
994 // add any, then no views will be targeted.
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700995 state.nonExistentView = new View(mManager.mHost.getContext());
George Mountd4c3c912014-06-09 12:31:34 -0700996
George Mountc03da0e2014-08-22 17:04:02 -0700997 // Go over all leaving fragments.
998 for (int i = 0; i < firstOutFragments.size(); i++) {
999 int containerId = firstOutFragments.keyAt(i);
1000 configureTransitions(containerId, state, isBack, firstOutFragments,
George Mountecfd0072014-09-07 10:06:25 -07001001 lastInFragments);
George Mountd4c3c912014-06-09 12:31:34 -07001002 }
1003
George Mountc03da0e2014-08-22 17:04:02 -07001004 // Now go over all entering fragments that didn't have a leaving fragment.
1005 for (int i = 0; i < lastInFragments.size(); i++) {
1006 int containerId = lastInFragments.keyAt(i);
1007 if (firstOutFragments.get(containerId) == null) {
1008 configureTransitions(containerId, state, isBack, firstOutFragments,
George Mountecfd0072014-09-07 10:06:25 -07001009 lastInFragments);
George Mountc03da0e2014-08-22 17:04:02 -07001010 }
1011 }
George Mountd4c3c912014-06-09 12:31:34 -07001012 return state;
1013 }
1014
George Mount128690c2015-12-03 14:43:33 -08001015 /**
1016 * Ensure that fragments that are entering are at least at the CREATED state
1017 * so that they may load Transitions using TransitionInflater.
1018 */
1019 private void ensureFragmentsAreInitialized(SparseArray<Fragment> lastInFragments) {
1020 final int count = lastInFragments.size();
1021 for (int i = 0; i < count; i++) {
1022 final Fragment fragment = lastInFragments.valueAt(i);
1023 if (fragment.mState < Fragment.CREATED) {
1024 mManager.makeActive(fragment);
1025 mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
1026 }
1027 }
1028 }
1029
George Mountecfd0072014-09-07 10:06:25 -07001030 private static Transition cloneTransition(Transition transition) {
1031 if (transition != null) {
1032 transition = transition.clone();
1033 }
1034 return transition;
1035 }
1036
George Mountc03da0e2014-08-22 17:04:02 -07001037 private static Transition getEnterTransition(Fragment inFragment, boolean isBack) {
1038 if (inFragment == null) {
1039 return null;
1040 }
George Mountecfd0072014-09-07 10:06:25 -07001041 return cloneTransition(isBack ? inFragment.getReenterTransition() :
1042 inFragment.getEnterTransition());
George Mountc03da0e2014-08-22 17:04:02 -07001043 }
George Mountd4c3c912014-06-09 12:31:34 -07001044
George Mountc03da0e2014-08-22 17:04:02 -07001045 private static Transition getExitTransition(Fragment outFragment, boolean isBack) {
1046 if (outFragment == null) {
1047 return null;
1048 }
George Mountecfd0072014-09-07 10:06:25 -07001049 return cloneTransition(isBack ? outFragment.getReturnTransition() :
1050 outFragment.getExitTransition());
George Mountc03da0e2014-08-22 17:04:02 -07001051 }
1052
George Mountc855f7d2015-06-10 15:00:17 -07001053 private static TransitionSet getSharedElementTransition(Fragment inFragment,
1054 Fragment outFragment, boolean isBack) {
George Mountc03da0e2014-08-22 17:04:02 -07001055 if (inFragment == null || outFragment == null) {
1056 return null;
1057 }
George Mountc855f7d2015-06-10 15:00:17 -07001058 Transition transition = cloneTransition(isBack
1059 ? outFragment.getSharedElementReturnTransition()
1060 : inFragment.getSharedElementEnterTransition());
1061 if (transition == null) {
1062 return null;
1063 }
1064 TransitionSet transitionSet = new TransitionSet();
1065 transitionSet.addTransition(transition);
1066 return transitionSet;
George Mountc03da0e2014-08-22 17:04:02 -07001067 }
1068
George Mountecfd0072014-09-07 10:06:25 -07001069 private static ArrayList<View> captureExitingViews(Transition exitTransition,
George Mount7ce5d752014-10-30 16:09:22 -07001070 Fragment outFragment, ArrayMap<String, View> namedViews, View nonExistentView) {
George Mountecfd0072014-09-07 10:06:25 -07001071 ArrayList<View> viewList = null;
George Mountc03da0e2014-08-22 17:04:02 -07001072 if (exitTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001073 viewList = new ArrayList<View>();
George Mountc03da0e2014-08-22 17:04:02 -07001074 View root = outFragment.getView();
George Mountc03da0e2014-08-22 17:04:02 -07001075 root.captureTransitioningViews(viewList);
George Mount27bc8aa2014-09-14 16:48:21 -07001076 if (namedViews != null) {
1077 viewList.removeAll(namedViews.values());
1078 }
George Mount7ce5d752014-10-30 16:09:22 -07001079 if (!viewList.isEmpty()) {
1080 viewList.add(nonExistentView);
1081 addTargets(exitTransition, viewList);
1082 }
George Mountc03da0e2014-08-22 17:04:02 -07001083 }
George Mountecfd0072014-09-07 10:06:25 -07001084 return viewList;
George Mountc03da0e2014-08-22 17:04:02 -07001085 }
1086
1087 private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
George Mountecfd0072014-09-07 10:06:25 -07001088 boolean isBack) {
1089 ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001090 if (mSharedElementSourceNames != null) {
1091 outFragment.getView().findNamedViews(namedViews);
1092 if (isBack) {
1093 namedViews.retainAll(mSharedElementTargetNames);
1094 } else {
1095 namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
George Mountecfd0072014-09-07 10:06:25 -07001096 namedViews);
George Mountd4c3c912014-06-09 12:31:34 -07001097 }
1098 }
1099
George Mountc03da0e2014-08-22 17:04:02 -07001100 if (isBack) {
George Mount65580562014-08-29 08:15:48 -07001101 outFragment.mEnterTransitionCallback.onMapSharedElements(
George Mountc03da0e2014-08-22 17:04:02 -07001102 mSharedElementTargetNames, namedViews);
1103 setBackNameOverrides(state, namedViews, false);
George Mountd4c3c912014-06-09 12:31:34 -07001104 } else {
George Mount65580562014-08-29 08:15:48 -07001105 outFragment.mExitTransitionCallback.onMapSharedElements(
George Mountc03da0e2014-08-22 17:04:02 -07001106 mSharedElementTargetNames, namedViews);
1107 setNameOverrides(state, namedViews, false);
1108 }
1109
1110 return namedViews;
1111 }
1112
1113 /**
1114 * Prepares the enter transition by adding a non-existent view to the transition's target list
1115 * and setting it epicenter callback. By adding a non-existent view to the target list,
1116 * we can prevent any view from being targeted at the beginning of the transition.
1117 * We will add to the views before the end state of the transition is captured so that the
1118 * views will appear. At the start of the transition, we clear the list of targets so that
1119 * we can restore the state of the transition and use it again.
George Mountecfd0072014-09-07 10:06:25 -07001120 *
1121 * <p>The shared element transition maps its shared elements immediately prior to
1122 * capturing the final state of the Transition.</p>
George Mountc03da0e2014-08-22 17:04:02 -07001123 */
George Mountecfd0072014-09-07 10:06:25 -07001124 private ArrayList<View> addTransitionTargets(final TransitionState state,
George Mountc855f7d2015-06-10 15:00:17 -07001125 final Transition enterTransition, final TransitionSet sharedElementTransition,
George Mount61f83312015-09-09 16:10:29 -07001126 final Transition exitTransition, final Transition overallTransition,
1127 final View container, final Fragment inFragment, final Fragment outFragment,
George Mount2deeaa32014-09-19 10:02:02 -07001128 final ArrayList<View> hiddenFragmentViews, final boolean isBack,
1129 final ArrayList<View> sharedElementTargets) {
George Mountecfd0072014-09-07 10:06:25 -07001130 if (enterTransition == null && sharedElementTransition == null &&
1131 overallTransition == null) {
1132 return null;
1133 }
1134 final ArrayList<View> enteringViews = new ArrayList<View>();
1135 container.getViewTreeObserver().addOnPreDrawListener(
1136 new ViewTreeObserver.OnPreDrawListener() {
1137 @Override
1138 public boolean onPreDraw() {
1139 container.getViewTreeObserver().removeOnPreDrawListener(this);
1140
1141 // Don't include any newly-hidden fragments in the transition.
George Mount13d70322015-07-21 14:30:24 -07001142 if (inFragment != null) {
1143 excludeHiddenFragments(hiddenFragmentViews, inFragment.mContainerId,
1144 overallTransition);
1145 }
George Mountecfd0072014-09-07 10:06:25 -07001146
George Mount27bc8aa2014-09-14 16:48:21 -07001147 ArrayMap<String, View> namedViews = null;
George Mountecfd0072014-09-07 10:06:25 -07001148 if (sharedElementTransition != null) {
George Mount27bc8aa2014-09-14 16:48:21 -07001149 namedViews = mapSharedElementsIn(state, isBack, inFragment);
George Mount2deeaa32014-09-19 10:02:02 -07001150 removeTargets(sharedElementTransition, sharedElementTargets);
George Mount61f83312015-09-09 16:10:29 -07001151 // keep the nonExistentView as excluded so the list doesn't get emptied
1152 sharedElementTargets.remove(state.nonExistentView);
1153 excludeViews(exitTransition, sharedElementTransition,
1154 sharedElementTargets, false);
1155 excludeViews(enterTransition, sharedElementTransition,
1156 sharedElementTargets, false);
1157
George Mountc855f7d2015-06-10 15:00:17 -07001158 setSharedElementTargets(sharedElementTransition,
1159 state.nonExistentView, namedViews, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001160
1161 setEpicenterIn(namedViews, state);
1162
1163 callSharedElementEnd(state, inFragment, outFragment, isBack,
1164 namedViews);
1165 }
1166
1167 if (enterTransition != null) {
George Mountec3364c2015-06-03 15:47:44 -07001168 enterTransition.removeTarget(state.nonExistentView);
George Mountc03da0e2014-08-22 17:04:02 -07001169 View view = inFragment.getView();
1170 if (view != null) {
1171 view.captureTransitioningViews(enteringViews);
George Mount27bc8aa2014-09-14 16:48:21 -07001172 if (namedViews != null) {
1173 enteringViews.removeAll(namedViews.values());
1174 }
George Mount7ce5d752014-10-30 16:09:22 -07001175 enteringViews.add(state.nonExistentView);
1176 // We added this earlier to prevent any views being targeted.
George Mountecfd0072014-09-07 10:06:25 -07001177 addTargets(enterTransition, enteringViews);
George Mountc03da0e2014-08-22 17:04:02 -07001178 }
George Mountecfd0072014-09-07 10:06:25 -07001179 setSharedElementEpicenter(enterTransition, state);
George Mountc03da0e2014-08-22 17:04:02 -07001180 }
George Mount61f83312015-09-09 16:10:29 -07001181
1182 excludeViews(exitTransition, enterTransition, enteringViews, true);
1183 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
1184 true);
1185 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
1186 true);
George Mountecfd0072014-09-07 10:06:25 -07001187 return true;
1188 }
1189 });
1190 return enteringViews;
1191 }
1192
1193 private void callSharedElementEnd(TransitionState state, Fragment inFragment,
1194 Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) {
1195 SharedElementCallback sharedElementCallback = isBack ?
1196 outFragment.mEnterTransitionCallback :
1197 inFragment.mEnterTransitionCallback;
1198 ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
1199 ArrayList<View> views = new ArrayList<View>(namedViews.values());
1200 sharedElementCallback.onSharedElementEnd(names, views, null);
1201 }
1202
1203 private void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) {
1204 if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
1205 // now we know the epicenter of the entering transition.
1206 View epicenter = namedViews
1207 .get(mSharedElementTargetNames.get(0));
1208 if (epicenter != null) {
1209 state.enteringEpicenterView = epicenter;
1210 }
George Mountc03da0e2014-08-22 17:04:02 -07001211 }
1212 }
1213
George Mountecfd0072014-09-07 10:06:25 -07001214 private ArrayMap<String, View> mapSharedElementsIn(TransitionState state,
1215 boolean isBack, Fragment inFragment) {
1216 // Now map the shared elements in the incoming fragment
1217 ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack);
1218
1219 // remap shared elements and set the name mapping used
1220 // in the shared element transition.
1221 if (isBack) {
1222 inFragment.mExitTransitionCallback.onMapSharedElements(
1223 mSharedElementTargetNames, namedViews);
1224 setBackNameOverrides(state, namedViews, true);
1225 } else {
1226 inFragment.mEnterTransitionCallback.onMapSharedElements(
1227 mSharedElementTargetNames, namedViews);
1228 setNameOverrides(state, namedViews, true);
1229 }
1230 return namedViews;
1231 }
1232
George Mountc03da0e2014-08-22 17:04:02 -07001233 private static Transition mergeTransitions(Transition enterTransition,
1234 Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
1235 boolean isBack) {
1236 boolean overlap = true;
George Mountec3364c2015-06-03 15:47:44 -07001237 if (enterTransition != null && exitTransition != null && inFragment != null) {
George Mountc03da0e2014-08-22 17:04:02 -07001238 overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
1239 inFragment.getAllowEnterTransitionOverlap();
1240 }
1241
George Mount27bc8aa2014-09-14 16:48:21 -07001242 // Wrap the transitions. Explicit targets like in enter and exit will cause the
1243 // views to be targeted regardless of excluded views. If that happens, then the
1244 // excluded fragments views (hidden fragments) will still be in the transition.
1245
George Mountc03da0e2014-08-22 17:04:02 -07001246 Transition transition;
1247 if (overlap) {
George Mount27bc8aa2014-09-14 16:48:21 -07001248 // Regular transition -- do it all together
George Mount2deeaa32014-09-19 10:02:02 -07001249 TransitionSet transitionSet = new TransitionSet();
1250 if (enterTransition != null) {
1251 transitionSet.addTransition(enterTransition);
George Mount27bc8aa2014-09-14 16:48:21 -07001252 }
George Mount2deeaa32014-09-19 10:02:02 -07001253 if (exitTransition != null) {
1254 transitionSet.addTransition(exitTransition);
1255 }
1256 if (sharedElementTransition != null) {
1257 transitionSet.addTransition(sharedElementTransition);
1258 }
1259 transition = transitionSet;
George Mountc03da0e2014-08-22 17:04:02 -07001260 } else {
George Mount27bc8aa2014-09-14 16:48:21 -07001261 // First do exit, then enter, but allow shared element transition to happen
1262 // during both.
1263 Transition staggered = null;
1264 if (exitTransition != null && enterTransition != null) {
1265 staggered = new TransitionSet()
1266 .addTransition(exitTransition)
1267 .addTransition(enterTransition)
1268 .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
1269 } else if (exitTransition != null) {
1270 staggered = exitTransition;
1271 } else if (enterTransition != null) {
1272 staggered = enterTransition;
1273 }
1274 if (sharedElementTransition != null) {
1275 TransitionSet together = new TransitionSet();
1276 if (staggered != null) {
1277 together.addTransition(staggered);
1278 }
1279 together.addTransition(sharedElementTransition);
1280 transition = together;
1281 } else {
1282 transition = staggered;
1283 }
George Mountc03da0e2014-08-22 17:04:02 -07001284 }
1285 return transition;
1286 }
1287
1288 /**
1289 * Configures custom transitions for a specific fragment container.
1290 *
1291 * @param containerId The container ID of the fragments to configure the transition for.
George Mountecfd0072014-09-07 10:06:25 -07001292 * @param state The Transition State keeping track of the executing transitions.
George Mountc03da0e2014-08-22 17:04:02 -07001293 * @param firstOutFragments The list of first fragments to be removed, keyed on the
1294 * container ID.
1295 * @param lastInFragments The list of last fragments to be added, keyed on the
1296 * container ID.
1297 * @param isBack true if this is popping the back stack or false if this is a
1298 * forward operation.
George Mountc03da0e2014-08-22 17:04:02 -07001299 */
1300 private void configureTransitions(int containerId, TransitionState state, boolean isBack,
George Mountecfd0072014-09-07 10:06:25 -07001301 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -07001302 ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
George Mountc03da0e2014-08-22 17:04:02 -07001303 if (sceneRoot != null) {
1304 Fragment inFragment = lastInFragments.get(containerId);
1305 Fragment outFragment = firstOutFragments.get(containerId);
1306
1307 Transition enterTransition = getEnterTransition(inFragment, isBack);
George Mountc855f7d2015-06-10 15:00:17 -07001308 TransitionSet sharedElementTransition =
1309 getSharedElementTransition(inFragment, outFragment, isBack);
George Mountc03da0e2014-08-22 17:04:02 -07001310 Transition exitTransition = getExitTransition(outFragment, isBack);
George Mountc03da0e2014-08-22 17:04:02 -07001311
George Mountecfd0072014-09-07 10:06:25 -07001312 if (enterTransition == null && sharedElementTransition == null &&
1313 exitTransition == null) {
1314 return; // no transitions!
1315 }
George Mount27bc8aa2014-09-14 16:48:21 -07001316 if (enterTransition != null) {
1317 enterTransition.addTarget(state.nonExistentView);
George Mountc03da0e2014-08-22 17:04:02 -07001318 }
George Mountecfd0072014-09-07 10:06:25 -07001319 ArrayMap<String, View> namedViews = null;
George Mount2deeaa32014-09-19 10:02:02 -07001320 ArrayList<View> sharedElementTargets = new ArrayList<View>();
George Mountecfd0072014-09-07 10:06:25 -07001321 if (sharedElementTransition != null) {
1322 namedViews = remapSharedElements(state, outFragment, isBack);
George Mountc855f7d2015-06-10 15:00:17 -07001323 setSharedElementTargets(sharedElementTransition,
1324 state.nonExistentView, namedViews, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001325
1326 // Notify the start of the transition.
1327 SharedElementCallback callback = isBack ?
1328 outFragment.mEnterTransitionCallback :
1329 inFragment.mEnterTransitionCallback;
1330 ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
1331 ArrayList<View> views = new ArrayList<View>(namedViews.values());
1332 callback.onSharedElementStart(names, views, null);
1333 }
George Mountc03da0e2014-08-22 17:04:02 -07001334
George Mount27bc8aa2014-09-14 16:48:21 -07001335 ArrayList<View> exitingViews = captureExitingViews(exitTransition, outFragment,
George Mount7ce5d752014-10-30 16:09:22 -07001336 namedViews, state.nonExistentView);
George Mount27bc8aa2014-09-14 16:48:21 -07001337 if (exitingViews == null || exitingViews.isEmpty()) {
1338 exitTransition = null;
1339 }
George Mount61f83312015-09-09 16:10:29 -07001340 excludeViews(enterTransition, exitTransition, exitingViews, true);
1341 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
1342 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
George Mount27bc8aa2014-09-14 16:48:21 -07001343
George Mountc03da0e2014-08-22 17:04:02 -07001344 // Set the epicenter of the exit transition
George Mount27bc8aa2014-09-14 16:48:21 -07001345 if (mSharedElementTargetNames != null && namedViews != null) {
George Mountc03da0e2014-08-22 17:04:02 -07001346 View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
1347 if (epicenterView != null) {
George Mount27bc8aa2014-09-14 16:48:21 -07001348 if (exitTransition != null) {
1349 setEpicenter(exitTransition, epicenterView);
1350 }
1351 if (sharedElementTransition != null) {
1352 setEpicenter(sharedElementTransition, epicenterView);
1353 }
George Mountc03da0e2014-08-22 17:04:02 -07001354 }
1355 }
1356
George Mountc03da0e2014-08-22 17:04:02 -07001357 Transition transition = mergeTransitions(enterTransition, exitTransition,
1358 sharedElementTransition, inFragment, isBack);
1359
1360 if (transition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001361 ArrayList<View> hiddenFragments = new ArrayList<View>();
1362 ArrayList<View> enteringViews = addTransitionTargets(state, enterTransition,
George Mount61f83312015-09-09 16:10:29 -07001363 sharedElementTransition, exitTransition, transition, sceneRoot, inFragment,
George Mountc855f7d2015-06-10 15:00:17 -07001364 outFragment, hiddenFragments, isBack, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001365
George Mountc03da0e2014-08-22 17:04:02 -07001366 transition.setNameOverrides(state.nameOverrides);
1367 // We want to exclude hidden views later, so we need a non-null list in the
1368 // transition now.
1369 transition.excludeTarget(state.nonExistentView, true);
1370 // Now exclude all currently hidden fragments.
George Mountecfd0072014-09-07 10:06:25 -07001371 excludeHiddenFragments(hiddenFragments, containerId, transition);
George Mountc03da0e2014-08-22 17:04:02 -07001372 TransitionManager.beginDelayedTransition(sceneRoot, transition);
George Mountecfd0072014-09-07 10:06:25 -07001373 // Remove the view targeting after the transition starts
1374 removeTargetedViewsFromTransitions(sceneRoot, state.nonExistentView,
1375 enterTransition, enteringViews, exitTransition, exitingViews,
George Mountc855f7d2015-06-10 15:00:17 -07001376 sharedElementTransition, sharedElementTargets, transition,
1377 hiddenFragments);
George Mountd4c3c912014-06-09 12:31:34 -07001378 }
1379 }
1380 }
1381
George Mountc03da0e2014-08-22 17:04:02 -07001382 /**
George Mountc855f7d2015-06-10 15:00:17 -07001383 * Finds all children of the shared elements and sets the wrapping TransitionSet
1384 * targets to point to those. It also limits transitions that have no targets to the
1385 * specific shared elements. This allows developers to target child views of the
1386 * shared elements specifically, but this doesn't happen by default.
1387 */
1388 private static void setSharedElementTargets(TransitionSet transition,
1389 View nonExistentView, ArrayMap<String, View> namedViews,
1390 ArrayList<View> sharedElementTargets) {
1391 sharedElementTargets.clear();
1392 sharedElementTargets.addAll(namedViews.values());
1393
1394 final List<View> views = transition.getTargets();
1395 views.clear();
1396 final int count = sharedElementTargets.size();
1397 for (int i = 0; i < count; i++) {
1398 final View view = sharedElementTargets.get(i);
1399 bfsAddViewChildren(views, view);
1400 }
1401 sharedElementTargets.add(nonExistentView);
1402 addTargets(transition, sharedElementTargets);
1403 }
1404
1405 /**
1406 * Uses a breadth-first scheme to add startView and all of its children to views.
1407 * It won't add a child if it is already in views.
1408 */
1409 private static void bfsAddViewChildren(final List<View> views, final View startView) {
1410 final int startIndex = views.size();
1411 if (containedBeforeIndex(views, startView, startIndex)) {
1412 return; // This child is already in the list, so all its children are also.
1413 }
1414 views.add(startView);
1415 for (int index = startIndex; index < views.size(); index++) {
1416 final View view = views.get(index);
1417 if (view instanceof ViewGroup) {
1418 ViewGroup viewGroup = (ViewGroup) view;
1419 final int childCount = viewGroup.getChildCount();
1420 for (int childIndex = 0; childIndex < childCount; childIndex++) {
1421 final View child = viewGroup.getChildAt(childIndex);
1422 if (!containedBeforeIndex(views, child, startIndex)) {
1423 views.add(child);
1424 }
1425 }
1426 }
1427 }
1428 }
1429
1430 /**
1431 * Does a linear search through views for view, limited to maxIndex.
1432 */
1433 private static boolean containedBeforeIndex(final List<View> views, final View view,
1434 final int maxIndex) {
1435 for (int i = 0; i < maxIndex; i++) {
1436 if (views.get(i) == view) {
1437 return true;
1438 }
1439 }
1440 return false;
1441 }
1442
George Mount61f83312015-09-09 16:10:29 -07001443 private static void excludeViews(Transition transition, Transition fromTransition,
1444 ArrayList<View> views, boolean exclude) {
1445 if (transition != null) {
1446 final int viewCount = fromTransition == null ? 0 : views.size();
1447 for (int i = 0; i < viewCount; i++) {
1448 transition.excludeTarget(views.get(i), exclude);
1449 }
1450 }
1451 }
1452
George Mountc855f7d2015-06-10 15:00:17 -07001453 /**
George Mountecfd0072014-09-07 10:06:25 -07001454 * After the transition has started, remove all targets that we added to the transitions
1455 * so that the transitions are left in a clean state.
1456 */
1457 private void removeTargetedViewsFromTransitions(
1458 final ViewGroup sceneRoot, final View nonExistingView,
1459 final Transition enterTransition, final ArrayList<View> enteringViews,
1460 final Transition exitTransition, final ArrayList<View> exitingViews,
George Mount2deeaa32014-09-19 10:02:02 -07001461 final Transition sharedElementTransition, final ArrayList<View> sharedElementTargets,
George Mountecfd0072014-09-07 10:06:25 -07001462 final Transition overallTransition, final ArrayList<View> hiddenViews) {
George Mount2deeaa32014-09-19 10:02:02 -07001463 if (overallTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001464 sceneRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
1465 @Override
1466 public boolean onPreDraw() {
1467 sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
1468 if (enterTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001469 removeTargets(enterTransition, enteringViews);
George Mount61f83312015-09-09 16:10:29 -07001470 excludeViews(enterTransition, exitTransition, exitingViews, false);
1471 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
1472 false);
George Mountecfd0072014-09-07 10:06:25 -07001473 }
1474 if (exitTransition != null) {
1475 removeTargets(exitTransition, exitingViews);
George Mount61f83312015-09-09 16:10:29 -07001476 excludeViews(exitTransition, enterTransition, enteringViews, false);
1477 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
1478 false);
George Mountecfd0072014-09-07 10:06:25 -07001479 }
George Mount2deeaa32014-09-19 10:02:02 -07001480 if (sharedElementTransition != null) {
1481 removeTargets(sharedElementTransition, sharedElementTargets);
1482 }
George Mountecfd0072014-09-07 10:06:25 -07001483 int numViews = hiddenViews.size();
1484 for (int i = 0; i < numViews; i++) {
1485 overallTransition.excludeTarget(hiddenViews.get(i), false);
1486 }
1487 overallTransition.excludeTarget(nonExistingView, false);
1488 return true;
1489 }
1490 });
1491 }
1492 }
1493
George Mount7ce5d752014-10-30 16:09:22 -07001494 /**
1495 * This method removes the views from transitions that target ONLY those views.
1496 * The views list should match those added in addTargets and should contain
1497 * one view that is not in the view hierarchy (state.nonExistentView).
1498 */
1499 public static void removeTargets(Transition transition, ArrayList<View> views) {
1500 if (transition instanceof TransitionSet) {
1501 TransitionSet set = (TransitionSet) transition;
1502 int numTransitions = set.getTransitionCount();
1503 for (int i = 0; i < numTransitions; i++) {
1504 Transition child = set.getTransitionAt(i);
1505 removeTargets(child, views);
1506 }
1507 } else if (!hasSimpleTarget(transition)) {
1508 List<View> targets = transition.getTargets();
1509 if (targets != null && targets.size() == views.size() &&
1510 targets.containsAll(views)) {
1511 // We have an exact match. We must have added these earlier in addTargets
1512 for (int i = views.size() - 1; i >= 0; i--) {
1513 transition.removeTarget(views.get(i));
1514 }
1515 }
George Mountecfd0072014-09-07 10:06:25 -07001516 }
1517 }
1518
George Mount7ce5d752014-10-30 16:09:22 -07001519 /**
1520 * This method adds views as targets to the transition, but only if the transition
1521 * doesn't already have a target. It is best for views to contain one View object
1522 * that does not exist in the view hierarchy (state.nonExistentView) so that
1523 * when they are removed later, a list match will suffice to remove the targets.
1524 * Otherwise, if you happened to have targeted the exact views for the transition,
1525 * the removeTargets call will remove them unexpectedly.
1526 */
1527 public static void addTargets(Transition transition, ArrayList<View> views) {
1528 if (transition instanceof TransitionSet) {
1529 TransitionSet set = (TransitionSet) transition;
1530 int numTransitions = set.getTransitionCount();
1531 for (int i = 0; i < numTransitions; i++) {
1532 Transition child = set.getTransitionAt(i);
1533 addTargets(child, views);
1534 }
1535 } else if (!hasSimpleTarget(transition)) {
1536 List<View> targets = transition.getTargets();
1537 if (isNullOrEmpty(targets)) {
1538 // We can just add the target views
1539 int numViews = views.size();
1540 for (int i = 0; i < numViews; i++) {
1541 transition.addTarget(views.get(i));
1542 }
1543 }
George Mountecfd0072014-09-07 10:06:25 -07001544 }
1545 }
1546
George Mount7ce5d752014-10-30 16:09:22 -07001547 private static boolean hasSimpleTarget(Transition transition) {
1548 return !isNullOrEmpty(transition.getTargetIds()) ||
1549 !isNullOrEmpty(transition.getTargetNames()) ||
1550 !isNullOrEmpty(transition.getTargetTypes());
1551 }
1552
1553 private static boolean isNullOrEmpty(List list) {
1554 return list == null || list.isEmpty();
1555 }
1556
George Mountecfd0072014-09-07 10:06:25 -07001557 /**
George Mountc03da0e2014-08-22 17:04:02 -07001558 * Remaps a name-to-View map, substituting different names for keys.
1559 *
1560 * @param inMap A list of keys found in the map, in the order in toGoInMap
1561 * @param toGoInMap A list of keys to use for the new map, in the order of inMap
1562 * @param namedViews The current mapping
George Mountecfd0072014-09-07 10:06:25 -07001563 * @return a new Map after it has been mapped with the new names as keys.
George Mountc03da0e2014-08-22 17:04:02 -07001564 */
1565 private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
George Mountecfd0072014-09-07 10:06:25 -07001566 ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) {
1567 ArrayMap<String, View> remappedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001568 if (!namedViews.isEmpty()) {
1569 int numKeys = inMap.size();
1570 for (int i = 0; i < numKeys; i++) {
1571 View view = namedViews.get(inMap.get(i));
George Mountecfd0072014-09-07 10:06:25 -07001572
George Mountc03da0e2014-08-22 17:04:02 -07001573 if (view != null) {
George Mountecfd0072014-09-07 10:06:25 -07001574 remappedViews.put(toGoInMap.get(i), view);
George Mountd4c3c912014-06-09 12:31:34 -07001575 }
1576 }
1577 }
George Mountecfd0072014-09-07 10:06:25 -07001578 return remappedViews;
George Mountc03da0e2014-08-22 17:04:02 -07001579 }
1580
1581 /**
George Mountecfd0072014-09-07 10:06:25 -07001582 * Maps shared elements to views in the entering fragment.
George Mountc03da0e2014-08-22 17:04:02 -07001583 *
1584 * @param state The transition State as returned from {@link #beginTransition(
1585 * android.util.SparseArray, android.util.SparseArray, boolean)}.
George Mountecfd0072014-09-07 10:06:25 -07001586 * @param inFragment The last fragment to be added.
George Mountc03da0e2014-08-22 17:04:02 -07001587 * @param isBack true if this is popping the back stack or false if this is a
1588 * forward operation.
1589 */
George Mountecfd0072014-09-07 10:06:25 -07001590 private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state,
1591 Fragment inFragment, boolean isBack) {
1592 ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001593 View root = inFragment.getView();
1594 if (root != null) {
1595 if (mSharedElementSourceNames != null) {
1596 root.findNamedViews(namedViews);
1597 if (isBack) {
1598 namedViews = remapNames(mSharedElementSourceNames,
George Mountecfd0072014-09-07 10:06:25 -07001599 mSharedElementTargetNames, namedViews);
George Mountc03da0e2014-08-22 17:04:02 -07001600 } else {
1601 namedViews.retainAll(mSharedElementTargetNames);
1602 }
1603 }
1604 }
1605 return namedViews;
1606 }
1607
George Mountecfd0072014-09-07 10:06:25 -07001608 private void excludeHiddenFragments(final ArrayList<View> hiddenFragmentViews, int containerId,
George Mountc03da0e2014-08-22 17:04:02 -07001609 Transition transition) {
1610 if (mManager.mAdded != null) {
1611 for (int i = 0; i < mManager.mAdded.size(); i++) {
1612 Fragment fragment = mManager.mAdded.get(i);
1613 if (fragment.mView != null && fragment.mContainer != null &&
1614 fragment.mContainerId == containerId) {
1615 if (fragment.mHidden) {
George Mountecfd0072014-09-07 10:06:25 -07001616 if (!hiddenFragmentViews.contains(fragment.mView)) {
George Mountc03da0e2014-08-22 17:04:02 -07001617 transition.excludeTarget(fragment.mView, true);
George Mountecfd0072014-09-07 10:06:25 -07001618 hiddenFragmentViews.add(fragment.mView);
George Mountc03da0e2014-08-22 17:04:02 -07001619 }
1620 } else {
1621 transition.excludeTarget(fragment.mView, false);
George Mountecfd0072014-09-07 10:06:25 -07001622 hiddenFragmentViews.remove(fragment.mView);
George Mountc03da0e2014-08-22 17:04:02 -07001623 }
1624 }
1625 }
George Mountd4c3c912014-06-09 12:31:34 -07001626 }
1627 }
1628
George Mountd4c3c912014-06-09 12:31:34 -07001629 private static void setEpicenter(Transition transition, View view) {
1630 final Rect epicenter = new Rect();
1631 view.getBoundsOnScreen(epicenter);
1632
1633 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1634 @Override
1635 public Rect onGetEpicenter(Transition transition) {
1636 return epicenter;
1637 }
1638 });
1639 }
1640
1641 private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
1642 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1643 private Rect mEpicenter;
1644
1645 @Override
1646 public Rect onGetEpicenter(Transition transition) {
George Mountc03da0e2014-08-22 17:04:02 -07001647 if (mEpicenter == null && state.enteringEpicenterView != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001648 mEpicenter = new Rect();
George Mountc03da0e2014-08-22 17:04:02 -07001649 state.enteringEpicenterView.getBoundsOnScreen(mEpicenter);
George Mountd4c3c912014-06-09 12:31:34 -07001650 }
1651 return mEpicenter;
1652 }
1653 });
1654 }
1655
George Mountc03da0e2014-08-22 17:04:02 -07001656 public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
1657 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001658 if (FragmentManagerImpl.DEBUG) {
1659 Log.v(TAG, "popFromBackStack: " + this);
1660 LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001661 PrintWriter pw = new FastPrintWriter(logw, false, 1024);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001662 dump(" ", null, pw, null);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001663 pw.flush();
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001664 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001665
George Mountd4c3c912014-06-09 12:31:34 -07001666 if (state == null) {
George Mountc03da0e2014-08-22 17:04:02 -07001667 if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
1668 state = beginTransition(firstOutFragments, lastInFragments, true);
1669 }
1670 } else if (!doStateMove) {
George Mountd4c3c912014-06-09 12:31:34 -07001671 setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
1672 }
1673
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001674 bumpBackStackNesting(-1);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001675
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001676 Op op = mTail;
1677 while (op != null) {
1678 switch (op.cmd) {
1679 case OP_ADD: {
1680 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001681 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001682 mManager.removeFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001683 FragmentManagerImpl.reverseTransit(mTransition),
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001684 mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001685 }
1686 break;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001687 case OP_REPLACE: {
1688 Fragment f = op.fragment;
Dianne Hackbornee76efb2012-06-05 10:27:40 -07001689 if (f != null) {
1690 f.mNextAnim = op.popExitAnim;
1691 mManager.removeFragment(f,
1692 FragmentManagerImpl.reverseTransit(mTransition),
1693 mTransitionStyle);
1694 }
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001695 if (op.removed != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001696 for (int i = 0; i < op.removed.size(); i++) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001697 Fragment old = op.removed.get(i);
Chet Haasebc377842011-03-22 11:35:22 -07001698 old.mNextAnim = op.popEnterAnim;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001699 mManager.addFragment(old, false);
1700 }
1701 }
George Mountd4c3c912014-06-09 12:31:34 -07001702 }
1703 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001704 case OP_REMOVE: {
1705 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001706 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001707 mManager.addFragment(f, false);
George Mountd4c3c912014-06-09 12:31:34 -07001708 }
1709 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001710 case OP_HIDE: {
1711 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001712 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001713 mManager.showFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001714 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001715 }
1716 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001717 case OP_SHOW: {
1718 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001719 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001720 mManager.hideFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001721 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001722 }
1723 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001724 case OP_DETACH: {
1725 Fragment f = op.fragment;
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001726 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001727 mManager.attachFragment(f,
1728 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001729 }
1730 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001731 case OP_ATTACH: {
1732 Fragment f = op.fragment;
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001733 f.mNextAnim = op.popExitAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001734 mManager.detachFragment(f,
1735 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001736 }
1737 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001738 default: {
1739 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
1740 }
1741 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001742
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001743 op = op.prev;
1744 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001745
Dianne Hackborn5f36c962010-08-26 15:54:17 -07001746 if (doStateMove) {
1747 mManager.moveToState(mManager.mCurState,
1748 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
George Mountecfd0072014-09-07 10:06:25 -07001749 state = null;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001750 }
Dianne Hackborndd913a52010-07-22 12:17:04 -07001751
1752 if (mIndex >= 0) {
1753 mManager.freeBackStackIndex(mIndex);
1754 mIndex = -1;
1755 }
George Mountd4c3c912014-06-09 12:31:34 -07001756 return state;
1757 }
1758
George Mountc03da0e2014-08-22 17:04:02 -07001759 private static void setNameOverride(ArrayMap<String, String> overrides,
1760 String source, String target) {
1761 if (source != null && target != null && !source.equals(target)) {
1762 for (int index = 0; index < overrides.size(); index++) {
1763 if (source.equals(overrides.valueAt(index))) {
1764 overrides.setValueAt(index, target);
1765 return;
1766 }
George Mountd4c3c912014-06-09 12:31:34 -07001767 }
George Mountc03da0e2014-08-22 17:04:02 -07001768 overrides.put(source, target);
George Mountd4c3c912014-06-09 12:31:34 -07001769 }
George Mountd4c3c912014-06-09 12:31:34 -07001770 }
1771
1772 private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
1773 ArrayList<String> targetNames) {
George Mount13d70322015-07-21 14:30:24 -07001774 if (sourceNames != null && targetNames != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001775 for (int i = 0; i < sourceNames.size(); i++) {
1776 String source = sourceNames.get(i);
1777 String target = targetNames.get(i);
George Mountc03da0e2014-08-22 17:04:02 -07001778 setNameOverride(state.nameOverrides, source, target);
1779 }
1780 }
1781 }
1782
1783 private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1784 boolean isEnd) {
George Mount13d70322015-07-21 14:30:24 -07001785 int targetCount = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
1786 int sourceCount = mSharedElementSourceNames == null ? 0 : mSharedElementSourceNames.size();
1787 final int count = Math.min(targetCount, sourceCount);
George Mountc03da0e2014-08-22 17:04:02 -07001788 for (int i = 0; i < count; i++) {
1789 String source = mSharedElementSourceNames.get(i);
1790 String originalTarget = mSharedElementTargetNames.get(i);
George Mountecfd0072014-09-07 10:06:25 -07001791 View view = namedViews.get(originalTarget);
1792 if (view != null) {
1793 String target = view.getTransitionName();
1794 if (isEnd) {
1795 setNameOverride(state.nameOverrides, source, target);
1796 } else {
1797 setNameOverride(state.nameOverrides, target, source);
1798 }
George Mountc03da0e2014-08-22 17:04:02 -07001799 }
1800 }
1801 }
1802
1803 private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1804 boolean isEnd) {
George Mountec3364c2015-06-03 15:47:44 -07001805 int count = namedViews == null ? 0 : namedViews.size();
George Mountc03da0e2014-08-22 17:04:02 -07001806 for (int i = 0; i < count; i++) {
1807 String source = namedViews.keyAt(i);
1808 String target = namedViews.valueAt(i).getTransitionName();
1809 if (isEnd) {
1810 setNameOverride(state.nameOverrides, source, target);
1811 } else {
1812 setNameOverride(state.nameOverrides, target, source);
George Mountd4c3c912014-06-09 12:31:34 -07001813 }
1814 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001815 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001816
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001817 public String getName() {
1818 return mName;
1819 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001820
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001821 public int getTransition() {
1822 return mTransition;
1823 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001824
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001825 public int getTransitionStyle() {
1826 return mTransitionStyle;
1827 }
Adam Powell2b6230e2010-09-07 17:55:25 -07001828
1829 public boolean isEmpty() {
1830 return mNumOp == 0;
1831 }
George Mountd4c3c912014-06-09 12:31:34 -07001832
1833 public class TransitionState {
George Mountc03da0e2014-08-22 17:04:02 -07001834 public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
George Mountc03da0e2014-08-22 17:04:02 -07001835 public View enteringEpicenterView;
1836 public View nonExistentView;
George Mountd4c3c912014-06-09 12:31:34 -07001837 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001838}