blob: dfda814132c8bc5168d65803be357996a9da41ec [file] [log] [blame]
/*
* Copyright (C) 2016 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 android.support.v4.app;
import android.content.Context;
import android.os.Bundle;
/**
* This fragment watches its primary lifecycle events and throws IllegalStateException
* if any of them are called out of order or from a bad/unexpected state.
*/
public class StrictFragment extends Fragment {
public static final int DETACHED = 0;
public static final int ATTACHED = 1;
public static final int CREATED = 2;
public static final int ACTIVITY_CREATED = 3;
public static final int STARTED = 4;
public static final int RESUMED = 5;
int mState;
boolean mCalledOnAttach, mCalledOnCreate, mCalledOnActivityCreated,
mCalledOnStart, mCalledOnResume, mCalledOnSaveInstanceState,
mCalledOnPause, mCalledOnStop, mCalledOnDestroy, mCalledOnDetach,
mCalledOnAttachFragment;
static String stateToString(int state) {
switch (state) {
case DETACHED: return "DETACHED";
case ATTACHED: return "ATTACHED";
case CREATED: return "CREATED";
case ACTIVITY_CREATED: return "ACTIVITY_CREATED";
case STARTED: return "STARTED";
case RESUMED: return "RESUMED";
}
return "(unknown " + state + ")";
}
public void checkGetActivity() {
if (getActivity() == null) {
throw new IllegalStateException("getActivity() returned null at unexpected time");
}
}
public void checkState(String caller, int... expected) {
if (expected == null || expected.length == 0) {
throw new IllegalArgumentException("must supply at least one expected state");
}
for (int expect : expected) {
if (mState == expect) {
return;
}
}
final StringBuilder expectString = new StringBuilder(stateToString(expected[0]));
for (int i = 1; i < expected.length; i++) {
expectString.append(" or ").append(stateToString(expected[i]));
}
throw new IllegalStateException(caller + " called while fragment was "
+ stateToString(mState) + "; expected " + expectString.toString());
}
public void checkStateAtLeast(String caller, int minState) {
if (mState < minState) {
throw new IllegalStateException(caller + " called while fragment was "
+ stateToString(mState) + "; expected at least " + stateToString(minState));
}
}
@Override
public void onAttachFragment(Fragment childFragment) {
mCalledOnAttachFragment = true;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mCalledOnAttach = true;
checkState("onAttach", DETACHED);
mState = ATTACHED;
checkGetActivity();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mCalledOnCreate) {
throw new IllegalStateException("onCreate called more than once");
}
mCalledOnCreate = true;
checkState("onCreate", ATTACHED);
mState = CREATED;
checkGetActivity();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mCalledOnActivityCreated = true;
checkState("onActivityCreated", ATTACHED, CREATED);
mState = ACTIVITY_CREATED;
checkGetActivity();
}
@Override
public void onStart() {
super.onStart();
mCalledOnStart = true;
checkState("onStart", ACTIVITY_CREATED);
mState = STARTED;
checkGetActivity();
}
@Override
public void onResume() {
super.onResume();
mCalledOnResume = true;
checkState("onResume", STARTED);
mState = RESUMED;
checkGetActivity();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mCalledOnSaveInstanceState = true;
checkGetActivity();
checkStateAtLeast("onSaveInstanceState", STARTED);
}
@Override
public void onPause() {
super.onPause();
mCalledOnPause = true;
checkState("onPause", RESUMED);
mState = STARTED;
checkGetActivity();
}
@Override
public void onStop() {
super.onStop();
mCalledOnStop = true;
checkState("onStop", STARTED);
mState = CREATED;
checkGetActivity();
}
@Override
public void onDestroy() {
super.onDestroy();
mCalledOnDestroy = true;
checkState("onDestroy", CREATED);
mState = ATTACHED;
checkGetActivity();
}
@Override
public void onDetach() {
super.onDetach();
mCalledOnDetach = true;
checkState("onDestroy", CREATED, ATTACHED);
mState = DETACHED;
checkGetActivity();
}
}