RequestFocusTest.java revision c6fd88e213703a581fe4680259981f09ae0444f2
1/*
2 * Copyright (C) 2007 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
17package android.widget.focus;
18
19import android.os.Handler;
20import android.test.ActivityInstrumentationTestCase2;
21import android.test.UiThreadTest;
22import android.test.suitebuilder.annotation.LargeTest;
23import android.test.suitebuilder.annotation.MediumTest;
24import android.util.AndroidRuntimeException;
25import android.view.View;
26import android.view.View.OnFocusChangeListener;
27import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
28import android.widget.Button;
29
30import com.android.frameworks.coretests.R;
31
32import java.util.ArrayList;
33import java.util.List;
34
35/**
36 * {@link RequestFocusTest} is set up to exercise cases where the views that
37 * have focus become invisible or GONE.
38 */
39public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> {
40
41    private Button mTopLeftButton;
42    private Button mBottomLeftButton;
43    private Button mTopRightButton;
44    private Button mBottomRightButton;
45    private Handler mHandler;
46
47    public RequestFocusTest() {
48        super(RequestFocus.class);
49    }
50
51    @Override
52    public void setUp() throws Exception {
53        super.setUp();
54
55        final RequestFocus a = getActivity();
56        mHandler = a.getHandler();
57        mTopLeftButton = (Button) a.findViewById(R.id.topLeftButton);
58        mBottomLeftButton = (Button) a.findViewById(R.id.bottomLeftButton);
59        mTopRightButton = (Button) a.findViewById(R.id.topRightButton);
60        mBottomRightButton = (Button) a.findViewById(R.id.bottomRightButton);
61    }
62
63    // Test that setUp did what we expect it to do.  These asserts
64    // can't go in SetUp, or the test will hang.
65    @MediumTest
66    public void testSetUpConditions() throws Exception {
67        assertNotNull(mHandler);
68        assertNotNull(mTopLeftButton);
69        assertNotNull(mTopRightButton);
70        assertNotNull(mBottomLeftButton);
71        assertNotNull(mBottomRightButton);
72        assertTrue("requestFocus() should work from onCreate.", mBottomRightButton.hasFocus());
73    }
74
75    // Test that a posted requestFocus works.
76    @LargeTest
77    public void testPostedRequestFocus() throws Exception {
78        mHandler.post(new Runnable() { public void run() {
79            mBottomLeftButton.requestFocus();
80        }});
81        synchronized(this) {
82            try {
83                wait(500);
84            } catch (InterruptedException e) {
85                // Don't care.
86            }
87        }
88        assertTrue("Focus should move to bottom left", mBottomLeftButton.hasFocus());
89    }
90
91    // Test that a requestFocus from the wrong thread fails.
92    @MediumTest
93    public void testWrongThreadRequestFocusFails() throws Exception {
94        try {
95            mTopRightButton.requestFocus();
96            fail("requestFocus from wrong thread should raise exception.");
97        } catch (AndroidRuntimeException e) {
98            // Expected.  The actual exception is not public, so we can't catch it.
99            assertEquals("android.view.ViewRootImpl$CalledFromWrongThreadException",
100                         e.getClass().getName());
101        }
102    }
103
104    /**
105     * This tests checks the case in which the first focusable View clears focus.
106     * In such a case the framework tries to give the focus to another View starting
107     * from the top. Hence, the framework will try to give focus to the view that
108     * wants to clear its focus. From a client perspective, the view does not loose
109     * focus after the call, therefore no callback for focus change should be invoked.
110     *
111     * @throws Exception If an error occurs.
112     */
113    @UiThreadTest
114    public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception {
115        // Get the first focusable.
116        Button button = mTopLeftButton;
117
118        // Make sure that the button is the first focusable and focus it.
119        button.getRootView().requestFocus(View.FOCUS_DOWN);
120        assertTrue(button.hasFocus());
121
122        // Attach on focus change listener that should not be called.
123        button.setOnFocusChangeListener(new OnFocusChangeListener() {
124            @Override
125            public void onFocusChange(View v, boolean hasFocus) {
126                throw new IllegalStateException("Unexpeced call to"
127                        + "OnFocusChangeListener#onFocusChange");
128            }
129        });
130
131        // Attach on global focus change listener that should not be called.
132        button.getViewTreeObserver().addOnGlobalFocusChangeListener(
133                new OnGlobalFocusChangeListener() {
134            @Override
135            public void onGlobalFocusChanged(View oldFocus, View newFocus) {
136                throw new IllegalStateException("Unexpeced call to"
137                        + "OnFocusChangeListener#onFocusChange");
138            }
139        });
140
141        // Try to clear focus.
142        button.clearFocus();
143    }
144
145    /**
146     * This tests check whether the on focus change callbacks are invoked in
147     * the proper order when a View loses focus and the framework gives it to
148     * the fist focusable one.
149     *
150     * @throws Exception
151     */
152    @UiThreadTest
153    public void testOnFocusChangeCallbackOrder() throws Exception {
154        // Get the first focusable.
155        Button clearingFocusButton = mTopRightButton;
156        Button gainingFocusButton = mTopLeftButton;
157
158        // Make sure that the clearing focus is not the first focusable.
159        View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
160                View.FOCUS_FORWARD);
161        assertNotSame("The clearing focus button is not the first focusable.",
162                clearingFocusButton, focusCandidate);
163        assertSame("The gaining focus button is the first focusable.",
164                gainingFocusButton, focusCandidate);
165
166        // Focus the clearing focus button.
167        clearingFocusButton.requestFocus();
168        assertTrue(clearingFocusButton.hasFocus());
169
170        // Register the invocation order checker.
171        CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton,
172                gainingFocusButton);
173        clearingFocusButton.setOnFocusChangeListener(checker);
174        gainingFocusButton.setOnFocusChangeListener(checker);
175        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker);
176
177        // Try to clear focus.
178        clearingFocusButton.clearFocus();
179
180        // Check that no callback was invoked since focus did not move.
181        checker.verify();
182    }
183
184    /**
185     * This class check whether the focus change callback are invoked in order.
186     */
187    private class CallbackOrderChecker implements OnFocusChangeListener,
188            OnGlobalFocusChangeListener {
189
190        private class CallbackInvocation {
191            final String mMethodName;
192            final Object[] mArguments;
193
194            CallbackInvocation(String methodName, Object[] arguments) {
195                mMethodName = methodName;
196                mArguments = arguments;
197            }
198        }
199
200        private final View mClearingFocusView;
201        private final View mGainingFocusView;
202
203        private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>();
204
205        public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) {
206            mClearingFocusView = clearingFocusView;
207            mGainingFocusView = gainingFocusView;
208        }
209
210        @Override
211        public void onFocusChange(View view, boolean hasFocus) {
212            CallbackInvocation invocation = new CallbackInvocation(
213                    "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus});
214            mInvocations.add(invocation);
215        }
216
217        @Override
218        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
219            CallbackInvocation invocation = new CallbackInvocation(
220                    "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus});
221            mInvocations.add(invocation);
222        }
223
224        public void verify() {
225            assertSame("All focus change callback should be invoked.", 3, mInvocations.size());
226            assertInvioked("Callback for View clearing focus explected.", 0,
227                    "OnFocusChangeListener#onFocusChange",
228                    new Object[] {mClearingFocusView, false});
229            assertInvioked("Callback for View global focus change explected.", 1,
230                    "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView,
231                    mGainingFocusView});
232            assertInvioked("Callback for View gaining focus explected.", 2,
233                    "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true});
234        }
235
236        private void assertInvioked(String message, int order, String methodName,
237                Object[] arguments) {
238            CallbackInvocation invocation = mInvocations.get(order);
239            assertEquals(message, methodName, invocation.mMethodName);
240            assertEquals(message, arguments[0], invocation.mArguments[0]);
241            assertEquals(message, arguments[1], invocation.mArguments[1]);
242        }
243    }
244}
245