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