Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2016 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 
     17 
     18 package android.support.v4.app;
     19 
     20 import android.content.Context;
     21 import android.os.Bundle;
     22 
     23 /**
     24  * This fragment watches its primary lifecycle events and throws IllegalStateException
     25  * if any of them are called out of order or from a bad/unexpected state.
     26  */
     27 public class StrictFragment extends Fragment {
     28     public static final int DETACHED = 0;
     29     public static final int ATTACHED = 1;
     30     public static final int CREATED = 2;
     31     public static final int ACTIVITY_CREATED = 3;
     32     public static final int STARTED = 4;
     33     public static final int RESUMED = 5;
     34 
     35     int mState;
     36 
     37     boolean mCalledOnAttach, mCalledOnCreate, mCalledOnActivityCreated,
     38             mCalledOnStart, mCalledOnResume, mCalledOnSaveInstanceState,
     39             mCalledOnPause, mCalledOnStop, mCalledOnDestroy, mCalledOnDetach,
     40             mCalledOnAttachFragment;
     41 
     42     static String stateToString(int state) {
     43         switch (state) {
     44             case DETACHED: return "DETACHED";
     45             case ATTACHED: return "ATTACHED";
     46             case CREATED: return "CREATED";
     47             case ACTIVITY_CREATED: return "ACTIVITY_CREATED";
     48             case STARTED: return "STARTED";
     49             case RESUMED: return "RESUMED";
     50         }
     51         return "(unknown " + state + ")";
     52     }
     53 
     54     public void checkGetActivity() {
     55         if (getActivity() == null) {
     56             throw new IllegalStateException("getActivity() returned null at unexpected time");
     57         }
     58     }
     59 
     60     public void checkState(String caller, int... expected) {
     61         if (expected == null || expected.length == 0) {
     62             throw new IllegalArgumentException("must supply at least one expected state");
     63         }
     64         for (int expect : expected) {
     65             if (mState == expect) {
     66                 return;
     67             }
     68         }
     69         final StringBuilder expectString = new StringBuilder(stateToString(expected[0]));
     70         for (int i = 1; i < expected.length; i++) {
     71             expectString.append(" or ").append(stateToString(expected[i]));
     72         }
     73         throw new IllegalStateException(caller + " called while fragment was "
     74                 + stateToString(mState) + "; expected " + expectString.toString());
     75     }
     76 
     77     public void checkStateAtLeast(String caller, int minState) {
     78         if (mState < minState) {
     79             throw new IllegalStateException(caller + " called while fragment was "
     80                     + stateToString(mState) + "; expected at least " + stateToString(minState));
     81         }
     82     }
     83 
     84     @Override
     85     public void onAttachFragment(Fragment childFragment) {
     86         mCalledOnAttachFragment = true;
     87     }
     88 
     89     @Override
     90     public void onAttach(Context context) {
     91         super.onAttach(context);
     92         mCalledOnAttach = true;
     93         checkState("onAttach", DETACHED);
     94         mState = ATTACHED;
     95         checkGetActivity();
     96     }
     97 
     98     @Override
     99     public void onCreate(Bundle savedInstanceState) {
    100         super.onCreate(savedInstanceState);
    101         if (mCalledOnCreate) {
    102             throw new IllegalStateException("onCreate called more than once");
    103         }
    104         mCalledOnCreate = true;
    105         checkState("onCreate", ATTACHED);
    106         mState = CREATED;
    107         checkGetActivity();
    108     }
    109 
    110     @Override
    111     public void onActivityCreated(Bundle savedInstanceState) {
    112         super.onActivityCreated(savedInstanceState);
    113         mCalledOnActivityCreated = true;
    114         checkState("onActivityCreated", ATTACHED, CREATED);
    115         mState = ACTIVITY_CREATED;
    116         checkGetActivity();
    117     }
    118 
    119     @Override
    120     public void onStart() {
    121         super.onStart();
    122         mCalledOnStart = true;
    123         checkState("onStart", ACTIVITY_CREATED);
    124         mState = STARTED;
    125         checkGetActivity();
    126     }
    127 
    128     @Override
    129     public void onResume() {
    130         super.onResume();
    131         mCalledOnResume = true;
    132         checkState("onResume", STARTED);
    133         mState = RESUMED;
    134         checkGetActivity();
    135     }
    136 
    137     @Override
    138     public void onSaveInstanceState(Bundle outState) {
    139         super.onSaveInstanceState(outState);
    140         mCalledOnSaveInstanceState = true;
    141         checkGetActivity();
    142         checkStateAtLeast("onSaveInstanceState", STARTED);
    143     }
    144 
    145     @Override
    146     public void onPause() {
    147         super.onPause();
    148         mCalledOnPause = true;
    149         checkState("onPause", RESUMED);
    150         mState = STARTED;
    151         checkGetActivity();
    152     }
    153 
    154     @Override
    155     public void onStop() {
    156         super.onStop();
    157         mCalledOnStop = true;
    158         checkState("onStop", STARTED);
    159         mState = CREATED;
    160         checkGetActivity();
    161     }
    162 
    163     @Override
    164     public void onDestroy() {
    165         super.onDestroy();
    166         mCalledOnDestroy = true;
    167         checkState("onDestroy", CREATED);
    168         mState = ATTACHED;
    169         checkGetActivity();
    170     }
    171 
    172     @Override
    173     public void onDetach() {
    174         super.onDetach();
    175         mCalledOnDetach = true;
    176         checkState("onDestroy", CREATED, ATTACHED);
    177         mState = DETACHED;
    178         checkGetActivity();
    179     }
    180 }
    181