Home | History | Annotate | Download | only in test
      1 /*
      2  * Copyright 2018 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 package androidx.fragment.app.test;
     17 
     18 import static org.junit.Assert.assertFalse;
     19 
     20 import android.app.Activity;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.os.Build.VERSION;
     24 import android.os.Build.VERSION_CODES;
     25 import android.os.Bundle;
     26 import android.transition.Transition;
     27 import android.transition.Transition.TransitionListener;
     28 import android.transition.TransitionInflater;
     29 import android.view.LayoutInflater;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 
     33 import androidx.annotation.Nullable;
     34 import androidx.fragment.app.Fragment;
     35 import androidx.fragment.app.FragmentActivity;
     36 import androidx.fragment.test.R;
     37 
     38 import java.util.concurrent.CountDownLatch;
     39 import java.util.concurrent.TimeUnit;
     40 
     41 /**
     42  * A simple activity used for Fragment Transitions and lifecycle event ordering
     43  */
     44 public class FragmentTestActivity extends FragmentActivity {
     45     public final CountDownLatch onDestroyLatch = new CountDownLatch(1);
     46 
     47     @Override
     48     public void onCreate(Bundle icicle) {
     49         super.onCreate(icicle);
     50         setContentView(R.layout.activity_content);
     51         Intent intent = getIntent();
     52         if (intent != null && intent.getBooleanExtra("finishEarly", false)) {
     53             finish();
     54             getSupportFragmentManager().beginTransaction()
     55                     .add(new AssertNotDestroyed(), "not destroyed")
     56                     .commit();
     57         }
     58     }
     59 
     60     @Override
     61     protected void onDestroy() {
     62         super.onDestroy();
     63         onDestroyLatch.countDown();
     64     }
     65 
     66     public static class TestFragment extends Fragment {
     67         public static final int ENTER = 0;
     68         public static final int RETURN = 1;
     69         public static final int EXIT = 2;
     70         public static final int REENTER = 3;
     71         public static final int SHARED_ELEMENT_ENTER = 4;
     72         public static final int SHARED_ELEMENT_RETURN = 5;
     73         private static final int TRANSITION_COUNT = 6;
     74 
     75         private static final String LAYOUT_ID = "layoutId";
     76         private static final String TRANSITION_KEY = "transition_";
     77         private int mLayoutId = R.layout.fragment_start;
     78         private final int[] mTransitionIds = new int[] {
     79                 R.transition.fade,
     80                 R.transition.fade,
     81                 R.transition.fade,
     82                 R.transition.fade,
     83                 R.transition.change_bounds,
     84                 R.transition.change_bounds,
     85         };
     86         private final Object[] mListeners = new Object[TRANSITION_COUNT];
     87 
     88         public TestFragment() {
     89             if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
     90                 for (int i = 0; i < TRANSITION_COUNT; i++) {
     91                     mListeners[i] = new TransitionCalledListener();
     92                 }
     93             }
     94         }
     95 
     96         public static TestFragment create(int layoutId) {
     97             TestFragment testFragment = new TestFragment();
     98             testFragment.mLayoutId = layoutId;
     99             return testFragment;
    100         }
    101 
    102         public void clearTransitions() {
    103             for (int i = 0; i < TRANSITION_COUNT; i++) {
    104                 mTransitionIds[i] = 0;
    105             }
    106         }
    107 
    108         public void clearNotifications() {
    109             for (int i = 0; i < TRANSITION_COUNT; i++) {
    110                 ((TransitionCalledListener)mListeners[i]).startLatch = new CountDownLatch(1);
    111                 ((TransitionCalledListener)mListeners[i]).endLatch = new CountDownLatch(1);
    112             }
    113         }
    114 
    115         @Override
    116         public void onCreate(Bundle savedInstanceState) {
    117             super.onCreate(savedInstanceState);
    118             if (savedInstanceState != null) {
    119                 mLayoutId = savedInstanceState.getInt(LAYOUT_ID, mLayoutId);
    120                 for (int i = 0; i < TRANSITION_COUNT; i++) {
    121                     String key = TRANSITION_KEY + i;
    122                     mTransitionIds[i] = savedInstanceState.getInt(key, mTransitionIds[i]);
    123                 }
    124             }
    125         }
    126 
    127         @Override
    128         public void onSaveInstanceState(Bundle outState) {
    129             super.onSaveInstanceState(outState);
    130             outState.putInt(LAYOUT_ID, mLayoutId);
    131             for (int i = 0; i < TRANSITION_COUNT; i++) {
    132                 String key = TRANSITION_KEY + i;
    133                 outState.putInt(key, mTransitionIds[i]);
    134             }
    135         }
    136 
    137         @Override
    138         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    139                 Bundle savedInstanceState) {
    140             return inflater.inflate(mLayoutId, container, false);
    141         }
    142 
    143         @SuppressWarnings("deprecation")
    144         @Override
    145         public void onAttach(Activity activity) {
    146             super.onAttach(activity);
    147             if (VERSION.SDK_INT > VERSION_CODES.KITKAT) {
    148                 setEnterTransition(loadTransition(ENTER));
    149                 setReenterTransition(loadTransition(REENTER));
    150                 setExitTransition(loadTransition(EXIT));
    151                 setReturnTransition(loadTransition(RETURN));
    152                 setSharedElementEnterTransition(loadTransition(SHARED_ELEMENT_ENTER));
    153                 setSharedElementReturnTransition(loadTransition(SHARED_ELEMENT_RETURN));
    154             }
    155         }
    156 
    157         public boolean wasStartCalled(int transitionKey) {
    158             return ((TransitionCalledListener)mListeners[transitionKey]).startLatch.getCount() == 0;
    159         }
    160 
    161         public boolean wasEndCalled(int transitionKey) {
    162             return ((TransitionCalledListener)mListeners[transitionKey]).endLatch.getCount() == 0;
    163         }
    164 
    165         public boolean waitForStart(int transitionKey)
    166                 throws InterruptedException {
    167             TransitionCalledListener l = ((TransitionCalledListener)mListeners[transitionKey]);
    168             return l.startLatch.await(500,TimeUnit.MILLISECONDS);
    169         }
    170 
    171         public boolean waitForEnd(int transitionKey)
    172                 throws InterruptedException {
    173             TransitionCalledListener l = ((TransitionCalledListener)mListeners[transitionKey]);
    174             return l.endLatch.await(500,TimeUnit.MILLISECONDS);
    175         }
    176 
    177         private Transition loadTransition(int key) {
    178             final int id = mTransitionIds[key];
    179             if (id == 0) {
    180                 return null;
    181             }
    182             Transition transition = TransitionInflater.from(getActivity()).inflateTransition(id);
    183             transition.addListener(((TransitionCalledListener)mListeners[key]));
    184             return transition;
    185         }
    186 
    187         private class TransitionCalledListener implements TransitionListener {
    188             public CountDownLatch startLatch = new CountDownLatch(1);
    189             public CountDownLatch endLatch = new CountDownLatch(1);
    190 
    191             public TransitionCalledListener() {
    192             }
    193 
    194             @Override
    195             public void onTransitionStart(Transition transition) {
    196                 startLatch.countDown();
    197             }
    198 
    199             @Override
    200             public void onTransitionEnd(Transition transition) {
    201                 endLatch.countDown();
    202             }
    203 
    204             @Override
    205             public void onTransitionCancel(Transition transition) {
    206             }
    207 
    208             @Override
    209             public void onTransitionPause(Transition transition) {
    210             }
    211 
    212             @Override
    213             public void onTransitionResume(Transition transition) {
    214             }
    215         }
    216     }
    217 
    218     public static class ParentFragment extends Fragment {
    219         static final String CHILD_FRAGMENT_TAG = "childFragment";
    220         public boolean wasAttachedInTime;
    221 
    222         private boolean mRetainChild;
    223 
    224         @Override
    225         public void onCreate(Bundle savedInstanceState) {
    226             super.onCreate(savedInstanceState);
    227 
    228             ChildFragment f = getChildFragment();
    229             if (f == null) {
    230                 f = new ChildFragment();
    231                 if (mRetainChild) {
    232                     f.setRetainInstance(true);
    233                 }
    234                 getChildFragmentManager().beginTransaction().add(f, CHILD_FRAGMENT_TAG).commitNow();
    235             }
    236             wasAttachedInTime = f.attached;
    237         }
    238 
    239         public ChildFragment getChildFragment() {
    240             return (ChildFragment) getChildFragmentManager().findFragmentByTag(CHILD_FRAGMENT_TAG);
    241         }
    242 
    243         public void setRetainChildInstance(boolean retainChild) {
    244             mRetainChild = retainChild;
    245         }
    246     }
    247 
    248     public static class ChildFragment extends Fragment {
    249         private OnAttachListener mOnAttachListener;
    250 
    251         public boolean attached;
    252         public boolean onActivityResultCalled;
    253         public int onActivityResultRequestCode;
    254         public int onActivityResultResultCode;
    255 
    256         @Override
    257         public void onAttach(Context activity) {
    258             super.onAttach(activity);
    259             attached = true;
    260             if (mOnAttachListener != null) {
    261                 mOnAttachListener.onAttach(activity, this);
    262             }
    263         }
    264 
    265         public void setOnAttachListener(OnAttachListener listener) {
    266             mOnAttachListener = listener;
    267         }
    268 
    269         public interface OnAttachListener {
    270             void onAttach(Context activity, ChildFragment fragment);
    271         }
    272 
    273         @Override
    274         public void onActivityResult(int requestCode, int resultCode, Intent data) {
    275             onActivityResultCalled = true;
    276             onActivityResultRequestCode = requestCode;
    277             onActivityResultResultCode = resultCode;
    278         }
    279     }
    280 
    281     public static class AssertNotDestroyed extends Fragment {
    282         @Override
    283         public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    284             super.onActivityCreated(savedInstanceState);
    285             if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
    286                 assertFalse(getActivity().isDestroyed());
    287             }
    288         }
    289     }
    290 }
    291