BackStackRecordState.java
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.fragment.app;
import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.Map;
@SuppressLint("BanParcelableUsage")
final class BackStackRecordState implements Parcelable {
private static final String TAG = FragmentManager.TAG;
final int[] mOps;
final ArrayList<String> mFragmentWhos;
final int[] mOldMaxLifecycleStates;
final int[] mCurrentMaxLifecycleStates;
final int mTransition;
final String mName;
final int mIndex;
final int mBreadCrumbTitleRes;
final CharSequence mBreadCrumbTitleText;
final int mBreadCrumbShortTitleRes;
final CharSequence mBreadCrumbShortTitleText;
final ArrayList<String> mSharedElementSourceNames;
final ArrayList<String> mSharedElementTargetNames;
final boolean mReorderingAllowed;
BackStackRecordState(BackStackRecord bse) {
final int numOps = bse.mOps.size();
mOps = new int[numOps * 6];
if (!bse.mAddToBackStack) {
throw new IllegalStateException("Not on back stack");
}
mFragmentWhos = new ArrayList<>(numOps);
mOldMaxLifecycleStates = new int[numOps];
mCurrentMaxLifecycleStates = new int[numOps];
int pos = 0;
for (int opNum = 0; opNum < numOps; opNum++) {
final BackStackRecord.Op op = bse.mOps.get(opNum);
mOps[pos++] = op.mCmd;
mFragmentWhos.add(op.mFragment != null ? op.mFragment.mWho : null);
mOps[pos++] = op.mFromExpandedOp ? 1 : 0;
mOps[pos++] = op.mEnterAnim;
mOps[pos++] = op.mExitAnim;
mOps[pos++] = op.mPopEnterAnim;
mOps[pos++] = op.mPopExitAnim;
mOldMaxLifecycleStates[opNum] = op.mOldMaxState.ordinal();
mCurrentMaxLifecycleStates[opNum] = op.mCurrentMaxState.ordinal();
}
mTransition = bse.mTransition;
mName = bse.mName;
mIndex = bse.mIndex;
mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
mSharedElementSourceNames = bse.mSharedElementSourceNames;
mSharedElementTargetNames = bse.mSharedElementTargetNames;
mReorderingAllowed = bse.mReorderingAllowed;
}
BackStackRecordState(Parcel in) {
mOps = in.createIntArray();
mFragmentWhos = in.createStringArrayList();
mOldMaxLifecycleStates = in.createIntArray();
mCurrentMaxLifecycleStates = in.createIntArray();
mTransition = in.readInt();
mName = in.readString();
mIndex = in.readInt();
mBreadCrumbTitleRes = in.readInt();
mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mBreadCrumbShortTitleRes = in.readInt();
mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mSharedElementSourceNames = in.createStringArrayList();
mSharedElementTargetNames = in.createStringArrayList();
mReorderingAllowed = in.readInt() != 0;
}
/**
* Instantiates a {@link BackStackRecord} from this state that mirrors the
* exact state it was in when the FragmentManager's state was saved. This
* assumes that all fragments included in this transactions are already
* added as active fragments to the FragmentManager.
*/
@NonNull
public BackStackRecord instantiate(@NonNull FragmentManager fm) {
BackStackRecord bse = new BackStackRecord(fm);
fillInBackStackRecord(bse);
bse.mIndex = mIndex;
for (int num = 0; num < mFragmentWhos.size(); num++) {
String fWho = mFragmentWhos.get(num);
if (fWho != null) {
bse.mOps.get(num).mFragment = fm.findActiveFragment(fWho);
}
}
bse.bumpBackStackNesting(1);
return bse;
}
/**
* Instantiates a {@link BackStackRecord} from a saved state that allows
* the returned BackStackRecord to be applied to the given FragmentManager
* as if it was a new transaction. Any fragments in the transactions will
* be pulled from the provided fragments map.
*/
@NonNull
public BackStackRecord instantiate(@NonNull FragmentManager fm,
@NonNull Map<String, Fragment> fragments) {
BackStackRecord bse = new BackStackRecord(fm);
fillInBackStackRecord(bse);
for (int num = 0; num < mFragmentWhos.size(); num++) {
String fWho = mFragmentWhos.get(num);
if (fWho != null) {
Fragment fragment = fragments.get(fWho);
if (fragment != null) {
bse.mOps.get(num).mFragment = fragment;
} else {
throw new IllegalStateException("Restoring FragmentTransaction "
+ mName + " failed due to missing saved state for Fragment ("
+ fWho + ")");
}
}
}
return bse;
}
private void fillInBackStackRecord(@NonNull BackStackRecord bse) {
int pos = 0;
int num = 0;
while (pos < mOps.length) {
BackStackRecord.Op op = new BackStackRecord.Op();
op.mCmd = mOps[pos++];
if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
Log.v(TAG, "Instantiate " + bse
+ " op #" + num + " base fragment #" + mOps[pos]);
}
op.mOldMaxState = Lifecycle.State.values()[mOldMaxLifecycleStates[num]];
op.mCurrentMaxState = Lifecycle.State.values()[mCurrentMaxLifecycleStates[num]];
op.mFromExpandedOp = mOps[pos++] != 0;
op.mEnterAnim = mOps[pos++];
op.mExitAnim = mOps[pos++];
op.mPopEnterAnim = mOps[pos++];
op.mPopExitAnim = mOps[pos++];
bse.mEnterAnim = op.mEnterAnim;
bse.mExitAnim = op.mExitAnim;
bse.mPopEnterAnim = op.mPopEnterAnim;
bse.mPopExitAnim = op.mPopExitAnim;
bse.addOp(op);
num++;
}
bse.mTransition = mTransition;
bse.mName = mName;
bse.mAddToBackStack = true;
bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
bse.mSharedElementSourceNames = mSharedElementSourceNames;
bse.mSharedElementTargetNames = mSharedElementTargetNames;
bse.mReorderingAllowed = mReorderingAllowed;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeIntArray(mOps);
dest.writeStringList(mFragmentWhos);
dest.writeIntArray(mOldMaxLifecycleStates);
dest.writeIntArray(mCurrentMaxLifecycleStates);
dest.writeInt(mTransition);
dest.writeString(mName);
dest.writeInt(mIndex);
dest.writeInt(mBreadCrumbTitleRes);
TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
dest.writeInt(mBreadCrumbShortTitleRes);
TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
dest.writeStringList(mSharedElementSourceNames);
dest.writeStringList(mSharedElementTargetNames);
dest.writeInt(mReorderingAllowed ? 1 : 0);
}
public static final Parcelable.Creator<BackStackRecordState> CREATOR =
new Parcelable.Creator<BackStackRecordState>() {
@Override
public BackStackRecordState createFromParcel(Parcel in) {
return new BackStackRecordState(in);
}
@Override
public BackStackRecordState[] newArray(int size) {
return new BackStackRecordState[size];
}
};
}