blob: 59f010c17c11e9a22cb3c94b16b24f942f9d652b [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;
George Mountc03da0e2014-08-22 17:04:02 -070028import android.transition.TransitionUtils;
George Mountd4c3c912014-06-09 12:31:34 -070029import android.util.ArrayMap;
Dianne Hackborn445646c2010-06-25 15:52:59 -070030import android.util.Log;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -070031import android.util.LogWriter;
George Mountd4c3c912014-06-09 12:31:34 -070032import android.util.Pair;
George Mountc03da0e2014-08-22 17:04:02 -070033import android.util.SparseArray;
George Mountd4c3c912014-06-09 12:31:34 -070034import android.view.View;
35import android.view.ViewGroup;
George Mountc03da0e2014-08-22 17:04:02 -070036import android.view.ViewTreeObserver;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070037
Dianne Hackborn30d71892010-12-11 10:37:55 -080038import java.io.FileDescriptor;
39import java.io.PrintWriter;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070040import java.util.ArrayList;
George Mountd4c3c912014-06-09 12:31:34 -070041import java.util.Collection;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070042
43final class BackStackState implements Parcelable {
44 final int[] mOps;
45 final int mTransition;
46 final int mTransitionStyle;
47 final String mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -070048 final int mIndex;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070049 final int mBreadCrumbTitleRes;
50 final CharSequence mBreadCrumbTitleText;
51 final int mBreadCrumbShortTitleRes;
52 final CharSequence mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -070053 final ArrayList<String> mSharedElementSourceNames;
54 final ArrayList<String> mSharedElementTargetNames;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070055
56 public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
Dianne Hackborn445646c2010-06-25 15:52:59 -070057 int numRemoved = 0;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070058 BackStackRecord.Op op = bse.mHead;
Dianne Hackborn445646c2010-06-25 15:52:59 -070059 while (op != null) {
George Mountd4c3c912014-06-09 12:31:34 -070060 if (op.removed != null) {
61 numRemoved += op.removed.size();
62 }
Dianne Hackborn445646c2010-06-25 15:52:59 -070063 op = op.next;
64 }
George Mountd4c3c912014-06-09 12:31:34 -070065 mOps = new int[bse.mNumOp * 7 + numRemoved];
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070066
Dianne Hackbornb7a2e472010-08-12 16:20:42 -070067 if (!bse.mAddToBackStack) {
68 throw new IllegalStateException("Not on back stack");
69 }
70
Dianne Hackborn445646c2010-06-25 15:52:59 -070071 op = bse.mHead;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070072 int pos = 0;
73 while (op != null) {
74 mOps[pos++] = op.cmd;
Dianne Hackbornee76efb2012-06-05 10:27:40 -070075 mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070076 mOps[pos++] = op.enterAnim;
77 mOps[pos++] = op.exitAnim;
Chet Haasebc377842011-03-22 11:35:22 -070078 mOps[pos++] = op.popEnterAnim;
79 mOps[pos++] = op.popExitAnim;
Dianne Hackborn445646c2010-06-25 15:52:59 -070080 if (op.removed != null) {
81 final int N = op.removed.size();
82 mOps[pos++] = N;
George Mountd4c3c912014-06-09 12:31:34 -070083 for (int i = 0; i < N; i++) {
Dianne Hackborn445646c2010-06-25 15:52:59 -070084 mOps[pos++] = op.removed.get(i).mIndex;
85 }
86 } else {
87 mOps[pos++] = 0;
88 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070089 op = op.next;
90 }
91 mTransition = bse.mTransition;
92 mTransitionStyle = bse.mTransitionStyle;
93 mName = bse.mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -070094 mIndex = bse.mIndex;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070095 mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
96 mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
97 mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
98 mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -070099 mSharedElementSourceNames = bse.mSharedElementSourceNames;
100 mSharedElementTargetNames = bse.mSharedElementTargetNames;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700101 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700102
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700103 public BackStackState(Parcel in) {
104 mOps = in.createIntArray();
105 mTransition = in.readInt();
106 mTransitionStyle = in.readInt();
107 mName = in.readString();
Dianne Hackborndd913a52010-07-22 12:17:04 -0700108 mIndex = in.readInt();
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700109 mBreadCrumbTitleRes = in.readInt();
110 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
111 mBreadCrumbShortTitleRes = in.readInt();
112 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
George Mountd4c3c912014-06-09 12:31:34 -0700113 mSharedElementSourceNames = in.createStringArrayList();
114 mSharedElementTargetNames = in.createStringArrayList();
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700115 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700116
117 public BackStackRecord instantiate(FragmentManagerImpl fm) {
118 BackStackRecord bse = new BackStackRecord(fm);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700119 int pos = 0;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700120 int num = 0;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700121 while (pos < mOps.length) {
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700122 BackStackRecord.Op op = new BackStackRecord.Op();
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700123 op.cmd = mOps[pos++];
George Mountd4c3c912014-06-09 12:31:34 -0700124 if (FragmentManagerImpl.DEBUG) {
125 Log.v(FragmentManagerImpl.TAG,
126 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
127 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700128 int findex = mOps[pos++];
129 if (findex >= 0) {
130 Fragment f = fm.mActive.get(findex);
131 op.fragment = f;
132 } else {
133 op.fragment = null;
134 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700135 op.enterAnim = mOps[pos++];
136 op.exitAnim = mOps[pos++];
Chet Haasebc377842011-03-22 11:35:22 -0700137 op.popEnterAnim = mOps[pos++];
138 op.popExitAnim = mOps[pos++];
Dianne Hackborn445646c2010-06-25 15:52:59 -0700139 final int N = mOps[pos++];
140 if (N > 0) {
141 op.removed = new ArrayList<Fragment>(N);
George Mountd4c3c912014-06-09 12:31:34 -0700142 for (int i = 0; i < N; i++) {
143 if (FragmentManagerImpl.DEBUG) {
144 Log.v(FragmentManagerImpl.TAG,
145 "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
146 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700147 Fragment r = fm.mActive.get(mOps[pos++]);
148 op.removed.add(r);
Dianne Hackborn445646c2010-06-25 15:52:59 -0700149 }
150 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700151 bse.addOp(op);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700152 num++;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700153 }
154 bse.mTransition = mTransition;
155 bse.mTransitionStyle = mTransitionStyle;
156 bse.mName = mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -0700157 bse.mIndex = mIndex;
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700158 bse.mAddToBackStack = true;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700159 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
160 bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
161 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
162 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -0700163 bse.mSharedElementSourceNames = mSharedElementSourceNames;
164 bse.mSharedElementTargetNames = mSharedElementTargetNames;
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700165 bse.bumpBackStackNesting(1);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700166 return bse;
167 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700168
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700169 public int describeContents() {
170 return 0;
171 }
172
173 public void writeToParcel(Parcel dest, int flags) {
174 dest.writeIntArray(mOps);
175 dest.writeInt(mTransition);
176 dest.writeInt(mTransitionStyle);
177 dest.writeString(mName);
Dianne Hackborndd913a52010-07-22 12:17:04 -0700178 dest.writeInt(mIndex);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700179 dest.writeInt(mBreadCrumbTitleRes);
180 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
181 dest.writeInt(mBreadCrumbShortTitleRes);
182 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
George Mountd4c3c912014-06-09 12:31:34 -0700183 dest.writeStringList(mSharedElementSourceNames);
184 dest.writeStringList(mSharedElementTargetNames);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700185 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700186
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700187 public static final Parcelable.Creator<BackStackState> CREATOR
188 = new Parcelable.Creator<BackStackState>() {
189 public BackStackState createFromParcel(Parcel in) {
190 return new BackStackState(in);
191 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700192
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700193 public BackStackState[] newArray(int size) {
194 return new BackStackState[size];
195 }
196 };
197}
198
199/**
200 * @hide Entry of an operation on the fragment back stack.
201 */
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700202final class BackStackRecord extends FragmentTransaction implements
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700203 FragmentManager.BackStackEntry, Runnable {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700204 static final String TAG = FragmentManagerImpl.TAG;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700205
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700206 final FragmentManagerImpl mManager;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700207
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700208 static final int OP_NULL = 0;
209 static final int OP_ADD = 1;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700210 static final int OP_REPLACE = 2;
211 static final int OP_REMOVE = 3;
212 static final int OP_HIDE = 4;
213 static final int OP_SHOW = 5;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700214 static final int OP_DETACH = 6;
215 static final int OP_ATTACH = 7;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700216
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700217 static final class Op {
218 Op next;
219 Op prev;
220 int cmd;
221 Fragment fragment;
222 int enterAnim;
223 int exitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700224 int popEnterAnim;
225 int popExitAnim;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700226 ArrayList<Fragment> removed;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700227 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700228
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700229 Op mHead;
230 Op mTail;
231 int mNumOp;
232 int mEnterAnim;
233 int mExitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700234 int mPopEnterAnim;
235 int mPopExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700236 int mTransition;
237 int mTransitionStyle;
238 boolean mAddToBackStack;
Adam Powell0c24a552010-11-03 16:44:11 -0700239 boolean mAllowAddToBackStack = true;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700240 String mName;
241 boolean mCommitted;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700242 int mIndex = -1;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700243
244 int mBreadCrumbTitleRes;
245 CharSequence mBreadCrumbTitleText;
246 int mBreadCrumbShortTitleRes;
247 CharSequence mBreadCrumbShortTitleText;
248
George Mountd4c3c912014-06-09 12:31:34 -0700249 ArrayList<String> mSharedElementSourceNames;
250 ArrayList<String> mSharedElementTargetNames;
251
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700252 @Override
253 public String toString() {
254 StringBuilder sb = new StringBuilder(128);
255 sb.append("BackStackEntry{");
256 sb.append(Integer.toHexString(System.identityHashCode(this)));
257 if (mIndex >= 0) {
258 sb.append(" #");
259 sb.append(mIndex);
260 }
261 if (mName != null) {
262 sb.append(" ");
263 sb.append(mName);
264 }
265 sb.append("}");
266 return sb.toString();
267 }
268
Dianne Hackborn30d71892010-12-11 10:37:55 -0800269 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700270 dump(prefix, writer, true);
271 }
272
273 void dump(String prefix, PrintWriter writer, boolean full) {
274 if (full) {
George Mountd4c3c912014-06-09 12:31:34 -0700275 writer.print(prefix);
276 writer.print("mName=");
277 writer.print(mName);
278 writer.print(" mIndex=");
279 writer.print(mIndex);
280 writer.print(" mCommitted=");
281 writer.println(mCommitted);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700282 if (mTransition != FragmentTransaction.TRANSIT_NONE) {
George Mountd4c3c912014-06-09 12:31:34 -0700283 writer.print(prefix);
284 writer.print("mTransition=#");
285 writer.print(Integer.toHexString(mTransition));
286 writer.print(" mTransitionStyle=#");
287 writer.println(Integer.toHexString(mTransitionStyle));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700288 }
George Mountd4c3c912014-06-09 12:31:34 -0700289 if (mEnterAnim != 0 || mExitAnim != 0) {
290 writer.print(prefix);
291 writer.print("mEnterAnim=#");
292 writer.print(Integer.toHexString(mEnterAnim));
293 writer.print(" mExitAnim=#");
294 writer.println(Integer.toHexString(mExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700295 }
George Mountd4c3c912014-06-09 12:31:34 -0700296 if (mPopEnterAnim != 0 || mPopExitAnim != 0) {
297 writer.print(prefix);
298 writer.print("mPopEnterAnim=#");
299 writer.print(Integer.toHexString(mPopEnterAnim));
300 writer.print(" mPopExitAnim=#");
301 writer.println(Integer.toHexString(mPopExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700302 }
303 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700304 writer.print(prefix);
305 writer.print("mBreadCrumbTitleRes=#");
306 writer.print(Integer.toHexString(mBreadCrumbTitleRes));
307 writer.print(" mBreadCrumbTitleText=");
308 writer.println(mBreadCrumbTitleText);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700309 }
310 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700311 writer.print(prefix);
312 writer.print("mBreadCrumbShortTitleRes=#");
313 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
314 writer.print(" mBreadCrumbShortTitleText=");
315 writer.println(mBreadCrumbShortTitleText);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700316 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800317 }
318
319 if (mHead != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700320 writer.print(prefix);
321 writer.println("Operations:");
Dianne Hackborn30d71892010-12-11 10:37:55 -0800322 String innerPrefix = prefix + " ";
323 Op op = mHead;
324 int num = 0;
325 while (op != null) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700326 String cmdStr;
327 switch (op.cmd) {
George Mountd4c3c912014-06-09 12:31:34 -0700328 case OP_NULL:
329 cmdStr = "NULL";
330 break;
331 case OP_ADD:
332 cmdStr = "ADD";
333 break;
334 case OP_REPLACE:
335 cmdStr = "REPLACE";
336 break;
337 case OP_REMOVE:
338 cmdStr = "REMOVE";
339 break;
340 case OP_HIDE:
341 cmdStr = "HIDE";
342 break;
343 case OP_SHOW:
344 cmdStr = "SHOW";
345 break;
346 case OP_DETACH:
347 cmdStr = "DETACH";
348 break;
349 case OP_ATTACH:
350 cmdStr = "ATTACH";
351 break;
352 default:
353 cmdStr = "cmd=" + op.cmd;
354 break;
Dianne Hackborn30d71892010-12-11 10:37:55 -0800355 }
George Mountd4c3c912014-06-09 12:31:34 -0700356 writer.print(prefix);
357 writer.print(" Op #");
358 writer.print(num);
359 writer.print(": ");
360 writer.print(cmdStr);
361 writer.print(" ");
362 writer.println(op.fragment);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700363 if (full) {
364 if (op.enterAnim != 0 || op.exitAnim != 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700365 writer.print(innerPrefix);
366 writer.print("enterAnim=#");
367 writer.print(Integer.toHexString(op.enterAnim));
368 writer.print(" exitAnim=#");
369 writer.println(Integer.toHexString(op.exitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700370 }
371 if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700372 writer.print(innerPrefix);
373 writer.print("popEnterAnim=#");
374 writer.print(Integer.toHexString(op.popEnterAnim));
375 writer.print(" popExitAnim=#");
376 writer.println(Integer.toHexString(op.popExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700377 }
Chet Haasebc377842011-03-22 11:35:22 -0700378 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800379 if (op.removed != null && op.removed.size() > 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700380 for (int i = 0; i < op.removed.size(); i++) {
Dianne Hackbornd2835932010-12-13 16:28:46 -0800381 writer.print(innerPrefix);
382 if (op.removed.size() == 1) {
383 writer.print("Removed: ");
384 } else {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700385 if (i == 0) {
386 writer.println("Removed:");
387 }
George Mountd4c3c912014-06-09 12:31:34 -0700388 writer.print(innerPrefix);
389 writer.print(" #");
390 writer.print(i);
391 writer.print(": ");
Dianne Hackbornd2835932010-12-13 16:28:46 -0800392 }
393 writer.println(op.removed.get(i));
Dianne Hackborn30d71892010-12-11 10:37:55 -0800394 }
395 }
396 op = op.next;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700397 num++;
Dianne Hackborn30d71892010-12-11 10:37:55 -0800398 }
399 }
400 }
401
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700402 public BackStackRecord(FragmentManagerImpl manager) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700403 mManager = manager;
404 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700405
406 public int getId() {
407 return mIndex;
408 }
409
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800410 public int getBreadCrumbTitleRes() {
411 return mBreadCrumbTitleRes;
412 }
413
414 public int getBreadCrumbShortTitleRes() {
415 return mBreadCrumbShortTitleRes;
416 }
417
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700418 public CharSequence getBreadCrumbTitle() {
419 if (mBreadCrumbTitleRes != 0) {
420 return mManager.mActivity.getText(mBreadCrumbTitleRes);
421 }
422 return mBreadCrumbTitleText;
423 }
424
425 public CharSequence getBreadCrumbShortTitle() {
426 if (mBreadCrumbShortTitleRes != 0) {
427 return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
428 }
429 return mBreadCrumbShortTitleText;
430 }
431
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700432 void addOp(Op op) {
433 if (mHead == null) {
434 mHead = mTail = op;
435 } else {
436 op.prev = mTail;
437 mTail.next = op;
438 mTail = op;
439 }
440 op.enterAnim = mEnterAnim;
441 op.exitAnim = mExitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700442 op.popEnterAnim = mPopEnterAnim;
443 op.popExitAnim = mPopExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700444 mNumOp++;
445 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700446
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700447 public FragmentTransaction add(Fragment fragment, String tag) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700448 doAddOp(0, fragment, tag, OP_ADD);
449 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700450 }
451
452 public FragmentTransaction add(int containerViewId, Fragment fragment) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700453 doAddOp(containerViewId, fragment, null, OP_ADD);
454 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700455 }
456
457 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700458 doAddOp(containerViewId, fragment, tag, OP_ADD);
459 return this;
460 }
461
462 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
Dianne Hackborn3e449ce2010-09-11 20:52:31 -0700463 fragment.mFragmentManager = mManager;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700464
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700465 if (tag != null) {
466 if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
467 throw new IllegalStateException("Can't change tag of fragment "
468 + fragment + ": was " + fragment.mTag
469 + " now " + tag);
470 }
471 fragment.mTag = tag;
472 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700473
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700474 if (containerViewId != 0) {
475 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
476 throw new IllegalStateException("Can't change container ID of fragment "
477 + fragment + ": was " + fragment.mFragmentId
478 + " now " + containerViewId);
479 }
480 fragment.mContainerId = fragment.mFragmentId = containerViewId;
481 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700482
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700483 Op op = new Op();
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700484 op.cmd = opcmd;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700485 op.fragment = fragment;
486 addOp(op);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700487 }
488
489 public FragmentTransaction replace(int containerViewId, Fragment fragment) {
490 return replace(containerViewId, fragment, null);
491 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700492
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700493 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
494 if (containerViewId == 0) {
495 throw new IllegalArgumentException("Must use non-zero containerViewId");
496 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700497
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700498 doAddOp(containerViewId, fragment, tag, OP_REPLACE);
499 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700500 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700501
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700502 public FragmentTransaction remove(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700503 Op op = new Op();
504 op.cmd = OP_REMOVE;
505 op.fragment = fragment;
506 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700507
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700508 return this;
509 }
510
511 public FragmentTransaction hide(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700512 Op op = new Op();
513 op.cmd = OP_HIDE;
514 op.fragment = fragment;
515 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700516
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700517 return this;
518 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700519
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700520 public FragmentTransaction show(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700521 Op op = new Op();
522 op.cmd = OP_SHOW;
523 op.fragment = fragment;
524 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700525
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700526 return this;
527 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700528
Dianne Hackborn47c41562011-04-15 19:00:20 -0700529 public FragmentTransaction detach(Fragment fragment) {
Dianne Hackborn47c41562011-04-15 19:00:20 -0700530 Op op = new Op();
531 op.cmd = OP_DETACH;
532 op.fragment = fragment;
533 addOp(op);
534
535 return this;
536 }
537
538 public FragmentTransaction attach(Fragment fragment) {
Dianne Hackborn47c41562011-04-15 19:00:20 -0700539 Op op = new Op();
540 op.cmd = OP_ATTACH;
541 op.fragment = fragment;
542 addOp(op);
543
544 return this;
545 }
546
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700547 public FragmentTransaction setCustomAnimations(int enter, int exit) {
Chet Haasebc377842011-03-22 11:35:22 -0700548 return setCustomAnimations(enter, exit, 0, 0);
549 }
550
551 public FragmentTransaction setCustomAnimations(int enter, int exit,
552 int popEnter, int popExit) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700553 mEnterAnim = enter;
554 mExitAnim = exit;
Chet Haasebc377842011-03-22 11:35:22 -0700555 mPopEnterAnim = popEnter;
556 mPopExitAnim = popExit;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700557 return this;
558 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700559
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700560 public FragmentTransaction setTransition(int transition) {
561 mTransition = transition;
562 return this;
563 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700564
George Mountd4c3c912014-06-09 12:31:34 -0700565 @Override
George Mount448bace2014-08-18 16:27:43 -0700566 public FragmentTransaction addSharedElement(View sharedElement, String name) {
567 String transitionName = sharedElement.getTransitionName();
568 if (transitionName == null) {
569 throw new IllegalArgumentException("Unique transitionNames are required for all" +
570 " sharedElements");
571 }
572 if (mSharedElementSourceNames == null) {
573 mSharedElementSourceNames = new ArrayList<String>();
574 mSharedElementTargetNames = new ArrayList<String>();
575 }
576 mSharedElementSourceNames.add(transitionName);
577 mSharedElementTargetNames.add(name);
578 return this;
579 }
580
581 /** TODO: remove this */
582 @Override
George Mountd4c3c912014-06-09 12:31:34 -0700583 public FragmentTransaction setSharedElement(View sharedElement, String name) {
George Mount0a2ae002014-06-23 14:57:27 +0000584 String transitionName = sharedElement.getTransitionName();
585 if (transitionName == null) {
586 throw new IllegalArgumentException("Unique transitionNames are required for all" +
George Mountd4c3c912014-06-09 12:31:34 -0700587 " sharedElements");
588 }
589 mSharedElementSourceNames = new ArrayList<String>(1);
George Mount0a2ae002014-06-23 14:57:27 +0000590 mSharedElementSourceNames.add(transitionName);
George Mountd4c3c912014-06-09 12:31:34 -0700591
592 mSharedElementTargetNames = new ArrayList<String>(1);
593 mSharedElementTargetNames.add(name);
594 return this;
595 }
596
George Mount448bace2014-08-18 16:27:43 -0700597 /** TODO: remove this */
George Mountd4c3c912014-06-09 12:31:34 -0700598 @Override
599 public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) {
600 if (sharedElements == null || sharedElements.length == 0) {
601 mSharedElementSourceNames = null;
602 mSharedElementTargetNames = null;
603 } else {
604 ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length);
605 ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length);
606 for (int i = 0; i < sharedElements.length; i++) {
George Mount0a2ae002014-06-23 14:57:27 +0000607 String transitionName = sharedElements[i].first.getTransitionName();
608 if (transitionName == null) {
609 throw new IllegalArgumentException("Unique transitionNames are required for all"
610 + " sharedElements");
George Mountd4c3c912014-06-09 12:31:34 -0700611 }
George Mount0a2ae002014-06-23 14:57:27 +0000612 sourceNames.add(transitionName);
George Mountd4c3c912014-06-09 12:31:34 -0700613 targetNames.add(sharedElements[i].second);
614 }
615 mSharedElementSourceNames = sourceNames;
616 mSharedElementTargetNames = targetNames;
617 }
618 return this;
619 }
620
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700621 public FragmentTransaction setTransitionStyle(int styleRes) {
622 mTransitionStyle = styleRes;
623 return this;
624 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700625
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700626 public FragmentTransaction addToBackStack(String name) {
Adam Powell0c24a552010-11-03 16:44:11 -0700627 if (!mAllowAddToBackStack) {
628 throw new IllegalStateException(
629 "This FragmentTransaction is not allowed to be added to the back stack.");
630 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700631 mAddToBackStack = true;
632 mName = name;
633 return this;
634 }
635
Adam Powell0c24a552010-11-03 16:44:11 -0700636 public boolean isAddToBackStackAllowed() {
637 return mAllowAddToBackStack;
638 }
639
640 public FragmentTransaction disallowAddToBackStack() {
641 if (mAddToBackStack) {
642 throw new IllegalStateException(
643 "This transaction is already being added to the back stack");
644 }
645 mAllowAddToBackStack = false;
646 return this;
647 }
648
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700649 public FragmentTransaction setBreadCrumbTitle(int res) {
650 mBreadCrumbTitleRes = res;
651 mBreadCrumbTitleText = null;
652 return this;
653 }
654
655 public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
656 mBreadCrumbTitleRes = 0;
657 mBreadCrumbTitleText = text;
658 return this;
659 }
660
661 public FragmentTransaction setBreadCrumbShortTitle(int res) {
662 mBreadCrumbShortTitleRes = res;
663 mBreadCrumbShortTitleText = null;
664 return this;
665 }
666
667 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
668 mBreadCrumbShortTitleRes = 0;
669 mBreadCrumbShortTitleText = text;
670 return this;
671 }
672
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700673 void bumpBackStackNesting(int amt) {
674 if (!mAddToBackStack) {
675 return;
676 }
George Mountd4c3c912014-06-09 12:31:34 -0700677 if (FragmentManagerImpl.DEBUG) {
678 Log.v(TAG, "Bump nesting in " + this
679 + " by " + amt);
680 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700681 Op op = mHead;
682 while (op != null) {
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700683 if (op.fragment != null) {
684 op.fragment.mBackStackNesting += amt;
George Mountd4c3c912014-06-09 12:31:34 -0700685 if (FragmentManagerImpl.DEBUG) {
686 Log.v(TAG, "Bump nesting of "
687 + op.fragment + " to " + op.fragment.mBackStackNesting);
688 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700689 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700690 if (op.removed != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700691 for (int i = op.removed.size() - 1; i >= 0; i--) {
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700692 Fragment r = op.removed.get(i);
693 r.mBackStackNesting += amt;
George Mountd4c3c912014-06-09 12:31:34 -0700694 if (FragmentManagerImpl.DEBUG) {
695 Log.v(TAG, "Bump nesting of "
696 + r + " to " + r.mBackStackNesting);
697 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700698 }
699 }
700 op = op.next;
701 }
702 }
703
Dianne Hackborndd913a52010-07-22 12:17:04 -0700704 public int commit() {
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700705 return commitInternal(false);
706 }
707
708 public int commitAllowingStateLoss() {
709 return commitInternal(true);
710 }
George Mountd4c3c912014-06-09 12:31:34 -0700711
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700712 int commitInternal(boolean allowStateLoss) {
George Mountd4c3c912014-06-09 12:31:34 -0700713 if (mCommitted) {
714 throw new IllegalStateException("commit already called");
715 }
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700716 if (FragmentManagerImpl.DEBUG) {
717 Log.v(TAG, "Commit: " + this);
718 LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700719 PrintWriter pw = new FastPrintWriter(logw, false, 1024);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700720 dump(" ", null, pw, null);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700721 pw.flush();
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700722 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700723 mCommitted = true;
Dianne Hackborndd913a52010-07-22 12:17:04 -0700724 if (mAddToBackStack) {
725 mIndex = mManager.allocBackStackIndex(this);
726 } else {
727 mIndex = -1;
728 }
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700729 mManager.enqueueAction(this, allowStateLoss);
Dianne Hackborndd913a52010-07-22 12:17:04 -0700730 return mIndex;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700731 }
George Mountd4c3c912014-06-09 12:31:34 -0700732
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700733 public void run() {
George Mountd4c3c912014-06-09 12:31:34 -0700734 if (FragmentManagerImpl.DEBUG) {
735 Log.v(TAG, "Run: " + this);
736 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700737
Dianne Hackborndd913a52010-07-22 12:17:04 -0700738 if (mAddToBackStack) {
739 if (mIndex < 0) {
740 throw new IllegalStateException("addToBackStack() called after commit()");
741 }
742 }
743
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700744 bumpBackStackNesting(1);
745
George Mountc03da0e2014-08-22 17:04:02 -0700746 SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
747 SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
748
749 calculateFragments(firstOutFragments, lastInFragments);
750
751 TransitionState state = null;
752 if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
753 state = beginTransition(firstOutFragments, lastInFragments, false);
754 }
George Mountd4c3c912014-06-09 12:31:34 -0700755
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700756 Op op = mHead;
757 while (op != null) {
758 switch (op.cmd) {
759 case OP_ADD: {
760 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700761 f.mNextAnim = op.enterAnim;
762 mManager.addFragment(f, false);
George Mountd4c3c912014-06-09 12:31:34 -0700763 }
764 break;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700765 case OP_REPLACE: {
766 Fragment f = op.fragment;
767 if (mManager.mAdded != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700768 for (int i = 0; i < mManager.mAdded.size(); i++) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700769 Fragment old = mManager.mAdded.get(i);
George Mountd4c3c912014-06-09 12:31:34 -0700770 if (FragmentManagerImpl.DEBUG) {
771 Log.v(TAG,
772 "OP_REPLACE: adding=" + f + " old=" + old);
773 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700774 if (f == null || old.mContainerId == f.mContainerId) {
775 if (old == f) {
776 op.fragment = f = null;
777 } else {
778 if (op.removed == null) {
779 op.removed = new ArrayList<Fragment>();
780 }
781 op.removed.add(old);
782 old.mNextAnim = op.exitAnim;
783 if (mAddToBackStack) {
784 old.mBackStackNesting += 1;
George Mountd4c3c912014-06-09 12:31:34 -0700785 if (FragmentManagerImpl.DEBUG) {
786 Log.v(TAG, "Bump nesting of "
787 + old + " to " + old.mBackStackNesting);
788 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700789 }
790 mManager.removeFragment(old, mTransition, mTransitionStyle);
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700791 }
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700792 }
793 }
794 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700795 if (f != null) {
796 f.mNextAnim = op.enterAnim;
797 mManager.addFragment(f, false);
798 }
George Mountd4c3c912014-06-09 12:31:34 -0700799 }
800 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700801 case OP_REMOVE: {
802 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700803 f.mNextAnim = op.exitAnim;
804 mManager.removeFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700805 }
806 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700807 case OP_HIDE: {
808 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700809 f.mNextAnim = op.exitAnim;
810 mManager.hideFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700811 }
812 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700813 case OP_SHOW: {
814 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700815 f.mNextAnim = op.enterAnim;
816 mManager.showFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700817 }
818 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700819 case OP_DETACH: {
820 Fragment f = op.fragment;
821 f.mNextAnim = op.exitAnim;
822 mManager.detachFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700823 }
824 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700825 case OP_ATTACH: {
826 Fragment f = op.fragment;
827 f.mNextAnim = op.enterAnim;
828 mManager.attachFragment(f, mTransition, mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -0700829 }
830 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700831 default: {
832 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
833 }
834 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700835
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700836 op = op.next;
837 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700838
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700839 mManager.moveToState(mManager.mCurState, mTransition,
840 mTransitionStyle, true);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700841
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700842 if (mAddToBackStack) {
843 mManager.addBackStackState(this);
844 }
George Mountd4c3c912014-06-09 12:31:34 -0700845
846 if (state != null) {
George Mountc03da0e2014-08-22 17:04:02 -0700847 updateTransitionEndState(state, firstOutFragments, lastInFragments, false);
George Mountd4c3c912014-06-09 12:31:34 -0700848 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700849 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700850
George Mountc03da0e2014-08-22 17:04:02 -0700851 private static void setFirstOut(SparseArray<Fragment> fragments, Fragment fragment) {
852 if (fragment != null) {
853 int containerId = fragment.mContainerId;
854 if (containerId != 0 && !fragment.isHidden() && fragment.isAdded() &&
855 fragment.getView() != null && fragments.get(containerId) == null) {
856 fragments.put(containerId, fragment);
857 }
George Mountd4c3c912014-06-09 12:31:34 -0700858 }
George Mountc03da0e2014-08-22 17:04:02 -0700859 }
George Mountd4c3c912014-06-09 12:31:34 -0700860
George Mountc03da0e2014-08-22 17:04:02 -0700861 private void setLastIn(SparseArray<Fragment> fragments, Fragment fragment) {
862 if (fragment != null) {
863 int containerId = fragment.mContainerId;
864 if (containerId != 0) {
865 fragments.put(containerId, fragment);
866 }
867 }
868 }
869
870 /**
871 * Finds the first removed fragment and last added fragments when going forward.
872 * If none of the fragments have transitions, then both lists will be empty.
873 *
874 * @param firstOutFragments The list of first fragments to be removed, keyed on the
875 * container ID. This list will be modified by the method.
876 * @param lastInFragments The list of last fragments to be added, keyed on the
877 * container ID. This list will be modified by the method.
878 */
879 private void calculateFragments(SparseArray<Fragment> firstOutFragments,
880 SparseArray<Fragment> lastInFragments) {
881 Op op = mHead;
882 while (op != null) {
883 switch (op.cmd) {
884 case OP_ADD:
885 setLastIn(lastInFragments, op.fragment);
886 break;
887 case OP_REPLACE: {
888 Fragment f = op.fragment;
889 if (mManager.mAdded != null) {
890 for (int i = 0; i < mManager.mAdded.size(); i++) {
891 Fragment old = mManager.mAdded.get(i);
892 if (f == null || old.mContainerId == f.mContainerId) {
893 if (old == f) {
894 f = null;
895 } else {
896 setFirstOut(firstOutFragments, old);
897 }
898 }
899 }
900 }
901 setLastIn(lastInFragments, f);
902 break;
903 }
904 case OP_REMOVE:
905 setFirstOut(firstOutFragments, op.fragment);
906 break;
907 case OP_HIDE:
908 setFirstOut(firstOutFragments, op.fragment);
909 break;
910 case OP_SHOW:
911 setLastIn(lastInFragments, op.fragment);
912 break;
913 case OP_DETACH:
914 setFirstOut(firstOutFragments, op.fragment);
915 break;
916 case OP_ATTACH:
917 setLastIn(lastInFragments, op.fragment);
918 break;
919 }
920
921 op = op.next;
922 }
923
924 if (!haveTransitions(firstOutFragments, lastInFragments, false)) {
925 firstOutFragments.clear();
926 lastInFragments.clear();
927 }
928 }
929
930 /**
931 * @return true if custom transitions exist on any fragment in firstOutFragments or
932 * lastInFragments or false otherwise.
933 */
934 private static boolean haveTransitions(SparseArray<Fragment> firstOutFragments,
935 SparseArray<Fragment> lastInFragments, boolean isBack) {
936 for (int i = firstOutFragments.size() - 1; i >= 0; i--) {
937 Fragment f = firstOutFragments.valueAt(i);
938 if (isBack) {
939 if (f.getReturnTransition() != null ||
940 f.getSharedElementReturnTransition() != null) {
941 return true;
942 }
943 } else if (f.getExitTransition() != null) {
944 return true;
945 }
946 }
947
948 for (int i = lastInFragments.size() - 1; i >= 0; i--) {
949 Fragment f = lastInFragments.valueAt(i);
950 if (isBack) {
951 if (f.getReenterTransition() != null) {
952 return true;
953 }
954 } else if (f.getEnterTransition() != null ||
955 f.getSharedElementEnterTransition() != null) {
956 return true;
957 }
958 }
959 return false;
960 }
961
962 /**
963 * Finds the first removed fragment and last added fragments when popping the back stack.
964 * If none of the fragments have transitions, then both lists will be empty.
965 *
966 * @param firstOutFragments The list of first fragments to be removed, keyed on the
967 * container ID. This list will be modified by the method.
968 * @param lastInFragments The list of last fragments to be added, keyed on the
969 * container ID. This list will be modified by the method.
970 */
971 public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
972 SparseArray<Fragment> lastInFragments) {
973 Op op = mHead;
974 while (op != null) {
975 switch (op.cmd) {
976 case OP_ADD:
977 setFirstOut(firstOutFragments, op.fragment);
978 break;
979 case OP_REPLACE:
980 if (op.removed != null) {
981 for (int i = op.removed.size() - 1; i >= 0; i--) {
982 setLastIn(lastInFragments, op.removed.get(i));
983 }
984 }
985 setFirstOut(firstOutFragments, op.fragment);
986 break;
987 case OP_REMOVE:
988 setLastIn(lastInFragments, op.fragment);
989 break;
990 case OP_HIDE:
991 setLastIn(lastInFragments, op.fragment);
992 break;
993 case OP_SHOW:
994 setFirstOut(firstOutFragments, op.fragment);
995 break;
996 case OP_DETACH:
997 setLastIn(lastInFragments, op.fragment);
998 break;
999 case OP_ATTACH:
1000 setFirstOut(firstOutFragments, op.fragment);
1001 break;
1002 }
1003
1004 op = op.next;
1005 }
1006
1007 if (!haveTransitions(firstOutFragments, lastInFragments, true)) {
1008 firstOutFragments.clear();
1009 lastInFragments.clear();
1010 }
1011 }
1012
1013 /**
1014 * When custom fragment transitions are used, this sets up the state for each transition
1015 * and begins the transition. A different transition is started for each fragment container
1016 * and consists of up to 3 different transitions: the exit transition, a shared element
1017 * transition and an enter transition.
1018 *
1019 * <p>The exit transition operates against the leaf nodes of the first fragment
1020 * with a view that was removed. If no such fragment was removed, then no exit
1021 * transition is executed. The exit transition comes from the outgoing fragment.</p>
1022 *
1023 * <p>The enter transition operates against the last fragment that was added. If
1024 * that fragment does not have a view or no fragment was added, then no enter
1025 * transition is executed. The enter transition comes from the incoming fragment.</p>
1026 *
1027 * <p>The shared element transition operates against all views and comes either
1028 * from the outgoing fragment or the incoming fragment, depending on whether this
1029 * is going forward or popping the back stack. When going forward, the incoming
1030 * fragment's enter shared element transition is used, but when going back, the
1031 * outgoing fragment's return shared element transition is used. Shared element
1032 * transitions only operate if there is both an incoming and outgoing fragment.</p>
1033 *
1034 * @param firstOutFragments The list of first fragments to be removed, keyed on the
1035 * container ID.
1036 * @param lastInFragments The list of last fragments to be added, keyed on the
1037 * container ID.
1038 * @param isBack true if this is popping the back stack or false if this is a
1039 * forward operation.
1040 * @return The TransitionState used to complete the operation of the transition
1041 * in {@link #updateTransitionEndState(android.app.BackStackRecord.TransitionState,
1042 * android.util.SparseArray, android.util.SparseArray, boolean)}.
1043 */
1044 private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
1045 SparseArray<Fragment> lastInFragments, boolean isBack) {
1046 TransitionState state = new TransitionState();
1047
George Mountd4c3c912014-06-09 12:31:34 -07001048 // Adding a non-existent target view makes sure that the transitions don't target
1049 // any views by default. They'll only target the views we tell add. If we don't
1050 // add any, then no views will be targeted.
George Mountc03da0e2014-08-22 17:04:02 -07001051 state.nonExistentView = new View(mManager.mActivity);
George Mountd4c3c912014-06-09 12:31:34 -07001052
George Mountc03da0e2014-08-22 17:04:02 -07001053 ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>();
1054 ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>();
1055 ArrayList<String> tempNames = new ArrayList<String>();
1056 ArrayList<View> tempViewList = new ArrayList<View>();
George Mountd4c3c912014-06-09 12:31:34 -07001057
George Mountc03da0e2014-08-22 17:04:02 -07001058 // Go over all leaving fragments.
1059 for (int i = 0; i < firstOutFragments.size(); i++) {
1060 int containerId = firstOutFragments.keyAt(i);
1061 configureTransitions(containerId, state, isBack, firstOutFragments,
1062 lastInFragments, tempViews1, tempViews2, tempNames, tempViewList);
George Mountd4c3c912014-06-09 12:31:34 -07001063 }
1064
George Mountc03da0e2014-08-22 17:04:02 -07001065 // Now go over all entering fragments that didn't have a leaving fragment.
1066 for (int i = 0; i < lastInFragments.size(); i++) {
1067 int containerId = lastInFragments.keyAt(i);
1068 if (firstOutFragments.get(containerId) == null) {
1069 configureTransitions(containerId, state, isBack, firstOutFragments,
1070 lastInFragments, tempViews1, tempViews2, tempNames, tempViewList);
1071 }
1072 }
George Mountd4c3c912014-06-09 12:31:34 -07001073
George Mountc03da0e2014-08-22 17:04:02 -07001074 if (state.overallTransitions.size() == 0) {
1075 state = null;
1076 }
George Mountd4c3c912014-06-09 12:31:34 -07001077 return state;
1078 }
1079
George Mountc03da0e2014-08-22 17:04:02 -07001080 private static Transition getEnterTransition(Fragment inFragment, boolean isBack) {
1081 if (inFragment == null) {
1082 return null;
1083 }
1084 return isBack ? inFragment.getReenterTransition() : inFragment.getEnterTransition();
1085 }
George Mountd4c3c912014-06-09 12:31:34 -07001086
George Mountc03da0e2014-08-22 17:04:02 -07001087 private static Transition getExitTransition(Fragment outFragment, boolean isBack) {
1088 if (outFragment == null) {
1089 return null;
1090 }
1091 return isBack ? outFragment.getReturnTransition() : outFragment.getExitTransition();
1092 }
1093
1094 private static Transition getSharedElementTransition(Fragment inFragment, Fragment outFragment,
1095 boolean isBack) {
1096 if (inFragment == null || outFragment == null) {
1097 return null;
1098 }
1099 return isBack ? outFragment.getSharedElementReturnTransition() :
1100 inFragment.getSharedElementEnterTransition();
1101 }
1102
1103 private static Transition captureExitingViews(Transition exitTransition, Fragment outFragment,
1104 ArrayList<View> viewList) {
1105 if (exitTransition != null) {
1106 View root = outFragment.getView();
1107 viewList.clear();
1108 root.captureTransitioningViews(viewList);
1109 if (viewList.isEmpty()) {
1110 exitTransition = null;
1111 } else {
1112 addTransitioningViews(exitTransition, viewList);
1113 }
1114 }
1115 return exitTransition;
1116 }
1117
1118 private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
1119 ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) {
1120 if (mSharedElementSourceNames != null) {
1121 outFragment.getView().findNamedViews(namedViews);
1122 if (isBack) {
1123 namedViews.retainAll(mSharedElementTargetNames);
1124 } else {
1125 namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
1126 namedViews, tempViews2);
George Mountd4c3c912014-06-09 12:31:34 -07001127 }
1128 }
1129
George Mountc03da0e2014-08-22 17:04:02 -07001130 if (isBack) {
1131 outFragment.mEnterTransitionListener.remapSharedElements(
1132 mSharedElementTargetNames, namedViews);
1133 setBackNameOverrides(state, namedViews, false);
George Mountd4c3c912014-06-09 12:31:34 -07001134 } else {
George Mountc03da0e2014-08-22 17:04:02 -07001135 outFragment.mExitTransitionListener.remapSharedElements(
1136 mSharedElementTargetNames, namedViews);
1137 setNameOverrides(state, namedViews, false);
1138 }
1139
1140 return namedViews;
1141 }
1142
1143 /**
1144 * Prepares the enter transition by adding a non-existent view to the transition's target list
1145 * and setting it epicenter callback. By adding a non-existent view to the target list,
1146 * we can prevent any view from being targeted at the beginning of the transition.
1147 * We will add to the views before the end state of the transition is captured so that the
1148 * views will appear. At the start of the transition, we clear the list of targets so that
1149 * we can restore the state of the transition and use it again.
1150 */
1151 private void prepareEnterTransition(TransitionState state, final Transition enterTransition,
1152 final View container, final Fragment inFragment) {
1153 if (enterTransition != null) {
1154 final ArrayList<View> enteringViews = new ArrayList<View>();
1155 final View nonExistentView = state.nonExistentView;
1156 enterTransition.addTarget(state.nonExistentView);
1157 enterTransition.addListener(new Transition.TransitionListenerAdapter() {
1158 @Override
1159 public void onTransitionStart(Transition transition) {
1160 transition.removeListener(this);
1161 transition.removeTarget(nonExistentView);
1162 int numViews = enteringViews.size();
1163 for (int i = 0; i < numViews; i++) {
1164 transition.removeTarget(enteringViews.get(i));
1165 }
1166 }
1167 });
1168 container.getViewTreeObserver().addOnPreDrawListener(
1169 new ViewTreeObserver.OnPreDrawListener() {
1170 @Override
1171 public boolean onPreDraw() {
1172 container.getViewTreeObserver().removeOnPreDrawListener(this);
1173 View view = inFragment.getView();
1174 if (view != null) {
1175 view.captureTransitioningViews(enteringViews);
1176 addTransitioningViews(enterTransition, enteringViews);
1177 }
1178 return true;
1179 }
1180 });
1181 setSharedElementEpicenter(enterTransition, state);
1182 }
1183 }
1184
1185 private static Transition mergeTransitions(Transition enterTransition,
1186 Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
1187 boolean isBack) {
1188 boolean overlap = true;
1189 if (enterTransition != null && exitTransition != null) {
1190 overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
1191 inFragment.getAllowEnterTransitionOverlap();
1192 }
1193
1194 Transition transition;
1195 if (overlap) {
1196 transition = TransitionUtils.mergeTransitions(enterTransition, exitTransition,
1197 sharedElementTransition);
1198 } else {
1199 TransitionSet staggered = new TransitionSet()
1200 .addTransition(exitTransition)
1201 .addTransition(enterTransition)
1202 .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
1203 transition = TransitionUtils.mergeTransitions(staggered, sharedElementTransition);
1204 }
1205 return transition;
1206 }
1207
1208 /**
1209 * Configures custom transitions for a specific fragment container.
1210 *
1211 * @param containerId The container ID of the fragments to configure the transition for.
1212 * @param state The Transition State to be shared with {@link #updateTransitionEndState(
1213 * android.app.BackStackRecord.TransitionState, android.util.SparseArray,
1214 * android.util.SparseArray, boolean)} later.
1215 * @param firstOutFragments The list of first fragments to be removed, keyed on the
1216 * container ID.
1217 * @param lastInFragments The list of last fragments to be added, keyed on the
1218 * container ID.
1219 * @param isBack true if this is popping the back stack or false if this is a
1220 * forward operation.
1221 * @param tempViews1 A temporary mapping of names to Views, used to avoid allocation
1222 * inside a loop.
1223 * @param tempViews2 A temporary mapping of names to Views, used to avoid allocation
1224 * inside a loop.
1225 * @param tempNames A temporary list of Strings, used to avoid allocation inside a loop.
1226 * @param tempViewList A temporary list of Views, used to avoid allocation inside a loop.
1227 */
1228 private void configureTransitions(int containerId, TransitionState state, boolean isBack,
1229 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments,
1230 ArrayMap<String, View> tempViews1, ArrayMap<String, View> tempViews2,
1231 ArrayList<String> tempNames, ArrayList<View> tempViewList) {
1232 ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId);
1233 if (sceneRoot != null) {
1234 Fragment inFragment = lastInFragments.get(containerId);
1235 Fragment outFragment = firstOutFragments.get(containerId);
1236
1237 Transition enterTransition = getEnterTransition(inFragment, isBack);
1238 Transition sharedElementTransition = getSharedElementTransition(inFragment, outFragment,
1239 isBack);
1240 Transition exitTransition = getExitTransition(outFragment, isBack);
1241 exitTransition = captureExitingViews(exitTransition, outFragment, tempViewList);
1242
1243 ArrayMap<String, View> namedViews = tempViews1;
1244 namedViews.clear();
1245 if (sharedElementTransition != null) {
1246 namedViews = remapSharedElements(state,
1247 outFragment, namedViews, tempViews2, isBack);
1248 }
1249
1250 // Notify the start of the transition.
1251 SharedElementListener listener = isBack ?
1252 outFragment.mEnterTransitionListener :
1253 inFragment.mEnterTransitionListener;
1254 tempNames.clear();
1255 tempNames.addAll(namedViews.keySet());
1256 tempViewList.clear();
1257 tempViewList.addAll(namedViews.values());
1258 listener.setSharedElementStart(tempNames, tempViewList, null);
1259
1260 // Set the epicenter of the exit transition
1261 if (mSharedElementTargetNames != null && exitTransition != null) {
1262 View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
1263 if (epicenterView != null) {
1264 setEpicenter(exitTransition, epicenterView);
1265 }
1266 }
1267
1268 prepareEnterTransition(state, enterTransition, sceneRoot, inFragment);
1269
1270 Transition transition = mergeTransitions(enterTransition, exitTransition,
1271 sharedElementTransition, inFragment, isBack);
1272
1273 if (transition != null) {
1274 state.overallTransitions.put(containerId, transition);
1275 transition.setNameOverrides(state.nameOverrides);
1276 // We want to exclude hidden views later, so we need a non-null list in the
1277 // transition now.
1278 transition.excludeTarget(state.nonExistentView, true);
1279 // Now exclude all currently hidden fragments.
1280 excludeHiddenFragments(state, containerId, transition);
1281 cleanupHiddenFragments(transition, state);
1282 TransitionManager.beginDelayedTransition(sceneRoot, transition);
George Mountd4c3c912014-06-09 12:31:34 -07001283 }
1284 }
1285 }
1286
George Mountc03da0e2014-08-22 17:04:02 -07001287 /**
1288 * Remaps a name-to-View map, substituting different names for keys.
1289 *
1290 * @param inMap A list of keys found in the map, in the order in toGoInMap
1291 * @param toGoInMap A list of keys to use for the new map, in the order of inMap
1292 * @param namedViews The current mapping
1293 * @param tempMap A temporary mapping that will be filled with the new values.
1294 * @return tempMap after it has been mapped with the new names as keys.
1295 */
1296 private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
1297 ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews,
1298 ArrayMap<String, View> tempMap) {
1299 tempMap.clear();
1300 if (!namedViews.isEmpty()) {
1301 int numKeys = inMap.size();
1302 for (int i = 0; i < numKeys; i++) {
1303 View view = namedViews.get(inMap.get(i));
1304 if (view != null) {
1305 tempMap.put(toGoInMap.get(i), view);
George Mountd4c3c912014-06-09 12:31:34 -07001306 }
1307 }
1308 }
George Mountc03da0e2014-08-22 17:04:02 -07001309 return tempMap;
1310 }
1311
1312 /**
1313 * After making all fragment changes, this updates the custom transitions to take into
1314 * account the entering views and any remapping.
1315 *
1316 * @param state The transition State as returned from {@link #beginTransition(
1317 * android.util.SparseArray, android.util.SparseArray, boolean)}.
1318 * @param outFragments The list of first fragments to be removed, keyed on the
1319 * container ID.
1320 * @param inFragments The list of last fragments to be added, keyed on the
1321 * container ID.
1322 * @param isBack true if this is popping the back stack or false if this is a
1323 * forward operation.
1324 */
1325 private void updateTransitionEndState(TransitionState state, SparseArray<Fragment> outFragments,
1326 SparseArray<Fragment> inFragments, boolean isBack) {
1327 ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>();
1328 ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>();
1329 ArrayList<String> tempNames = new ArrayList<String>();
1330 ArrayList<View> tempViews = new ArrayList<View>();
1331
1332 int numInFragments = inFragments.size();
1333 for (int i = 0; i < numInFragments; i++) {
1334 Fragment inFragment = inFragments.valueAt(i);
1335 tempViews1.clear();
1336 ArrayMap<String, View> namedViews = mapEnteringSharedElements(inFragment, tempViews1,
1337 tempViews2, isBack);
1338 // remap shared elements and set the name mapping used in the shared element transition.
1339 if (isBack) {
1340 inFragment.mExitTransitionListener.remapSharedElements(
1341 mSharedElementTargetNames, namedViews);
1342 setBackNameOverrides(state, namedViews, true);
1343 } else {
1344 inFragment.mEnterTransitionListener.remapSharedElements(
1345 mSharedElementTargetNames, namedViews);
1346 setNameOverrides(state, namedViews, true);
1347 }
1348
1349 if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
1350 // now we know the epicenter of the entering transition.
1351 View epicenter = namedViews.get(mSharedElementTargetNames.get(0));
1352 if (epicenter != null) {
1353 state.enteringEpicenterView = epicenter;
1354 }
1355 }
1356
1357 int containerId = inFragments.keyAt(i);
1358 SharedElementListener sharedElementListener = isBack ?
1359 outFragments.get(containerId).mEnterTransitionListener :
1360 inFragment.mEnterTransitionListener;
1361 tempNames.clear();
1362 tempNames.addAll(namedViews.keySet());
1363 tempViews.clear();
1364 tempViews.addAll(namedViews.values());
1365 sharedElementListener.setSharedElementEnd(tempNames, tempViews, null);
1366 }
1367
1368 // Don't include any newly-hidden fragments in the transition.
1369 excludeHiddenFragments(state);
1370 }
1371
1372 private ArrayMap<String, View> mapEnteringSharedElements(Fragment inFragment,
1373 ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) {
1374 View root = inFragment.getView();
1375 if (root != null) {
1376 if (mSharedElementSourceNames != null) {
1377 root.findNamedViews(namedViews);
1378 if (isBack) {
1379 namedViews = remapNames(mSharedElementSourceNames,
1380 mSharedElementTargetNames, namedViews, tempViews2);
1381 } else {
1382 namedViews.retainAll(mSharedElementTargetNames);
1383 }
1384 }
1385 }
1386 return namedViews;
1387 }
1388
1389 private static void cleanupHiddenFragments(Transition transition, TransitionState state) {
1390 final ArrayList<View> hiddenViews = state.hiddenFragmentViews;
1391 transition.addListener(new Transition.TransitionListenerAdapter() {
1392 @Override
1393 public void onTransitionStart(Transition transition) {
1394 transition.removeListener(this);
1395 int numViews = hiddenViews.size();
1396 for (int i = 0; i < numViews; i++) {
1397 transition.excludeTarget(hiddenViews.get(i), false);
1398 }
1399 }
1400 });
1401 }
1402
1403 private void excludeHiddenFragments(TransitionState state, int containerId,
1404 Transition transition) {
1405 if (mManager.mAdded != null) {
1406 for (int i = 0; i < mManager.mAdded.size(); i++) {
1407 Fragment fragment = mManager.mAdded.get(i);
1408 if (fragment.mView != null && fragment.mContainer != null &&
1409 fragment.mContainerId == containerId) {
1410 if (fragment.mHidden) {
1411 if (!state.hiddenFragmentViews.contains(fragment.mView)) {
1412 transition.excludeTarget(fragment.mView, true);
1413 state.hiddenFragmentViews.add(fragment.mView);
1414 }
1415 } else {
1416 transition.excludeTarget(fragment.mView, false);
1417 state.hiddenFragmentViews.remove(fragment.mView);
1418 }
1419 }
1420 }
George Mountd4c3c912014-06-09 12:31:34 -07001421 }
1422 }
1423
George Mountc03da0e2014-08-22 17:04:02 -07001424 private void excludeHiddenFragments(TransitionState state) {
1425 int numTransitions = state.overallTransitions.size();
1426 for (int i = 0; i < numTransitions; i++) {
1427 Transition transition = state.overallTransitions.valueAt(i);
1428 int containerId = state.overallTransitions.keyAt(i);
1429 excludeHiddenFragments(state, containerId, transition);
1430 }
1431 }
1432
1433 private static void addTransitioningViews(Transition transition, final Collection<View> views) {
1434 for (View view : views) {
1435 transition.addTarget(view);
1436 }
1437
1438 transition.addListener(new Transition.TransitionListenerAdapter() {
1439 @Override
1440 public void onTransitionStart(Transition transition) {
1441 transition.removeListener(this);
1442 for (View view : views) {
1443 transition.removeTarget(view);
George Mountd4c3c912014-06-09 12:31:34 -07001444 }
1445 }
George Mountc03da0e2014-08-22 17:04:02 -07001446 });
George Mountd4c3c912014-06-09 12:31:34 -07001447 }
1448
1449 private static void setEpicenter(Transition transition, View view) {
1450 final Rect epicenter = new Rect();
1451 view.getBoundsOnScreen(epicenter);
1452
1453 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1454 @Override
1455 public Rect onGetEpicenter(Transition transition) {
1456 return epicenter;
1457 }
1458 });
1459 }
1460
1461 private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
1462 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1463 private Rect mEpicenter;
1464
1465 @Override
1466 public Rect onGetEpicenter(Transition transition) {
George Mountc03da0e2014-08-22 17:04:02 -07001467 if (mEpicenter == null && state.enteringEpicenterView != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001468 mEpicenter = new Rect();
George Mountc03da0e2014-08-22 17:04:02 -07001469 state.enteringEpicenterView.getBoundsOnScreen(mEpicenter);
George Mountd4c3c912014-06-09 12:31:34 -07001470 }
1471 return mEpicenter;
1472 }
1473 });
1474 }
1475
George Mountc03da0e2014-08-22 17:04:02 -07001476 public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
1477 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001478 if (FragmentManagerImpl.DEBUG) {
1479 Log.v(TAG, "popFromBackStack: " + this);
1480 LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001481 PrintWriter pw = new FastPrintWriter(logw, false, 1024);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001482 dump(" ", null, pw, null);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001483 pw.flush();
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001484 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001485
George Mountd4c3c912014-06-09 12:31:34 -07001486 if (state == null) {
George Mountc03da0e2014-08-22 17:04:02 -07001487 if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
1488 state = beginTransition(firstOutFragments, lastInFragments, true);
1489 }
1490 } else if (!doStateMove) {
George Mountd4c3c912014-06-09 12:31:34 -07001491 setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
1492 }
1493
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001494 bumpBackStackNesting(-1);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001495
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001496 Op op = mTail;
1497 while (op != null) {
1498 switch (op.cmd) {
1499 case OP_ADD: {
1500 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001501 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001502 mManager.removeFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001503 FragmentManagerImpl.reverseTransit(mTransition),
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001504 mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001505 }
1506 break;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001507 case OP_REPLACE: {
1508 Fragment f = op.fragment;
Dianne Hackbornee76efb2012-06-05 10:27:40 -07001509 if (f != null) {
1510 f.mNextAnim = op.popExitAnim;
1511 mManager.removeFragment(f,
1512 FragmentManagerImpl.reverseTransit(mTransition),
1513 mTransitionStyle);
1514 }
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001515 if (op.removed != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001516 for (int i = 0; i < op.removed.size(); i++) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001517 Fragment old = op.removed.get(i);
Chet Haasebc377842011-03-22 11:35:22 -07001518 old.mNextAnim = op.popEnterAnim;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001519 mManager.addFragment(old, false);
1520 }
1521 }
George Mountd4c3c912014-06-09 12:31:34 -07001522 }
1523 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001524 case OP_REMOVE: {
1525 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001526 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001527 mManager.addFragment(f, false);
George Mountd4c3c912014-06-09 12:31:34 -07001528 }
1529 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001530 case OP_HIDE: {
1531 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001532 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001533 mManager.showFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001534 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001535 }
1536 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001537 case OP_SHOW: {
1538 Fragment f = op.fragment;
Chet Haasebc377842011-03-22 11:35:22 -07001539 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001540 mManager.hideFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001541 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001542 }
1543 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001544 case OP_DETACH: {
1545 Fragment f = op.fragment;
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001546 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001547 mManager.attachFragment(f,
1548 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001549 }
1550 break;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001551 case OP_ATTACH: {
1552 Fragment f = op.fragment;
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001553 f.mNextAnim = op.popExitAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001554 mManager.detachFragment(f,
1555 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountd4c3c912014-06-09 12:31:34 -07001556 }
1557 break;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001558 default: {
1559 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
1560 }
1561 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001562
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001563 op = op.prev;
1564 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001565
Dianne Hackborn5f36c962010-08-26 15:54:17 -07001566 if (doStateMove) {
1567 mManager.moveToState(mManager.mCurState,
1568 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
George Mountd4c3c912014-06-09 12:31:34 -07001569 if (state != null) {
George Mountc03da0e2014-08-22 17:04:02 -07001570 updateTransitionEndState(state, firstOutFragments, lastInFragments, true);
George Mountd4c3c912014-06-09 12:31:34 -07001571 state = null;
1572 }
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001573 }
Dianne Hackborndd913a52010-07-22 12:17:04 -07001574
1575 if (mIndex >= 0) {
1576 mManager.freeBackStackIndex(mIndex);
1577 mIndex = -1;
1578 }
George Mountd4c3c912014-06-09 12:31:34 -07001579 return state;
1580 }
1581
George Mountc03da0e2014-08-22 17:04:02 -07001582 private static void setNameOverride(ArrayMap<String, String> overrides,
1583 String source, String target) {
1584 if (source != null && target != null && !source.equals(target)) {
1585 for (int index = 0; index < overrides.size(); index++) {
1586 if (source.equals(overrides.valueAt(index))) {
1587 overrides.setValueAt(index, target);
1588 return;
1589 }
George Mountd4c3c912014-06-09 12:31:34 -07001590 }
George Mountc03da0e2014-08-22 17:04:02 -07001591 overrides.put(source, target);
George Mountd4c3c912014-06-09 12:31:34 -07001592 }
George Mountd4c3c912014-06-09 12:31:34 -07001593 }
1594
1595 private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
1596 ArrayList<String> targetNames) {
1597 if (sourceNames != null) {
1598 for (int i = 0; i < sourceNames.size(); i++) {
1599 String source = sourceNames.get(i);
1600 String target = targetNames.get(i);
George Mountc03da0e2014-08-22 17:04:02 -07001601 setNameOverride(state.nameOverrides, source, target);
1602 }
1603 }
1604 }
1605
1606 private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1607 boolean isEnd) {
1608 int count = mSharedElementTargetNames.size();
1609 for (int i = 0; i < count; i++) {
1610 String source = mSharedElementSourceNames.get(i);
1611 String originalTarget = mSharedElementTargetNames.get(i);
1612 String target = namedViews.get(originalTarget).getTransitionName();
1613 if (isEnd) {
1614 setNameOverride(state.nameOverrides, source, target);
1615 } else {
1616 setNameOverride(state.nameOverrides, target, source);
1617 }
1618 }
1619 }
1620
1621 private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1622 boolean isEnd) {
1623 int count = namedViews.size();
1624 for (int i = 0; i < count; i++) {
1625 String source = namedViews.keyAt(i);
1626 String target = namedViews.valueAt(i).getTransitionName();
1627 if (isEnd) {
1628 setNameOverride(state.nameOverrides, source, target);
1629 } else {
1630 setNameOverride(state.nameOverrides, target, source);
George Mountd4c3c912014-06-09 12:31:34 -07001631 }
1632 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001633 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001634
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001635 public String getName() {
1636 return mName;
1637 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001638
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001639 public int getTransition() {
1640 return mTransition;
1641 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001642
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001643 public int getTransitionStyle() {
1644 return mTransitionStyle;
1645 }
Adam Powell2b6230e2010-09-07 17:55:25 -07001646
1647 public boolean isEmpty() {
1648 return mNumOp == 0;
1649 }
George Mountd4c3c912014-06-09 12:31:34 -07001650
1651 public class TransitionState {
George Mountc03da0e2014-08-22 17:04:02 -07001652 public SparseArray<Transition> overallTransitions = new SparseArray<Transition>();
1653 public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
1654 public ArrayList<View> hiddenFragmentViews = new ArrayList<View>();
1655
1656 public View enteringEpicenterView;
1657 public View nonExistentView;
George Mountd4c3c912014-06-09 12:31:34 -07001658 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001659}