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