blob: a4b1a1f501044ba207c02e1d0696176e917a2622 [file] [log] [blame]
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
George Mountd4c3c912014-06-09 12:31:34 -070019import android.graphics.Rect;
Clara Bayarricd916f22016-06-13 13:14:51 +010020import android.os.Build;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070021import android.os.Parcel;
22import android.os.Parcelable;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070023import android.text.TextUtils;
George Mountd4c3c912014-06-09 12:31:34 -070024import android.transition.Transition;
George Mountd4c3c912014-06-09 12:31:34 -070025import android.transition.TransitionManager;
26import android.transition.TransitionSet;
27import android.util.ArrayMap;
Dianne Hackborn445646c2010-06-25 15:52:59 -070028import android.util.Log;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -070029import android.util.LogWriter;
George Mountc03da0e2014-08-22 17:04:02 -070030import android.util.SparseArray;
George Mountd4c3c912014-06-09 12:31:34 -070031import android.view.View;
32import android.view.ViewGroup;
George Mountc03da0e2014-08-22 17:04:02 -070033import android.view.ViewTreeObserver;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070034
George Mounta7245b42016-02-10 17:03:32 -080035import com.android.internal.util.FastPrintWriter;
36
Dianne Hackborn30d71892010-12-11 10:37:55 -080037import java.io.FileDescriptor;
38import java.io.PrintWriter;
Clara Bayarricd916f22016-06-13 13:14:51 +010039import java.lang.reflect.Modifier;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070040import java.util.ArrayList;
George Mount7ce5d752014-10-30 16:09:22 -070041import java.util.List;
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) {
George Mountf2045f82016-06-27 13:15:01 -070057 final int numOps = bse.mOps.size();
58 mOps = new int[numOps * 6];
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070059
Dianne Hackbornb7a2e472010-08-12 16:20:42 -070060 if (!bse.mAddToBackStack) {
61 throw new IllegalStateException("Not on back stack");
62 }
63
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070064 int pos = 0;
George Mountf2045f82016-06-27 13:15:01 -070065 for (int opNum = 0; opNum < numOps; opNum++) {
66 final BackStackRecord.Op op = bse.mOps.get(opNum);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070067 mOps[pos++] = op.cmd;
Dianne Hackbornee76efb2012-06-05 10:27:40 -070068 mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070069 mOps[pos++] = op.enterAnim;
70 mOps[pos++] = op.exitAnim;
Chet Haasebc377842011-03-22 11:35:22 -070071 mOps[pos++] = op.popEnterAnim;
72 mOps[pos++] = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070073 }
74 mTransition = bse.mTransition;
75 mTransitionStyle = bse.mTransitionStyle;
76 mName = bse.mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -070077 mIndex = bse.mIndex;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070078 mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
79 mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
80 mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
81 mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -070082 mSharedElementSourceNames = bse.mSharedElementSourceNames;
83 mSharedElementTargetNames = bse.mSharedElementTargetNames;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070084 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070085
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070086 public BackStackState(Parcel in) {
87 mOps = in.createIntArray();
88 mTransition = in.readInt();
89 mTransitionStyle = in.readInt();
90 mName = in.readString();
Dianne Hackborndd913a52010-07-22 12:17:04 -070091 mIndex = in.readInt();
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070092 mBreadCrumbTitleRes = in.readInt();
93 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
94 mBreadCrumbShortTitleRes = in.readInt();
95 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
George Mountd4c3c912014-06-09 12:31:34 -070096 mSharedElementSourceNames = in.createStringArrayList();
97 mSharedElementTargetNames = in.createStringArrayList();
Dianne Hackborn5ae74d62010-05-19 19:14:57 -070098 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070099
100 public BackStackRecord instantiate(FragmentManagerImpl fm) {
101 BackStackRecord bse = new BackStackRecord(fm);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700102 int pos = 0;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700103 int num = 0;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700104 while (pos < mOps.length) {
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700105 BackStackRecord.Op op = new BackStackRecord.Op();
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700106 op.cmd = mOps[pos++];
George Mountd4c3c912014-06-09 12:31:34 -0700107 if (FragmentManagerImpl.DEBUG) {
108 Log.v(FragmentManagerImpl.TAG,
109 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
110 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700111 int findex = mOps[pos++];
112 if (findex >= 0) {
113 Fragment f = fm.mActive.get(findex);
114 op.fragment = f;
115 } else {
116 op.fragment = null;
117 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700118 op.enterAnim = mOps[pos++];
119 op.exitAnim = mOps[pos++];
Chet Haasebc377842011-03-22 11:35:22 -0700120 op.popEnterAnim = mOps[pos++];
121 op.popExitAnim = mOps[pos++];
George Mount41fb9942016-03-30 13:59:23 -0700122 bse.mEnterAnim = op.enterAnim;
123 bse.mExitAnim = op.exitAnim;
124 bse.mPopEnterAnim = op.popEnterAnim;
125 bse.mPopExitAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700126 bse.addOp(op);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700127 num++;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700128 }
129 bse.mTransition = mTransition;
130 bse.mTransitionStyle = mTransitionStyle;
131 bse.mName = mName;
Dianne Hackborndd913a52010-07-22 12:17:04 -0700132 bse.mIndex = mIndex;
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700133 bse.mAddToBackStack = true;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700134 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
135 bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
136 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
137 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
George Mountd4c3c912014-06-09 12:31:34 -0700138 bse.mSharedElementSourceNames = mSharedElementSourceNames;
139 bse.mSharedElementTargetNames = mSharedElementTargetNames;
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700140 bse.bumpBackStackNesting(1);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700141 return bse;
142 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700143
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700144 public int describeContents() {
145 return 0;
146 }
147
148 public void writeToParcel(Parcel dest, int flags) {
149 dest.writeIntArray(mOps);
150 dest.writeInt(mTransition);
151 dest.writeInt(mTransitionStyle);
152 dest.writeString(mName);
Dianne Hackborndd913a52010-07-22 12:17:04 -0700153 dest.writeInt(mIndex);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700154 dest.writeInt(mBreadCrumbTitleRes);
155 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
156 dest.writeInt(mBreadCrumbShortTitleRes);
157 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
George Mountd4c3c912014-06-09 12:31:34 -0700158 dest.writeStringList(mSharedElementSourceNames);
159 dest.writeStringList(mSharedElementTargetNames);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700160 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700161
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700162 public static final Parcelable.Creator<BackStackState> CREATOR
163 = new Parcelable.Creator<BackStackState>() {
164 public BackStackState createFromParcel(Parcel in) {
165 return new BackStackState(in);
166 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700167
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700168 public BackStackState[] newArray(int size) {
169 return new BackStackState[size];
170 }
171 };
172}
173
174/**
175 * @hide Entry of an operation on the fragment back stack.
176 */
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700177final class BackStackRecord extends FragmentTransaction implements
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700178 FragmentManager.BackStackEntry, Runnable {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700179 static final String TAG = FragmentManagerImpl.TAG;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700180
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700181 final FragmentManagerImpl mManager;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700182
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700183 static final int OP_NULL = 0;
184 static final int OP_ADD = 1;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700185 static final int OP_REPLACE = 2;
186 static final int OP_REMOVE = 3;
187 static final int OP_HIDE = 4;
188 static final int OP_SHOW = 5;
Dianne Hackborn47c41562011-04-15 19:00:20 -0700189 static final int OP_DETACH = 6;
190 static final int OP_ATTACH = 7;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700191
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700192 static final class Op {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700193 int cmd;
194 Fragment fragment;
195 int enterAnim;
196 int exitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700197 int popEnterAnim;
198 int popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700199 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700200
George Mountf2045f82016-06-27 13:15:01 -0700201 ArrayList<Op> mOps = new ArrayList<>();
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700202 int mEnterAnim;
203 int mExitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700204 int mPopEnterAnim;
205 int mPopExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700206 int mTransition;
207 int mTransitionStyle;
208 boolean mAddToBackStack;
Adam Powell0c24a552010-11-03 16:44:11 -0700209 boolean mAllowAddToBackStack = true;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700210 String mName;
211 boolean mCommitted;
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700212 int mIndex = -1;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700213
214 int mBreadCrumbTitleRes;
215 CharSequence mBreadCrumbTitleText;
216 int mBreadCrumbShortTitleRes;
217 CharSequence mBreadCrumbShortTitleText;
218
George Mountd4c3c912014-06-09 12:31:34 -0700219 ArrayList<String> mSharedElementSourceNames;
220 ArrayList<String> mSharedElementTargetNames;
221
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700222 @Override
223 public String toString() {
224 StringBuilder sb = new StringBuilder(128);
225 sb.append("BackStackEntry{");
226 sb.append(Integer.toHexString(System.identityHashCode(this)));
227 if (mIndex >= 0) {
228 sb.append(" #");
229 sb.append(mIndex);
230 }
231 if (mName != null) {
232 sb.append(" ");
233 sb.append(mName);
234 }
235 sb.append("}");
236 return sb.toString();
237 }
238
Dianne Hackborn30d71892010-12-11 10:37:55 -0800239 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700240 dump(prefix, writer, true);
241 }
242
243 void dump(String prefix, PrintWriter writer, boolean full) {
244 if (full) {
George Mountd4c3c912014-06-09 12:31:34 -0700245 writer.print(prefix);
246 writer.print("mName=");
247 writer.print(mName);
248 writer.print(" mIndex=");
249 writer.print(mIndex);
250 writer.print(" mCommitted=");
251 writer.println(mCommitted);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700252 if (mTransition != FragmentTransaction.TRANSIT_NONE) {
George Mountd4c3c912014-06-09 12:31:34 -0700253 writer.print(prefix);
254 writer.print("mTransition=#");
255 writer.print(Integer.toHexString(mTransition));
256 writer.print(" mTransitionStyle=#");
257 writer.println(Integer.toHexString(mTransitionStyle));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700258 }
George Mountd4c3c912014-06-09 12:31:34 -0700259 if (mEnterAnim != 0 || mExitAnim != 0) {
260 writer.print(prefix);
261 writer.print("mEnterAnim=#");
262 writer.print(Integer.toHexString(mEnterAnim));
263 writer.print(" mExitAnim=#");
264 writer.println(Integer.toHexString(mExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700265 }
George Mountd4c3c912014-06-09 12:31:34 -0700266 if (mPopEnterAnim != 0 || mPopExitAnim != 0) {
267 writer.print(prefix);
268 writer.print("mPopEnterAnim=#");
269 writer.print(Integer.toHexString(mPopEnterAnim));
270 writer.print(" mPopExitAnim=#");
271 writer.println(Integer.toHexString(mPopExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700272 }
273 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700274 writer.print(prefix);
275 writer.print("mBreadCrumbTitleRes=#");
276 writer.print(Integer.toHexString(mBreadCrumbTitleRes));
277 writer.print(" mBreadCrumbTitleText=");
278 writer.println(mBreadCrumbTitleText);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700279 }
280 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
George Mountd4c3c912014-06-09 12:31:34 -0700281 writer.print(prefix);
282 writer.print("mBreadCrumbShortTitleRes=#");
283 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
284 writer.print(" mBreadCrumbShortTitleText=");
285 writer.println(mBreadCrumbShortTitleText);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700286 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800287 }
288
George Mountf2045f82016-06-27 13:15:01 -0700289 if (!mOps.isEmpty()) {
George Mountd4c3c912014-06-09 12:31:34 -0700290 writer.print(prefix);
291 writer.println("Operations:");
Dianne Hackborn30d71892010-12-11 10:37:55 -0800292 String innerPrefix = prefix + " ";
George Mountf2045f82016-06-27 13:15:01 -0700293 final int numOps = mOps.size();
294 for (int opNum = 0; opNum < numOps; opNum++) {
295 final Op op = mOps.get(opNum);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700296 String cmdStr;
297 switch (op.cmd) {
George Mountd4c3c912014-06-09 12:31:34 -0700298 case OP_NULL:
299 cmdStr = "NULL";
300 break;
301 case OP_ADD:
302 cmdStr = "ADD";
303 break;
304 case OP_REPLACE:
305 cmdStr = "REPLACE";
306 break;
307 case OP_REMOVE:
308 cmdStr = "REMOVE";
309 break;
310 case OP_HIDE:
311 cmdStr = "HIDE";
312 break;
313 case OP_SHOW:
314 cmdStr = "SHOW";
315 break;
316 case OP_DETACH:
317 cmdStr = "DETACH";
318 break;
319 case OP_ATTACH:
320 cmdStr = "ATTACH";
321 break;
322 default:
323 cmdStr = "cmd=" + op.cmd;
324 break;
Dianne Hackborn30d71892010-12-11 10:37:55 -0800325 }
George Mountd4c3c912014-06-09 12:31:34 -0700326 writer.print(prefix);
327 writer.print(" Op #");
George Mountf2045f82016-06-27 13:15:01 -0700328 writer.print(opNum);
George Mountd4c3c912014-06-09 12:31:34 -0700329 writer.print(": ");
330 writer.print(cmdStr);
331 writer.print(" ");
332 writer.println(op.fragment);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700333 if (full) {
334 if (op.enterAnim != 0 || op.exitAnim != 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700335 writer.print(innerPrefix);
336 writer.print("enterAnim=#");
337 writer.print(Integer.toHexString(op.enterAnim));
338 writer.print(" exitAnim=#");
339 writer.println(Integer.toHexString(op.exitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700340 }
341 if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
George Mountd4c3c912014-06-09 12:31:34 -0700342 writer.print(innerPrefix);
343 writer.print("popEnterAnim=#");
344 writer.print(Integer.toHexString(op.popEnterAnim));
345 writer.print(" popExitAnim=#");
346 writer.println(Integer.toHexString(op.popExitAnim));
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700347 }
Chet Haasebc377842011-03-22 11:35:22 -0700348 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800349 }
350 }
351 }
352
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700353 public BackStackRecord(FragmentManagerImpl manager) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700354 mManager = manager;
355 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700356
357 public int getId() {
358 return mIndex;
359 }
360
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800361 public int getBreadCrumbTitleRes() {
362 return mBreadCrumbTitleRes;
363 }
364
365 public int getBreadCrumbShortTitleRes() {
366 return mBreadCrumbShortTitleRes;
367 }
368
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700369 public CharSequence getBreadCrumbTitle() {
370 if (mBreadCrumbTitleRes != 0) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700371 return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700372 }
373 return mBreadCrumbTitleText;
374 }
375
376 public CharSequence getBreadCrumbShortTitle() {
377 if (mBreadCrumbShortTitleRes != 0) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700378 return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700379 }
380 return mBreadCrumbShortTitleText;
381 }
382
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700383 void addOp(Op op) {
George Mountf2045f82016-06-27 13:15:01 -0700384 mOps.add(op);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700385 op.enterAnim = mEnterAnim;
386 op.exitAnim = mExitAnim;
Chet Haasebc377842011-03-22 11:35:22 -0700387 op.popEnterAnim = mPopEnterAnim;
388 op.popExitAnim = mPopExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700389 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700390
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700391 public FragmentTransaction add(Fragment fragment, String tag) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700392 doAddOp(0, fragment, tag, OP_ADD);
393 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700394 }
395
396 public FragmentTransaction add(int containerViewId, Fragment fragment) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700397 doAddOp(containerViewId, fragment, null, OP_ADD);
398 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700399 }
400
401 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700402 doAddOp(containerViewId, fragment, tag, OP_ADD);
403 return this;
404 }
405
406 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
Clara Bayarricd916f22016-06-13 13:14:51 +0100407 if (mManager.mHost.getContext() != null) {
408 final int targetSdkVersion =
409 mManager.mHost.getContext().getApplicationInfo().targetSdkVersion;
410 final Class fragmentClass = fragment.getClass();
411 final int modifiers = fragmentClass.getModifiers();
412 // TODO: make the check N_MR1 or O
413 if (targetSdkVersion > Build.VERSION_CODES.N && (fragmentClass.isAnonymousClass()
414 || !Modifier.isPublic(modifiers)
415 || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers)))) {
Clara Bayarrieb7b7ec2016-06-23 11:08:55 +0100416 throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
417 + " must be a public static class to be properly recreated from"
418 + " instance state.");
Clara Bayarricd916f22016-06-13 13:14:51 +0100419 }
420 }
Dianne Hackborn3e449ce2010-09-11 20:52:31 -0700421 fragment.mFragmentManager = mManager;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700422
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700423 if (tag != null) {
424 if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
425 throw new IllegalStateException("Can't change tag of fragment "
426 + fragment + ": was " + fragment.mTag
427 + " now " + tag);
428 }
429 fragment.mTag = tag;
430 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700431
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700432 if (containerViewId != 0) {
Adam Powelle01f5952016-02-23 15:25:42 -0800433 if (containerViewId == View.NO_ID) {
434 throw new IllegalArgumentException("Can't add fragment "
435 + fragment + " with tag " + tag + " to container view with no id");
436 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700437 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
438 throw new IllegalStateException("Can't change container ID of fragment "
439 + fragment + ": was " + fragment.mFragmentId
440 + " now " + containerViewId);
441 }
442 fragment.mContainerId = fragment.mFragmentId = containerViewId;
443 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700444
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700445 Op op = new Op();
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700446 op.cmd = opcmd;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700447 op.fragment = fragment;
448 addOp(op);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700449 }
450
451 public FragmentTransaction replace(int containerViewId, Fragment fragment) {
452 return replace(containerViewId, fragment, null);
453 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700454
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700455 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
456 if (containerViewId == 0) {
457 throw new IllegalArgumentException("Must use non-zero containerViewId");
458 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700459
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -0700460 doAddOp(containerViewId, fragment, tag, OP_REPLACE);
461 return this;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700462 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700463
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700464 public FragmentTransaction remove(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700465 Op op = new Op();
466 op.cmd = OP_REMOVE;
467 op.fragment = fragment;
468 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700469
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700470 return this;
471 }
472
473 public FragmentTransaction hide(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700474 Op op = new Op();
475 op.cmd = OP_HIDE;
476 op.fragment = fragment;
477 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700478
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700479 return this;
480 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700481
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700482 public FragmentTransaction show(Fragment fragment) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700483 Op op = new Op();
484 op.cmd = OP_SHOW;
485 op.fragment = fragment;
486 addOp(op);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700487
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700488 return this;
489 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700490
Dianne Hackborn47c41562011-04-15 19:00:20 -0700491 public FragmentTransaction detach(Fragment fragment) {
Dianne Hackborn47c41562011-04-15 19:00:20 -0700492 Op op = new Op();
493 op.cmd = OP_DETACH;
494 op.fragment = fragment;
495 addOp(op);
496
497 return this;
498 }
499
500 public FragmentTransaction attach(Fragment fragment) {
Dianne Hackborn47c41562011-04-15 19:00:20 -0700501 Op op = new Op();
502 op.cmd = OP_ATTACH;
503 op.fragment = fragment;
504 addOp(op);
505
506 return this;
507 }
508
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700509 public FragmentTransaction setCustomAnimations(int enter, int exit) {
Chet Haasebc377842011-03-22 11:35:22 -0700510 return setCustomAnimations(enter, exit, 0, 0);
511 }
512
513 public FragmentTransaction setCustomAnimations(int enter, int exit,
514 int popEnter, int popExit) {
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700515 mEnterAnim = enter;
516 mExitAnim = exit;
Chet Haasebc377842011-03-22 11:35:22 -0700517 mPopEnterAnim = popEnter;
518 mPopExitAnim = popExit;
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 setTransition(int transition) {
523 mTransition = transition;
524 return this;
525 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700526
George Mountd4c3c912014-06-09 12:31:34 -0700527 @Override
George Mount448bace2014-08-18 16:27:43 -0700528 public FragmentTransaction addSharedElement(View sharedElement, String name) {
529 String transitionName = sharedElement.getTransitionName();
530 if (transitionName == null) {
531 throw new IllegalArgumentException("Unique transitionNames are required for all" +
532 " sharedElements");
533 }
534 if (mSharedElementSourceNames == null) {
535 mSharedElementSourceNames = new ArrayList<String>();
536 mSharedElementTargetNames = new ArrayList<String>();
537 }
538 mSharedElementSourceNames.add(transitionName);
539 mSharedElementTargetNames.add(name);
540 return this;
541 }
542
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700543 public FragmentTransaction setTransitionStyle(int styleRes) {
544 mTransitionStyle = styleRes;
545 return this;
546 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700547
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700548 public FragmentTransaction addToBackStack(String name) {
Adam Powell0c24a552010-11-03 16:44:11 -0700549 if (!mAllowAddToBackStack) {
550 throw new IllegalStateException(
551 "This FragmentTransaction is not allowed to be added to the back stack.");
552 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700553 mAddToBackStack = true;
554 mName = name;
555 return this;
556 }
557
Adam Powell0c24a552010-11-03 16:44:11 -0700558 public boolean isAddToBackStackAllowed() {
559 return mAllowAddToBackStack;
560 }
561
562 public FragmentTransaction disallowAddToBackStack() {
563 if (mAddToBackStack) {
564 throw new IllegalStateException(
565 "This transaction is already being added to the back stack");
566 }
567 mAllowAddToBackStack = false;
568 return this;
569 }
570
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700571 public FragmentTransaction setBreadCrumbTitle(int res) {
572 mBreadCrumbTitleRes = res;
573 mBreadCrumbTitleText = null;
574 return this;
575 }
576
577 public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
578 mBreadCrumbTitleRes = 0;
579 mBreadCrumbTitleText = text;
580 return this;
581 }
582
583 public FragmentTransaction setBreadCrumbShortTitle(int res) {
584 mBreadCrumbShortTitleRes = res;
585 mBreadCrumbShortTitleText = null;
586 return this;
587 }
588
589 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
590 mBreadCrumbShortTitleRes = 0;
591 mBreadCrumbShortTitleText = text;
592 return this;
593 }
594
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700595 void bumpBackStackNesting(int amt) {
596 if (!mAddToBackStack) {
597 return;
598 }
George Mountd4c3c912014-06-09 12:31:34 -0700599 if (FragmentManagerImpl.DEBUG) {
600 Log.v(TAG, "Bump nesting in " + this
601 + " by " + amt);
602 }
George Mountf2045f82016-06-27 13:15:01 -0700603 final int numOps = mOps.size();
604 for (int opNum = 0; opNum < numOps; opNum++) {
605 final Op op = mOps.get(opNum);
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700606 if (op.fragment != null) {
607 op.fragment.mBackStackNesting += amt;
George Mountd4c3c912014-06-09 12:31:34 -0700608 if (FragmentManagerImpl.DEBUG) {
609 Log.v(TAG, "Bump nesting of "
610 + op.fragment + " to " + op.fragment.mBackStackNesting);
611 }
Dianne Hackbornee76efb2012-06-05 10:27:40 -0700612 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700613 }
614 }
615
Dianne Hackborndd913a52010-07-22 12:17:04 -0700616 public int commit() {
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700617 return commitInternal(false);
618 }
619
620 public int commitAllowingStateLoss() {
621 return commitInternal(true);
622 }
George Mountd4c3c912014-06-09 12:31:34 -0700623
Adam Powell8585ed62016-02-04 15:38:20 -0800624 @Override
625 public void commitNow() {
626 disallowAddToBackStack();
627 mManager.execSingleAction(this, false);
628 }
629
630 @Override
631 public void commitNowAllowingStateLoss() {
632 disallowAddToBackStack();
633 mManager.execSingleAction(this, true);
634 }
635
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700636 int commitInternal(boolean allowStateLoss) {
George Mountd4c3c912014-06-09 12:31:34 -0700637 if (mCommitted) {
638 throw new IllegalStateException("commit already called");
639 }
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700640 if (FragmentManagerImpl.DEBUG) {
641 Log.v(TAG, "Commit: " + this);
642 LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700643 PrintWriter pw = new FastPrintWriter(logw, false, 1024);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700644 dump(" ", null, pw, null);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700645 pw.flush();
Dianne Hackbornf43a33c2012-09-27 00:48:11 -0700646 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700647 mCommitted = true;
Dianne Hackborndd913a52010-07-22 12:17:04 -0700648 if (mAddToBackStack) {
649 mIndex = mManager.allocBackStackIndex(this);
650 } else {
651 mIndex = -1;
652 }
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700653 mManager.enqueueAction(this, allowStateLoss);
Dianne Hackborndd913a52010-07-22 12:17:04 -0700654 return mIndex;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700655 }
George Mountd4c3c912014-06-09 12:31:34 -0700656
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700657 public void run() {
George Mountd4c3c912014-06-09 12:31:34 -0700658 if (FragmentManagerImpl.DEBUG) {
659 Log.v(TAG, "Run: " + this);
660 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700661
Dianne Hackborndd913a52010-07-22 12:17:04 -0700662 if (mAddToBackStack) {
663 if (mIndex < 0) {
664 throw new IllegalStateException("addToBackStack() called after commit()");
665 }
666 }
667
George Mountf2045f82016-06-27 13:15:01 -0700668 expandReplaceOps();
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700669 bumpBackStackNesting(1);
670
George Mounta7245b42016-02-10 17:03:32 -0800671 if (mManager.mCurState >= Fragment.CREATED) {
George Mount7fe44142016-08-22 15:49:00 -0700672 SparseArray<FragmentContainerTransition> transitioningFragments = new SparseArray<>();
673 calculateFragments(transitioningFragments);
674 beginTransition(transitioningFragments);
George Mounta7245b42016-02-10 17:03:32 -0800675 }
George Mountd4c3c912014-06-09 12:31:34 -0700676
George Mountf2045f82016-06-27 13:15:01 -0700677 final int numOps = mOps.size();
678 for (int opNum = 0; opNum < numOps; opNum++) {
679 final Op op = mOps.get(opNum);
680 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700681 switch (op.cmd) {
George Mountf2045f82016-06-27 13:15:01 -0700682 case OP_ADD:
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700683 f.mNextAnim = op.enterAnim;
684 mManager.addFragment(f, false);
George Mountf2045f82016-06-27 13:15:01 -0700685 break;
686 case OP_REMOVE:
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700687 f.mNextAnim = op.exitAnim;
688 mManager.removeFragment(f, mTransition, mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -0700689 break;
690 case OP_HIDE:
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700691 f.mNextAnim = op.exitAnim;
692 mManager.hideFragment(f, mTransition, mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -0700693 break;
694 case OP_SHOW:
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700695 f.mNextAnim = op.enterAnim;
696 mManager.showFragment(f, mTransition, mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -0700697 break;
698 case OP_DETACH:
Dianne Hackborn47c41562011-04-15 19:00:20 -0700699 f.mNextAnim = op.exitAnim;
700 mManager.detachFragment(f, mTransition, mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -0700701 break;
702 case OP_ATTACH:
Dianne Hackborn47c41562011-04-15 19:00:20 -0700703 f.mNextAnim = op.enterAnim;
704 mManager.attachFragment(f, mTransition, mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -0700705 break;
706 default:
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700707 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700708 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700709 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700710
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700711 mManager.moveToState(mManager.mCurState, mTransition,
712 mTransitionStyle, true);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700713
Dianne Hackborn5ae74d62010-05-19 19:14:57 -0700714 if (mAddToBackStack) {
715 mManager.addBackStackState(this);
716 }
717 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700718
George Mountf2045f82016-06-27 13:15:01 -0700719 private void expandReplaceOps() {
720 final int numOps = mOps.size();
721
722 boolean hasReplace = false;
723 // Before we do anything, check to see if any replace operations exist:
724 for (int opNum = 0; opNum < numOps; opNum++) {
725 final Op op = mOps.get(opNum);
726 if (op.cmd == OP_REPLACE) {
727 hasReplace = true;
728 break;
729 }
730 }
731
732 if (!hasReplace) {
733 return; // nothing to expand
734 }
735
736 ArrayList<Fragment> added = (mManager.mAdded == null) ? new ArrayList<Fragment>() :
737 new ArrayList<>(mManager.mAdded);
738 for (int opNum = 0; opNum < mOps.size(); opNum++) {
739 final Op op = mOps.get(opNum);
740 switch (op.cmd) {
741 case OP_ADD:
742 case OP_ATTACH:
743 added.add(op.fragment);
744 break;
745 case OP_REMOVE:
746 case OP_DETACH:
747 added.remove(op.fragment);
748 break;
749 case OP_REPLACE: {
750 Fragment f = op.fragment;
751 int containerId = f.mContainerId;
752 boolean alreadyAdded = false;
753 for (int i = added.size() - 1; i >= 0; i--) {
754 Fragment old = added.get(i);
755 if (old.mContainerId == containerId) {
756 if (old == f) {
757 alreadyAdded = true;
758 } else {
759 Op removeOp = new Op();
760 removeOp.cmd = OP_REMOVE;
761 removeOp.fragment = old;
762 removeOp.enterAnim = op.enterAnim;
763 removeOp.popEnterAnim = op.popEnterAnim;
764 removeOp.exitAnim = op.exitAnim;
765 removeOp.popExitAnim = op.popExitAnim;
766 mOps.add(opNum, removeOp);
767 added.remove(old);
768 opNum++;
769 }
770 }
771 }
772 if (alreadyAdded) {
773 mOps.remove(opNum);
774 opNum--;
775 } else {
776 op.cmd = OP_ADD;
777 added.add(f);
778 }
779 }
780 break;
781 }
782 }
783 }
784
George Mount7fe44142016-08-22 15:49:00 -0700785 private static void setFirstOut(SparseArray<FragmentContainerTransition> transitioningFragments,
786 Fragment fragment, boolean isPop) {
George Mountc03da0e2014-08-22 17:04:02 -0700787 if (fragment != null) {
788 int containerId = fragment.mContainerId;
George Mount233c97c2015-12-04 18:00:55 +0000789 if (containerId != 0 && !fragment.isHidden()) {
George Mount7fe44142016-08-22 15:49:00 -0700790 FragmentContainerTransition fragments = transitioningFragments.get(containerId);
791 if (fragment.isAdded() && fragment.getView() != null && (fragments == null ||
792 fragments.firstOut == null)) {
793 if (fragments == null) {
794 fragments = new FragmentContainerTransition();
795 transitioningFragments.put(containerId, fragments);
796 }
797 fragments.firstOut = fragment;
798 fragments.firstOutIsPop = isPop;
George Mount233c97c2015-12-04 18:00:55 +0000799 }
George Mount7fe44142016-08-22 15:49:00 -0700800 if (fragments != null && fragments.lastIn == fragment) {
801 fragments.lastIn = null;
George Mount233c97c2015-12-04 18:00:55 +0000802 }
George Mountc03da0e2014-08-22 17:04:02 -0700803 }
George Mountd4c3c912014-06-09 12:31:34 -0700804 }
George Mountc03da0e2014-08-22 17:04:02 -0700805 }
George Mountd4c3c912014-06-09 12:31:34 -0700806
George Mount7fe44142016-08-22 15:49:00 -0700807 private void setLastIn(SparseArray<FragmentContainerTransition> transitioningFragments,
808 Fragment fragment, boolean isPop) {
George Mountc03da0e2014-08-22 17:04:02 -0700809 if (fragment != null) {
810 int containerId = fragment.mContainerId;
811 if (containerId != 0) {
George Mount7fe44142016-08-22 15:49:00 -0700812 FragmentContainerTransition fragments = transitioningFragments.get(containerId);
George Mount233c97c2015-12-04 18:00:55 +0000813 if (!fragment.isAdded()) {
George Mount7fe44142016-08-22 15:49:00 -0700814 if (fragments == null) {
815 fragments = new FragmentContainerTransition();
816 transitioningFragments.put(containerId, fragments);
817 }
818 fragments.lastIn = fragment;
819 fragments.lastInIsPop = isPop;
George Mount233c97c2015-12-04 18:00:55 +0000820 }
George Mount7fe44142016-08-22 15:49:00 -0700821 if (fragments != null && fragments.firstOut == fragment) {
822 fragments.firstOut = null;
George Mount233c97c2015-12-04 18:00:55 +0000823 }
George Mountc03da0e2014-08-22 17:04:02 -0700824 }
George Mounta7245b42016-02-10 17:03:32 -0800825 /**
826 * Ensure that fragments that are entering are at least at the CREATED state
827 * so that they may load Transitions using TransitionInflater.
828 */
George Mount78d38fc2016-07-22 15:25:27 -0700829 if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED &&
830 mManager.mHost.getContext().getApplicationInfo().targetSdkVersion >=
831 Build.VERSION_CODES.N) {
George Mounta7245b42016-02-10 17:03:32 -0800832 mManager.makeActive(fragment);
833 mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
834 }
George Mountc03da0e2014-08-22 17:04:02 -0700835 }
836 }
837
838 /**
839 * Finds the first removed fragment and last added fragments when going forward.
840 * If none of the fragments have transitions, then both lists will be empty.
841 *
George Mount7fe44142016-08-22 15:49:00 -0700842 * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
843 * and last fragments to be added. This will be modified by
844 * this method.
George Mountc03da0e2014-08-22 17:04:02 -0700845 */
George Mount7fe44142016-08-22 15:49:00 -0700846 private void calculateFragments(
847 SparseArray<FragmentContainerTransition> transitioningFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700848 if (!mManager.mContainer.onHasView()) {
George Mount0b26e4d2014-09-17 16:36:42 -0700849 return; // nothing to see, so no transitions
850 }
George Mountf2045f82016-06-27 13:15:01 -0700851 final int numOps = mOps.size();
852 for (int opNum = 0; opNum < numOps; opNum++) {
853 final Op op = mOps.get(opNum);
George Mountc03da0e2014-08-22 17:04:02 -0700854 switch (op.cmd) {
855 case OP_ADD:
George Mount7fe44142016-08-22 15:49:00 -0700856 case OP_SHOW:
857 case OP_ATTACH:
858 setLastIn(transitioningFragments, op.fragment, false);
George Mountc03da0e2014-08-22 17:04:02 -0700859 break;
George Mountc03da0e2014-08-22 17:04:02 -0700860 case OP_REMOVE:
George Mountc03da0e2014-08-22 17:04:02 -0700861 case OP_HIDE:
George Mountc03da0e2014-08-22 17:04:02 -0700862 case OP_DETACH:
George Mount7fe44142016-08-22 15:49:00 -0700863 setFirstOut(transitioningFragments, op.fragment, false);
George Mountc03da0e2014-08-22 17:04:02 -0700864 break;
865 }
George Mountc03da0e2014-08-22 17:04:02 -0700866 }
George Mountc03da0e2014-08-22 17:04:02 -0700867 }
868
869 /**
870 * Finds the first removed fragment and last added fragments when popping the back stack.
871 * If none of the fragments have transitions, then both lists will be empty.
872 *
George Mount7fe44142016-08-22 15:49:00 -0700873 * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
874 * and last fragments to be added. This will be modified by
875 * this method.
George Mountc03da0e2014-08-22 17:04:02 -0700876 */
George Mount7fe44142016-08-22 15:49:00 -0700877 public void calculateBackFragments(
878 SparseArray<FragmentContainerTransition> transitioningFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700879 if (!mManager.mContainer.onHasView()) {
George Mount0b26e4d2014-09-17 16:36:42 -0700880 return; // nothing to see, so no transitions
881 }
George Mountf2045f82016-06-27 13:15:01 -0700882 final int numOps = mOps.size();
883 for (int opNum = numOps - 1; opNum >= 0; opNum--) {
884 final Op op = mOps.get(opNum);
George Mountc03da0e2014-08-22 17:04:02 -0700885 switch (op.cmd) {
886 case OP_ADD:
George Mount7fe44142016-08-22 15:49:00 -0700887 case OP_SHOW:
888 case OP_ATTACH:
889 setFirstOut(transitioningFragments, op.fragment, true);
George Mountc03da0e2014-08-22 17:04:02 -0700890 break;
George Mountc03da0e2014-08-22 17:04:02 -0700891 case OP_REMOVE:
George Mountc03da0e2014-08-22 17:04:02 -0700892 case OP_HIDE:
George Mountc03da0e2014-08-22 17:04:02 -0700893 case OP_DETACH:
George Mount7fe44142016-08-22 15:49:00 -0700894 setLastIn(transitioningFragments, op.fragment, true);
George Mountc03da0e2014-08-22 17:04:02 -0700895 break;
896 }
George Mountc03da0e2014-08-22 17:04:02 -0700897 }
George Mountc03da0e2014-08-22 17:04:02 -0700898 }
899
900 /**
901 * When custom fragment transitions are used, this sets up the state for each transition
902 * and begins the transition. A different transition is started for each fragment container
903 * and consists of up to 3 different transitions: the exit transition, a shared element
904 * transition and an enter transition.
905 *
906 * <p>The exit transition operates against the leaf nodes of the first fragment
907 * with a view that was removed. If no such fragment was removed, then no exit
908 * transition is executed. The exit transition comes from the outgoing fragment.</p>
909 *
910 * <p>The enter transition operates against the last fragment that was added. If
911 * that fragment does not have a view or no fragment was added, then no enter
912 * transition is executed. The enter transition comes from the incoming fragment.</p>
913 *
914 * <p>The shared element transition operates against all views and comes either
915 * from the outgoing fragment or the incoming fragment, depending on whether this
916 * is going forward or popping the back stack. When going forward, the incoming
917 * fragment's enter shared element transition is used, but when going back, the
918 * outgoing fragment's return shared element transition is used. Shared element
919 * transitions only operate if there is both an incoming and outgoing fragment.</p>
920 *
George Mount7fe44142016-08-22 15:49:00 -0700921 * @param containers The first in and last out fragments that are transitioning.
George Mountc03da0e2014-08-22 17:04:02 -0700922 * @return The TransitionState used to complete the operation of the transition
George Mountecfd0072014-09-07 10:06:25 -0700923 * in {@link #setNameOverrides(android.app.BackStackRecord.TransitionState, java.util.ArrayList,
924 * java.util.ArrayList)}.
George Mountc03da0e2014-08-22 17:04:02 -0700925 */
George Mount7fe44142016-08-22 15:49:00 -0700926 private TransitionState beginTransition(SparseArray<FragmentContainerTransition> containers) {
George Mountc03da0e2014-08-22 17:04:02 -0700927 TransitionState state = new TransitionState();
928
George Mountd4c3c912014-06-09 12:31:34 -0700929 // Adding a non-existent target view makes sure that the transitions don't target
930 // any views by default. They'll only target the views we tell add. If we don't
931 // add any, then no views will be targeted.
Todd Kennedya5fc6f02015-04-14 18:22:54 -0700932 state.nonExistentView = new View(mManager.mHost.getContext());
George Mountd4c3c912014-06-09 12:31:34 -0700933
George Mount7fe44142016-08-22 15:49:00 -0700934 final int numContainers = containers.size();
935 for (int i = 0; i < numContainers; i++) {
936 int containerId = containers.keyAt(i);
937 FragmentContainerTransition containerTransition = containers.valueAt(i);
938 configureTransitions(containerId, state, containerTransition);
George Mountc03da0e2014-08-22 17:04:02 -0700939 }
George Mountd4c3c912014-06-09 12:31:34 -0700940 return state;
941 }
942
George Mountecfd0072014-09-07 10:06:25 -0700943 private static Transition cloneTransition(Transition transition) {
944 if (transition != null) {
945 transition = transition.clone();
946 }
947 return transition;
948 }
949
George Mountc03da0e2014-08-22 17:04:02 -0700950 private static Transition getEnterTransition(Fragment inFragment, boolean isBack) {
951 if (inFragment == null) {
952 return null;
953 }
George Mountecfd0072014-09-07 10:06:25 -0700954 return cloneTransition(isBack ? inFragment.getReenterTransition() :
955 inFragment.getEnterTransition());
George Mountc03da0e2014-08-22 17:04:02 -0700956 }
George Mountd4c3c912014-06-09 12:31:34 -0700957
George Mountc03da0e2014-08-22 17:04:02 -0700958 private static Transition getExitTransition(Fragment outFragment, boolean isBack) {
959 if (outFragment == null) {
960 return null;
961 }
George Mountecfd0072014-09-07 10:06:25 -0700962 return cloneTransition(isBack ? outFragment.getReturnTransition() :
963 outFragment.getExitTransition());
George Mountc03da0e2014-08-22 17:04:02 -0700964 }
965
George Mountc855f7d2015-06-10 15:00:17 -0700966 private static TransitionSet getSharedElementTransition(Fragment inFragment,
967 Fragment outFragment, boolean isBack) {
George Mountc03da0e2014-08-22 17:04:02 -0700968 if (inFragment == null || outFragment == null) {
969 return null;
970 }
George Mountc855f7d2015-06-10 15:00:17 -0700971 Transition transition = cloneTransition(isBack
972 ? outFragment.getSharedElementReturnTransition()
973 : inFragment.getSharedElementEnterTransition());
974 if (transition == null) {
975 return null;
976 }
977 TransitionSet transitionSet = new TransitionSet();
978 transitionSet.addTransition(transition);
979 return transitionSet;
George Mountc03da0e2014-08-22 17:04:02 -0700980 }
981
George Mountecfd0072014-09-07 10:06:25 -0700982 private static ArrayList<View> captureExitingViews(Transition exitTransition,
George Mount7ce5d752014-10-30 16:09:22 -0700983 Fragment outFragment, ArrayMap<String, View> namedViews, View nonExistentView) {
George Mountecfd0072014-09-07 10:06:25 -0700984 ArrayList<View> viewList = null;
George Mountc03da0e2014-08-22 17:04:02 -0700985 if (exitTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -0700986 viewList = new ArrayList<View>();
George Mountc03da0e2014-08-22 17:04:02 -0700987 View root = outFragment.getView();
George Mountc03da0e2014-08-22 17:04:02 -0700988 root.captureTransitioningViews(viewList);
George Mount27bc8aa2014-09-14 16:48:21 -0700989 if (namedViews != null) {
990 viewList.removeAll(namedViews.values());
991 }
George Mount7ce5d752014-10-30 16:09:22 -0700992 if (!viewList.isEmpty()) {
993 viewList.add(nonExistentView);
994 addTargets(exitTransition, viewList);
995 }
George Mountc03da0e2014-08-22 17:04:02 -0700996 }
George Mountecfd0072014-09-07 10:06:25 -0700997 return viewList;
George Mountc03da0e2014-08-22 17:04:02 -0700998 }
999
1000 private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
George Mountecfd0072014-09-07 10:06:25 -07001001 boolean isBack) {
1002 ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001003 if (mSharedElementSourceNames != null) {
1004 outFragment.getView().findNamedViews(namedViews);
1005 if (isBack) {
1006 namedViews.retainAll(mSharedElementTargetNames);
1007 } else {
1008 namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
George Mountecfd0072014-09-07 10:06:25 -07001009 namedViews);
George Mountd4c3c912014-06-09 12:31:34 -07001010 }
1011 }
1012
George Mountc03da0e2014-08-22 17:04:02 -07001013 if (isBack) {
George Mount65580562014-08-29 08:15:48 -07001014 outFragment.mEnterTransitionCallback.onMapSharedElements(
George Mountc03da0e2014-08-22 17:04:02 -07001015 mSharedElementTargetNames, namedViews);
1016 setBackNameOverrides(state, namedViews, false);
George Mountd4c3c912014-06-09 12:31:34 -07001017 } else {
George Mount65580562014-08-29 08:15:48 -07001018 outFragment.mExitTransitionCallback.onMapSharedElements(
George Mountc03da0e2014-08-22 17:04:02 -07001019 mSharedElementTargetNames, namedViews);
1020 setNameOverrides(state, namedViews, false);
1021 }
1022
1023 return namedViews;
1024 }
1025
1026 /**
1027 * Prepares the enter transition by adding a non-existent view to the transition's target list
1028 * and setting it epicenter callback. By adding a non-existent view to the target list,
1029 * we can prevent any view from being targeted at the beginning of the transition.
1030 * We will add to the views before the end state of the transition is captured so that the
1031 * views will appear. At the start of the transition, we clear the list of targets so that
1032 * we can restore the state of the transition and use it again.
George Mountecfd0072014-09-07 10:06:25 -07001033 *
1034 * <p>The shared element transition maps its shared elements immediately prior to
1035 * capturing the final state of the Transition.</p>
George Mountc03da0e2014-08-22 17:04:02 -07001036 */
George Mountecfd0072014-09-07 10:06:25 -07001037 private ArrayList<View> addTransitionTargets(final TransitionState state,
George Mountc855f7d2015-06-10 15:00:17 -07001038 final Transition enterTransition, final TransitionSet sharedElementTransition,
George Mount61f83312015-09-09 16:10:29 -07001039 final Transition exitTransition, final Transition overallTransition,
1040 final View container, final Fragment inFragment, final Fragment outFragment,
George Mount2deeaa32014-09-19 10:02:02 -07001041 final ArrayList<View> hiddenFragmentViews, final boolean isBack,
1042 final ArrayList<View> sharedElementTargets) {
George Mountecfd0072014-09-07 10:06:25 -07001043 if (enterTransition == null && sharedElementTransition == null &&
1044 overallTransition == null) {
1045 return null;
1046 }
1047 final ArrayList<View> enteringViews = new ArrayList<View>();
1048 container.getViewTreeObserver().addOnPreDrawListener(
1049 new ViewTreeObserver.OnPreDrawListener() {
1050 @Override
1051 public boolean onPreDraw() {
1052 container.getViewTreeObserver().removeOnPreDrawListener(this);
1053
1054 // Don't include any newly-hidden fragments in the transition.
George Mount13d70322015-07-21 14:30:24 -07001055 if (inFragment != null) {
1056 excludeHiddenFragments(hiddenFragmentViews, inFragment.mContainerId,
1057 overallTransition);
1058 }
George Mountecfd0072014-09-07 10:06:25 -07001059
George Mount27bc8aa2014-09-14 16:48:21 -07001060 ArrayMap<String, View> namedViews = null;
George Mountecfd0072014-09-07 10:06:25 -07001061 if (sharedElementTransition != null) {
George Mount27bc8aa2014-09-14 16:48:21 -07001062 namedViews = mapSharedElementsIn(state, isBack, inFragment);
George Mount2deeaa32014-09-19 10:02:02 -07001063 removeTargets(sharedElementTransition, sharedElementTargets);
George Mount61f83312015-09-09 16:10:29 -07001064 // keep the nonExistentView as excluded so the list doesn't get emptied
1065 sharedElementTargets.remove(state.nonExistentView);
1066 excludeViews(exitTransition, sharedElementTransition,
1067 sharedElementTargets, false);
1068 excludeViews(enterTransition, sharedElementTransition,
1069 sharedElementTargets, false);
1070
George Mountc855f7d2015-06-10 15:00:17 -07001071 setSharedElementTargets(sharedElementTransition,
1072 state.nonExistentView, namedViews, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001073
1074 setEpicenterIn(namedViews, state);
1075
1076 callSharedElementEnd(state, inFragment, outFragment, isBack,
1077 namedViews);
1078 }
1079
1080 if (enterTransition != null) {
George Mountec3364c2015-06-03 15:47:44 -07001081 enterTransition.removeTarget(state.nonExistentView);
George Mountc03da0e2014-08-22 17:04:02 -07001082 View view = inFragment.getView();
1083 if (view != null) {
1084 view.captureTransitioningViews(enteringViews);
George Mount27bc8aa2014-09-14 16:48:21 -07001085 if (namedViews != null) {
1086 enteringViews.removeAll(namedViews.values());
1087 }
George Mount7ce5d752014-10-30 16:09:22 -07001088 enteringViews.add(state.nonExistentView);
1089 // We added this earlier to prevent any views being targeted.
George Mountecfd0072014-09-07 10:06:25 -07001090 addTargets(enterTransition, enteringViews);
George Mountc03da0e2014-08-22 17:04:02 -07001091 }
George Mountecfd0072014-09-07 10:06:25 -07001092 setSharedElementEpicenter(enterTransition, state);
George Mountc03da0e2014-08-22 17:04:02 -07001093 }
George Mount61f83312015-09-09 16:10:29 -07001094
1095 excludeViews(exitTransition, enterTransition, enteringViews, true);
1096 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
1097 true);
1098 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
1099 true);
George Mountecfd0072014-09-07 10:06:25 -07001100 return true;
1101 }
1102 });
1103 return enteringViews;
1104 }
1105
1106 private void callSharedElementEnd(TransitionState state, Fragment inFragment,
1107 Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) {
1108 SharedElementCallback sharedElementCallback = isBack ?
1109 outFragment.mEnterTransitionCallback :
1110 inFragment.mEnterTransitionCallback;
1111 ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
1112 ArrayList<View> views = new ArrayList<View>(namedViews.values());
1113 sharedElementCallback.onSharedElementEnd(names, views, null);
1114 }
1115
1116 private void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) {
1117 if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
1118 // now we know the epicenter of the entering transition.
1119 View epicenter = namedViews
1120 .get(mSharedElementTargetNames.get(0));
1121 if (epicenter != null) {
1122 state.enteringEpicenterView = epicenter;
1123 }
George Mountc03da0e2014-08-22 17:04:02 -07001124 }
1125 }
1126
George Mountecfd0072014-09-07 10:06:25 -07001127 private ArrayMap<String, View> mapSharedElementsIn(TransitionState state,
1128 boolean isBack, Fragment inFragment) {
1129 // Now map the shared elements in the incoming fragment
1130 ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack);
1131
1132 // remap shared elements and set the name mapping used
1133 // in the shared element transition.
1134 if (isBack) {
1135 inFragment.mExitTransitionCallback.onMapSharedElements(
1136 mSharedElementTargetNames, namedViews);
1137 setBackNameOverrides(state, namedViews, true);
1138 } else {
1139 inFragment.mEnterTransitionCallback.onMapSharedElements(
1140 mSharedElementTargetNames, namedViews);
1141 setNameOverrides(state, namedViews, true);
1142 }
1143 return namedViews;
1144 }
1145
George Mountc03da0e2014-08-22 17:04:02 -07001146 private static Transition mergeTransitions(Transition enterTransition,
1147 Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
1148 boolean isBack) {
1149 boolean overlap = true;
George Mountec3364c2015-06-03 15:47:44 -07001150 if (enterTransition != null && exitTransition != null && inFragment != null) {
George Mountc03da0e2014-08-22 17:04:02 -07001151 overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
1152 inFragment.getAllowEnterTransitionOverlap();
1153 }
1154
George Mount27bc8aa2014-09-14 16:48:21 -07001155 // Wrap the transitions. Explicit targets like in enter and exit will cause the
1156 // views to be targeted regardless of excluded views. If that happens, then the
1157 // excluded fragments views (hidden fragments) will still be in the transition.
1158
George Mountc03da0e2014-08-22 17:04:02 -07001159 Transition transition;
1160 if (overlap) {
George Mount27bc8aa2014-09-14 16:48:21 -07001161 // Regular transition -- do it all together
George Mount2deeaa32014-09-19 10:02:02 -07001162 TransitionSet transitionSet = new TransitionSet();
1163 if (enterTransition != null) {
1164 transitionSet.addTransition(enterTransition);
George Mount27bc8aa2014-09-14 16:48:21 -07001165 }
George Mount2deeaa32014-09-19 10:02:02 -07001166 if (exitTransition != null) {
1167 transitionSet.addTransition(exitTransition);
1168 }
1169 if (sharedElementTransition != null) {
1170 transitionSet.addTransition(sharedElementTransition);
1171 }
1172 transition = transitionSet;
George Mountc03da0e2014-08-22 17:04:02 -07001173 } else {
George Mount27bc8aa2014-09-14 16:48:21 -07001174 // First do exit, then enter, but allow shared element transition to happen
1175 // during both.
1176 Transition staggered = null;
1177 if (exitTransition != null && enterTransition != null) {
1178 staggered = new TransitionSet()
1179 .addTransition(exitTransition)
1180 .addTransition(enterTransition)
1181 .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
1182 } else if (exitTransition != null) {
1183 staggered = exitTransition;
1184 } else if (enterTransition != null) {
1185 staggered = enterTransition;
1186 }
1187 if (sharedElementTransition != null) {
1188 TransitionSet together = new TransitionSet();
1189 if (staggered != null) {
1190 together.addTransition(staggered);
1191 }
1192 together.addTransition(sharedElementTransition);
1193 transition = together;
1194 } else {
1195 transition = staggered;
1196 }
George Mountc03da0e2014-08-22 17:04:02 -07001197 }
1198 return transition;
1199 }
1200
1201 /**
1202 * Configures custom transitions for a specific fragment container.
1203 *
1204 * @param containerId The container ID of the fragments to configure the transition for.
George Mountecfd0072014-09-07 10:06:25 -07001205 * @param state The Transition State keeping track of the executing transitions.
George Mount7fe44142016-08-22 15:49:00 -07001206 * @param transitioningFragments The first out and last in fragments for the fragment container.
George Mountc03da0e2014-08-22 17:04:02 -07001207 */
George Mount7fe44142016-08-22 15:49:00 -07001208 private void configureTransitions(int containerId, TransitionState state,
1209 FragmentContainerTransition transitioningFragments) {
Todd Kennedya5fc6f02015-04-14 18:22:54 -07001210 ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
George Mountc03da0e2014-08-22 17:04:02 -07001211 if (sceneRoot != null) {
George Mount7fe44142016-08-22 15:49:00 -07001212 final Fragment inFragment = transitioningFragments.lastIn;
1213 final Fragment outFragment = transitioningFragments.firstOut;
George Mountc03da0e2014-08-22 17:04:02 -07001214
George Mount7fe44142016-08-22 15:49:00 -07001215 Transition enterTransition =
1216 getEnterTransition(inFragment, transitioningFragments.lastInIsPop);
1217 TransitionSet sharedElementTransition = getSharedElementTransition(inFragment,
1218 outFragment, transitioningFragments.lastInIsPop);
1219 Transition exitTransition =
1220 getExitTransition(outFragment, transitioningFragments.firstOutIsPop);
George Mountc03da0e2014-08-22 17:04:02 -07001221
George Mountecfd0072014-09-07 10:06:25 -07001222 if (enterTransition == null && sharedElementTransition == null &&
1223 exitTransition == null) {
1224 return; // no transitions!
1225 }
George Mount27bc8aa2014-09-14 16:48:21 -07001226 if (enterTransition != null) {
1227 enterTransition.addTarget(state.nonExistentView);
George Mountc03da0e2014-08-22 17:04:02 -07001228 }
George Mountecfd0072014-09-07 10:06:25 -07001229 ArrayMap<String, View> namedViews = null;
George Mount2deeaa32014-09-19 10:02:02 -07001230 ArrayList<View> sharedElementTargets = new ArrayList<View>();
George Mountecfd0072014-09-07 10:06:25 -07001231 if (sharedElementTransition != null) {
George Mount7fe44142016-08-22 15:49:00 -07001232 namedViews = remapSharedElements(state, outFragment,
1233 transitioningFragments.firstOutIsPop);
George Mountc855f7d2015-06-10 15:00:17 -07001234 setSharedElementTargets(sharedElementTransition,
1235 state.nonExistentView, namedViews, sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001236
1237 // Notify the start of the transition.
George Mount7fe44142016-08-22 15:49:00 -07001238 SharedElementCallback callback = transitioningFragments.lastInIsPop ?
George Mountecfd0072014-09-07 10:06:25 -07001239 outFragment.mEnterTransitionCallback :
1240 inFragment.mEnterTransitionCallback;
1241 ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
1242 ArrayList<View> views = new ArrayList<View>(namedViews.values());
1243 callback.onSharedElementStart(names, views, null);
1244 }
George Mountc03da0e2014-08-22 17:04:02 -07001245
George Mount27bc8aa2014-09-14 16:48:21 -07001246 ArrayList<View> exitingViews = captureExitingViews(exitTransition, outFragment,
George Mount7ce5d752014-10-30 16:09:22 -07001247 namedViews, state.nonExistentView);
George Mount27bc8aa2014-09-14 16:48:21 -07001248 if (exitingViews == null || exitingViews.isEmpty()) {
1249 exitTransition = null;
1250 }
George Mount61f83312015-09-09 16:10:29 -07001251 excludeViews(enterTransition, exitTransition, exitingViews, true);
1252 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
1253 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
George Mount27bc8aa2014-09-14 16:48:21 -07001254
George Mountc03da0e2014-08-22 17:04:02 -07001255 // Set the epicenter of the exit transition
George Mount27bc8aa2014-09-14 16:48:21 -07001256 if (mSharedElementTargetNames != null && namedViews != null) {
George Mountc03da0e2014-08-22 17:04:02 -07001257 View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
1258 if (epicenterView != null) {
George Mount27bc8aa2014-09-14 16:48:21 -07001259 if (exitTransition != null) {
1260 setEpicenter(exitTransition, epicenterView);
1261 }
1262 if (sharedElementTransition != null) {
1263 setEpicenter(sharedElementTransition, epicenterView);
1264 }
George Mountc03da0e2014-08-22 17:04:02 -07001265 }
1266 }
1267
George Mountc03da0e2014-08-22 17:04:02 -07001268 Transition transition = mergeTransitions(enterTransition, exitTransition,
George Mount7fe44142016-08-22 15:49:00 -07001269 sharedElementTransition, inFragment, transitioningFragments.lastInIsPop);
George Mountc03da0e2014-08-22 17:04:02 -07001270
1271 if (transition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001272 ArrayList<View> hiddenFragments = new ArrayList<View>();
1273 ArrayList<View> enteringViews = addTransitionTargets(state, enterTransition,
George Mount61f83312015-09-09 16:10:29 -07001274 sharedElementTransition, exitTransition, transition, sceneRoot, inFragment,
George Mount7fe44142016-08-22 15:49:00 -07001275 outFragment, hiddenFragments, transitioningFragments.lastInIsPop,
1276 sharedElementTargets);
George Mountecfd0072014-09-07 10:06:25 -07001277
George Mountc03da0e2014-08-22 17:04:02 -07001278 transition.setNameOverrides(state.nameOverrides);
1279 // We want to exclude hidden views later, so we need a non-null list in the
1280 // transition now.
1281 transition.excludeTarget(state.nonExistentView, true);
1282 // Now exclude all currently hidden fragments.
George Mountecfd0072014-09-07 10:06:25 -07001283 excludeHiddenFragments(hiddenFragments, containerId, transition);
George Mountc03da0e2014-08-22 17:04:02 -07001284 TransitionManager.beginDelayedTransition(sceneRoot, transition);
George Mountecfd0072014-09-07 10:06:25 -07001285 // Remove the view targeting after the transition starts
1286 removeTargetedViewsFromTransitions(sceneRoot, state.nonExistentView,
1287 enterTransition, enteringViews, exitTransition, exitingViews,
George Mountc855f7d2015-06-10 15:00:17 -07001288 sharedElementTransition, sharedElementTargets, transition,
1289 hiddenFragments);
George Mountd4c3c912014-06-09 12:31:34 -07001290 }
1291 }
1292 }
1293
George Mountc03da0e2014-08-22 17:04:02 -07001294 /**
George Mountc855f7d2015-06-10 15:00:17 -07001295 * Finds all children of the shared elements and sets the wrapping TransitionSet
1296 * targets to point to those. It also limits transitions that have no targets to the
1297 * specific shared elements. This allows developers to target child views of the
1298 * shared elements specifically, but this doesn't happen by default.
1299 */
1300 private static void setSharedElementTargets(TransitionSet transition,
1301 View nonExistentView, ArrayMap<String, View> namedViews,
1302 ArrayList<View> sharedElementTargets) {
1303 sharedElementTargets.clear();
1304 sharedElementTargets.addAll(namedViews.values());
1305
1306 final List<View> views = transition.getTargets();
1307 views.clear();
1308 final int count = sharedElementTargets.size();
1309 for (int i = 0; i < count; i++) {
1310 final View view = sharedElementTargets.get(i);
1311 bfsAddViewChildren(views, view);
1312 }
1313 sharedElementTargets.add(nonExistentView);
1314 addTargets(transition, sharedElementTargets);
1315 }
1316
1317 /**
1318 * Uses a breadth-first scheme to add startView and all of its children to views.
1319 * It won't add a child if it is already in views.
1320 */
1321 private static void bfsAddViewChildren(final List<View> views, final View startView) {
1322 final int startIndex = views.size();
1323 if (containedBeforeIndex(views, startView, startIndex)) {
1324 return; // This child is already in the list, so all its children are also.
1325 }
1326 views.add(startView);
1327 for (int index = startIndex; index < views.size(); index++) {
1328 final View view = views.get(index);
1329 if (view instanceof ViewGroup) {
1330 ViewGroup viewGroup = (ViewGroup) view;
1331 final int childCount = viewGroup.getChildCount();
1332 for (int childIndex = 0; childIndex < childCount; childIndex++) {
1333 final View child = viewGroup.getChildAt(childIndex);
1334 if (!containedBeforeIndex(views, child, startIndex)) {
1335 views.add(child);
1336 }
1337 }
1338 }
1339 }
1340 }
1341
1342 /**
1343 * Does a linear search through views for view, limited to maxIndex.
1344 */
1345 private static boolean containedBeforeIndex(final List<View> views, final View view,
1346 final int maxIndex) {
1347 for (int i = 0; i < maxIndex; i++) {
1348 if (views.get(i) == view) {
1349 return true;
1350 }
1351 }
1352 return false;
1353 }
1354
George Mount61f83312015-09-09 16:10:29 -07001355 private static void excludeViews(Transition transition, Transition fromTransition,
1356 ArrayList<View> views, boolean exclude) {
1357 if (transition != null) {
1358 final int viewCount = fromTransition == null ? 0 : views.size();
1359 for (int i = 0; i < viewCount; i++) {
1360 transition.excludeTarget(views.get(i), exclude);
1361 }
1362 }
1363 }
1364
George Mountc855f7d2015-06-10 15:00:17 -07001365 /**
George Mountecfd0072014-09-07 10:06:25 -07001366 * After the transition has started, remove all targets that we added to the transitions
1367 * so that the transitions are left in a clean state.
1368 */
1369 private void removeTargetedViewsFromTransitions(
1370 final ViewGroup sceneRoot, final View nonExistingView,
1371 final Transition enterTransition, final ArrayList<View> enteringViews,
1372 final Transition exitTransition, final ArrayList<View> exitingViews,
George Mount2deeaa32014-09-19 10:02:02 -07001373 final Transition sharedElementTransition, final ArrayList<View> sharedElementTargets,
George Mountecfd0072014-09-07 10:06:25 -07001374 final Transition overallTransition, final ArrayList<View> hiddenViews) {
George Mount2deeaa32014-09-19 10:02:02 -07001375 if (overallTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001376 sceneRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
1377 @Override
1378 public boolean onPreDraw() {
1379 sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
1380 if (enterTransition != null) {
George Mountecfd0072014-09-07 10:06:25 -07001381 removeTargets(enterTransition, enteringViews);
George Mount61f83312015-09-09 16:10:29 -07001382 excludeViews(enterTransition, exitTransition, exitingViews, false);
1383 excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
1384 false);
George Mountecfd0072014-09-07 10:06:25 -07001385 }
1386 if (exitTransition != null) {
1387 removeTargets(exitTransition, exitingViews);
George Mount61f83312015-09-09 16:10:29 -07001388 excludeViews(exitTransition, enterTransition, enteringViews, false);
1389 excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
1390 false);
George Mountecfd0072014-09-07 10:06:25 -07001391 }
George Mount2deeaa32014-09-19 10:02:02 -07001392 if (sharedElementTransition != null) {
1393 removeTargets(sharedElementTransition, sharedElementTargets);
1394 }
George Mountecfd0072014-09-07 10:06:25 -07001395 int numViews = hiddenViews.size();
1396 for (int i = 0; i < numViews; i++) {
1397 overallTransition.excludeTarget(hiddenViews.get(i), false);
1398 }
1399 overallTransition.excludeTarget(nonExistingView, false);
1400 return true;
1401 }
1402 });
1403 }
1404 }
1405
George Mount7ce5d752014-10-30 16:09:22 -07001406 /**
1407 * This method removes the views from transitions that target ONLY those views.
1408 * The views list should match those added in addTargets and should contain
1409 * one view that is not in the view hierarchy (state.nonExistentView).
1410 */
1411 public static void removeTargets(Transition transition, ArrayList<View> views) {
1412 if (transition instanceof TransitionSet) {
1413 TransitionSet set = (TransitionSet) transition;
1414 int numTransitions = set.getTransitionCount();
1415 for (int i = 0; i < numTransitions; i++) {
1416 Transition child = set.getTransitionAt(i);
1417 removeTargets(child, views);
1418 }
1419 } else if (!hasSimpleTarget(transition)) {
1420 List<View> targets = transition.getTargets();
1421 if (targets != null && targets.size() == views.size() &&
1422 targets.containsAll(views)) {
1423 // We have an exact match. We must have added these earlier in addTargets
1424 for (int i = views.size() - 1; i >= 0; i--) {
1425 transition.removeTarget(views.get(i));
1426 }
1427 }
George Mountecfd0072014-09-07 10:06:25 -07001428 }
1429 }
1430
George Mount7ce5d752014-10-30 16:09:22 -07001431 /**
1432 * This method adds views as targets to the transition, but only if the transition
1433 * doesn't already have a target. It is best for views to contain one View object
1434 * that does not exist in the view hierarchy (state.nonExistentView) so that
1435 * when they are removed later, a list match will suffice to remove the targets.
1436 * Otherwise, if you happened to have targeted the exact views for the transition,
1437 * the removeTargets call will remove them unexpectedly.
1438 */
1439 public static void addTargets(Transition transition, ArrayList<View> views) {
1440 if (transition instanceof TransitionSet) {
1441 TransitionSet set = (TransitionSet) transition;
1442 int numTransitions = set.getTransitionCount();
1443 for (int i = 0; i < numTransitions; i++) {
1444 Transition child = set.getTransitionAt(i);
1445 addTargets(child, views);
1446 }
1447 } else if (!hasSimpleTarget(transition)) {
1448 List<View> targets = transition.getTargets();
1449 if (isNullOrEmpty(targets)) {
1450 // We can just add the target views
1451 int numViews = views.size();
1452 for (int i = 0; i < numViews; i++) {
1453 transition.addTarget(views.get(i));
1454 }
1455 }
George Mountecfd0072014-09-07 10:06:25 -07001456 }
1457 }
1458
George Mount7ce5d752014-10-30 16:09:22 -07001459 private static boolean hasSimpleTarget(Transition transition) {
1460 return !isNullOrEmpty(transition.getTargetIds()) ||
1461 !isNullOrEmpty(transition.getTargetNames()) ||
1462 !isNullOrEmpty(transition.getTargetTypes());
1463 }
1464
1465 private static boolean isNullOrEmpty(List list) {
1466 return list == null || list.isEmpty();
1467 }
1468
George Mountecfd0072014-09-07 10:06:25 -07001469 /**
George Mountc03da0e2014-08-22 17:04:02 -07001470 * Remaps a name-to-View map, substituting different names for keys.
1471 *
1472 * @param inMap A list of keys found in the map, in the order in toGoInMap
1473 * @param toGoInMap A list of keys to use for the new map, in the order of inMap
1474 * @param namedViews The current mapping
George Mountecfd0072014-09-07 10:06:25 -07001475 * @return a new Map after it has been mapped with the new names as keys.
George Mountc03da0e2014-08-22 17:04:02 -07001476 */
1477 private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
George Mountecfd0072014-09-07 10:06:25 -07001478 ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) {
1479 ArrayMap<String, View> remappedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001480 if (!namedViews.isEmpty()) {
1481 int numKeys = inMap.size();
1482 for (int i = 0; i < numKeys; i++) {
1483 View view = namedViews.get(inMap.get(i));
George Mountecfd0072014-09-07 10:06:25 -07001484
George Mountc03da0e2014-08-22 17:04:02 -07001485 if (view != null) {
George Mountecfd0072014-09-07 10:06:25 -07001486 remappedViews.put(toGoInMap.get(i), view);
George Mountd4c3c912014-06-09 12:31:34 -07001487 }
1488 }
1489 }
George Mountecfd0072014-09-07 10:06:25 -07001490 return remappedViews;
George Mountc03da0e2014-08-22 17:04:02 -07001491 }
1492
1493 /**
George Mountecfd0072014-09-07 10:06:25 -07001494 * Maps shared elements to views in the entering fragment.
George Mountc03da0e2014-08-22 17:04:02 -07001495 *
1496 * @param state The transition State as returned from {@link #beginTransition(
1497 * android.util.SparseArray, android.util.SparseArray, boolean)}.
George Mountecfd0072014-09-07 10:06:25 -07001498 * @param inFragment The last fragment to be added.
George Mountc03da0e2014-08-22 17:04:02 -07001499 * @param isBack true if this is popping the back stack or false if this is a
1500 * forward operation.
1501 */
George Mountecfd0072014-09-07 10:06:25 -07001502 private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state,
1503 Fragment inFragment, boolean isBack) {
1504 ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
George Mountc03da0e2014-08-22 17:04:02 -07001505 View root = inFragment.getView();
1506 if (root != null) {
1507 if (mSharedElementSourceNames != null) {
1508 root.findNamedViews(namedViews);
1509 if (isBack) {
1510 namedViews = remapNames(mSharedElementSourceNames,
George Mountecfd0072014-09-07 10:06:25 -07001511 mSharedElementTargetNames, namedViews);
George Mountc03da0e2014-08-22 17:04:02 -07001512 } else {
1513 namedViews.retainAll(mSharedElementTargetNames);
1514 }
1515 }
1516 }
1517 return namedViews;
1518 }
1519
George Mountecfd0072014-09-07 10:06:25 -07001520 private void excludeHiddenFragments(final ArrayList<View> hiddenFragmentViews, int containerId,
George Mountc03da0e2014-08-22 17:04:02 -07001521 Transition transition) {
1522 if (mManager.mAdded != null) {
1523 for (int i = 0; i < mManager.mAdded.size(); i++) {
1524 Fragment fragment = mManager.mAdded.get(i);
1525 if (fragment.mView != null && fragment.mContainer != null &&
1526 fragment.mContainerId == containerId) {
1527 if (fragment.mHidden) {
George Mountecfd0072014-09-07 10:06:25 -07001528 if (!hiddenFragmentViews.contains(fragment.mView)) {
George Mountc03da0e2014-08-22 17:04:02 -07001529 transition.excludeTarget(fragment.mView, true);
George Mountecfd0072014-09-07 10:06:25 -07001530 hiddenFragmentViews.add(fragment.mView);
George Mountc03da0e2014-08-22 17:04:02 -07001531 }
1532 } else {
1533 transition.excludeTarget(fragment.mView, false);
George Mountecfd0072014-09-07 10:06:25 -07001534 hiddenFragmentViews.remove(fragment.mView);
George Mountc03da0e2014-08-22 17:04:02 -07001535 }
1536 }
1537 }
George Mountd4c3c912014-06-09 12:31:34 -07001538 }
1539 }
1540
George Mountd4c3c912014-06-09 12:31:34 -07001541 private static void setEpicenter(Transition transition, View view) {
1542 final Rect epicenter = new Rect();
1543 view.getBoundsOnScreen(epicenter);
1544
1545 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1546 @Override
1547 public Rect onGetEpicenter(Transition transition) {
1548 return epicenter;
1549 }
1550 });
1551 }
1552
1553 private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
1554 transition.setEpicenterCallback(new Transition.EpicenterCallback() {
1555 private Rect mEpicenter;
1556
1557 @Override
1558 public Rect onGetEpicenter(Transition transition) {
George Mountc03da0e2014-08-22 17:04:02 -07001559 if (mEpicenter == null && state.enteringEpicenterView != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001560 mEpicenter = new Rect();
George Mountc03da0e2014-08-22 17:04:02 -07001561 state.enteringEpicenterView.getBoundsOnScreen(mEpicenter);
George Mountd4c3c912014-06-09 12:31:34 -07001562 }
1563 return mEpicenter;
1564 }
1565 });
1566 }
1567
George Mountc03da0e2014-08-22 17:04:02 -07001568 public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
George Mount7fe44142016-08-22 15:49:00 -07001569 SparseArray<FragmentContainerTransition> transitioningFragments) {
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001570 if (FragmentManagerImpl.DEBUG) {
1571 Log.v(TAG, "popFromBackStack: " + this);
1572 LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001573 PrintWriter pw = new FastPrintWriter(logw, false, 1024);
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001574 dump(" ", null, pw, null);
Dianne Hackborn8c841092013-06-24 13:46:13 -07001575 pw.flush();
Dianne Hackbornf43a33c2012-09-27 00:48:11 -07001576 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001577
George Mounta7245b42016-02-10 17:03:32 -08001578 if (mManager.mCurState >= Fragment.CREATED) {
1579 if (state == null) {
George Mount7fe44142016-08-22 15:49:00 -07001580 if (transitioningFragments.size() != 0) {
1581 state = beginTransition(transitioningFragments);
George Mounta7245b42016-02-10 17:03:32 -08001582 }
1583 } else if (!doStateMove) {
1584 setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
George Mountc03da0e2014-08-22 17:04:02 -07001585 }
George Mountd4c3c912014-06-09 12:31:34 -07001586 }
1587
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001588 bumpBackStackNesting(-1);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001589
George Mountf2045f82016-06-27 13:15:01 -07001590 final int numOps = mOps.size();
1591 for (int opNum = numOps - 1; opNum >= 0; opNum--) {
1592 final Op op = mOps.get(opNum);
1593 Fragment f = op.fragment;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001594 switch (op.cmd) {
George Mountf2045f82016-06-27 13:15:01 -07001595 case OP_ADD:
Chet Haasebc377842011-03-22 11:35:22 -07001596 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001597 mManager.removeFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001598 FragmentManagerImpl.reverseTransit(mTransition),
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001599 mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -07001600 break;
1601 case OP_REMOVE:
Chet Haasebc377842011-03-22 11:35:22 -07001602 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001603 mManager.addFragment(f, false);
George Mountf2045f82016-06-27 13:15:01 -07001604 break;
1605 case OP_HIDE:
Chet Haasebc377842011-03-22 11:35:22 -07001606 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001607 mManager.showFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001608 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -07001609 break;
1610 case OP_SHOW:
Chet Haasebc377842011-03-22 11:35:22 -07001611 f.mNextAnim = op.popExitAnim;
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001612 mManager.hideFragment(f,
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001613 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -07001614 break;
1615 case OP_DETACH:
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001616 f.mNextAnim = op.popEnterAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001617 mManager.attachFragment(f,
1618 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -07001619 break;
1620 case OP_ATTACH:
Dianne Hackbornd04ad542011-07-25 16:16:15 -07001621 f.mNextAnim = op.popExitAnim;
Dianne Hackborn47c41562011-04-15 19:00:20 -07001622 mManager.detachFragment(f,
1623 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
George Mountf2045f82016-06-27 13:15:01 -07001624 break;
1625 default:
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001626 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001627 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001628 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001629
Dianne Hackborn5f36c962010-08-26 15:54:17 -07001630 if (doStateMove) {
1631 mManager.moveToState(mManager.mCurState,
1632 FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
George Mountecfd0072014-09-07 10:06:25 -07001633 state = null;
Dianne Hackbornb31e84bc2010-06-08 18:04:35 -07001634 }
Dianne Hackborndd913a52010-07-22 12:17:04 -07001635
1636 if (mIndex >= 0) {
1637 mManager.freeBackStackIndex(mIndex);
1638 mIndex = -1;
1639 }
George Mountd4c3c912014-06-09 12:31:34 -07001640 return state;
1641 }
1642
George Mountc03da0e2014-08-22 17:04:02 -07001643 private static void setNameOverride(ArrayMap<String, String> overrides,
1644 String source, String target) {
1645 if (source != null && target != null && !source.equals(target)) {
1646 for (int index = 0; index < overrides.size(); index++) {
1647 if (source.equals(overrides.valueAt(index))) {
1648 overrides.setValueAt(index, target);
1649 return;
1650 }
George Mountd4c3c912014-06-09 12:31:34 -07001651 }
George Mountc03da0e2014-08-22 17:04:02 -07001652 overrides.put(source, target);
George Mountd4c3c912014-06-09 12:31:34 -07001653 }
George Mountd4c3c912014-06-09 12:31:34 -07001654 }
1655
1656 private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
1657 ArrayList<String> targetNames) {
George Mount13d70322015-07-21 14:30:24 -07001658 if (sourceNames != null && targetNames != null) {
George Mountd4c3c912014-06-09 12:31:34 -07001659 for (int i = 0; i < sourceNames.size(); i++) {
1660 String source = sourceNames.get(i);
1661 String target = targetNames.get(i);
George Mountc03da0e2014-08-22 17:04:02 -07001662 setNameOverride(state.nameOverrides, source, target);
1663 }
1664 }
1665 }
1666
1667 private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1668 boolean isEnd) {
George Mount13d70322015-07-21 14:30:24 -07001669 int targetCount = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
1670 int sourceCount = mSharedElementSourceNames == null ? 0 : mSharedElementSourceNames.size();
1671 final int count = Math.min(targetCount, sourceCount);
George Mountc03da0e2014-08-22 17:04:02 -07001672 for (int i = 0; i < count; i++) {
1673 String source = mSharedElementSourceNames.get(i);
1674 String originalTarget = mSharedElementTargetNames.get(i);
George Mountecfd0072014-09-07 10:06:25 -07001675 View view = namedViews.get(originalTarget);
1676 if (view != null) {
1677 String target = view.getTransitionName();
1678 if (isEnd) {
1679 setNameOverride(state.nameOverrides, source, target);
1680 } else {
1681 setNameOverride(state.nameOverrides, target, source);
1682 }
George Mountc03da0e2014-08-22 17:04:02 -07001683 }
1684 }
1685 }
1686
1687 private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
1688 boolean isEnd) {
George Mountec3364c2015-06-03 15:47:44 -07001689 int count = namedViews == null ? 0 : namedViews.size();
George Mountc03da0e2014-08-22 17:04:02 -07001690 for (int i = 0; i < count; i++) {
1691 String source = namedViews.keyAt(i);
1692 String target = namedViews.valueAt(i).getTransitionName();
1693 if (isEnd) {
1694 setNameOverride(state.nameOverrides, source, target);
1695 } else {
1696 setNameOverride(state.nameOverrides, target, source);
George Mountd4c3c912014-06-09 12:31:34 -07001697 }
1698 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001699 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001700
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001701 public String getName() {
1702 return mName;
1703 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001704
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001705 public int getTransition() {
1706 return mTransition;
1707 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001708
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001709 public int getTransitionStyle() {
1710 return mTransitionStyle;
1711 }
Adam Powell2b6230e2010-09-07 17:55:25 -07001712
1713 public boolean isEmpty() {
George Mountf2045f82016-06-27 13:15:01 -07001714 return mOps.isEmpty();
Adam Powell2b6230e2010-09-07 17:55:25 -07001715 }
George Mountd4c3c912014-06-09 12:31:34 -07001716
1717 public class TransitionState {
George Mountc03da0e2014-08-22 17:04:02 -07001718 public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
George Mountc03da0e2014-08-22 17:04:02 -07001719 public View enteringEpicenterView;
1720 public View nonExistentView;
George Mountd4c3c912014-06-09 12:31:34 -07001721 }
George Mount7fe44142016-08-22 15:49:00 -07001722
1723 /**
1724 * Tracks the last fragment added and first fragment removed for fragment transitions.
1725 * This also tracks which fragments are changed by push or pop transactions.
1726 */
1727 public static class FragmentContainerTransition {
1728 /**
1729 * The last fragment added/attached/shown in its container
1730 */
1731 public Fragment lastIn;
1732
1733 /**
1734 * true when lastIn was added during a pop transaction or false if added with a push
1735 */
1736 public boolean lastInIsPop;
1737
1738 /**
1739 * The first fragment with a View that was removed/detached/hidden in its container.
1740 */
1741 public Fragment firstOut;
1742
1743 /**
1744 * true when firstOut was removed during a pop transaction or false otherwise
1745 */
1746 public boolean firstOutIsPop;
1747 }
Dianne Hackborn5ae74d62010-05-19 19:14:57 -07001748}