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