1 /* 2 * Copyright (C) 2008 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 package android.view.cts; 18 19 import static com.android.compatibility.common.util.CtsMockitoUtils.within; 20 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.Matchers.anyBoolean; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.reset; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.verifyZeroInteractions; 28 29 import android.app.Activity; 30 import android.app.Instrumentation; 31 import android.os.SystemClock; 32 import android.support.test.InstrumentationRegistry; 33 import android.support.test.filters.LargeTest; 34 import android.support.test.filters.MediumTest; 35 import android.support.test.rule.ActivityTestRule; 36 import android.support.test.runner.AndroidJUnit4; 37 import android.view.View; 38 import android.view.ViewTreeObserver; 39 import android.widget.Button; 40 import android.widget.LinearLayout; 41 import android.widget.ScrollView; 42 43 import com.android.compatibility.common.util.CtsTouchUtils; 44 import com.android.compatibility.common.util.PollingCheck; 45 46 import org.junit.Before; 47 import org.junit.Rule; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 51 @MediumTest 52 @RunWith(AndroidJUnit4.class) 53 public class ViewTreeObserverTest { 54 private static int TIMEOUT_MS = 2000; 55 56 private Instrumentation mInstrumentation; 57 private Activity mActivity; 58 private ViewTreeObserver mViewTreeObserver; 59 60 private LinearLayout mLinearLayout; 61 private Button mButton; 62 63 @Rule 64 public ActivityTestRule<MockActivity> mActivityRule = 65 new ActivityTestRule<>(MockActivity.class); 66 67 @Before 68 public void setup() throws Throwable { 69 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 70 mActivity = mActivityRule.getActivity(); 71 PollingCheck.waitFor(mActivity::hasWindowFocus); 72 layout(R.layout.viewtreeobserver_layout); 73 74 mLinearLayout = (LinearLayout) mActivity.findViewById(R.id.linearlayout); 75 mButton = (Button) mActivity.findViewById(R.id.button1); 76 } 77 78 private void layout(final int layoutId) throws Throwable { 79 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layoutId)); 80 mInstrumentation.waitForIdleSync(); 81 } 82 83 @Test 84 public void testAddOnGlobalFocusChangeListener() throws Throwable { 85 final View view1 = mActivity.findViewById(R.id.view1); 86 final View view2 = mActivity.findViewById(R.id.view2); 87 88 mActivityRule.runOnUiThread(view1::requestFocus); 89 90 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 91 final ViewTreeObserver.OnGlobalFocusChangeListener listener = 92 mock(ViewTreeObserver.OnGlobalFocusChangeListener.class); 93 mViewTreeObserver.addOnGlobalFocusChangeListener(listener); 94 95 mActivityRule.runOnUiThread(view2::requestFocus); 96 mInstrumentation.waitForIdleSync(); 97 verify(listener, within(TIMEOUT_MS)).onGlobalFocusChanged(view1, view2); 98 } 99 100 @Test 101 public void testAddOnGlobalLayoutListener() { 102 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 103 104 final ViewTreeObserver.OnGlobalLayoutListener listener = 105 mock(ViewTreeObserver.OnGlobalLayoutListener.class); 106 mViewTreeObserver.addOnGlobalLayoutListener(listener); 107 mViewTreeObserver.dispatchOnGlobalLayout(); 108 verify(listener, times(1)).onGlobalLayout(); 109 } 110 111 @Test 112 public void testAddOnPreDrawListener() { 113 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 114 115 final ViewTreeObserver.OnPreDrawListener listener = 116 mock(ViewTreeObserver.OnPreDrawListener.class); 117 mViewTreeObserver.addOnPreDrawListener(listener); 118 mViewTreeObserver.dispatchOnPreDraw(); 119 verify(listener, times(1)).onPreDraw(); 120 } 121 122 @Test 123 public void testAddOnDrawListener() { 124 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 125 126 final ViewTreeObserver.OnDrawListener listener = 127 mock(ViewTreeObserver.OnDrawListener.class); 128 mViewTreeObserver.addOnDrawListener(listener); 129 mViewTreeObserver.dispatchOnDraw(); 130 verify(listener, times(1)).onDraw(); 131 } 132 133 @Test(expected=IllegalStateException.class) 134 public void testRemoveOnDrawListenerInDispatch() { 135 final View view = new View(mActivity); 136 mViewTreeObserver = view.getViewTreeObserver(); 137 138 final ViewTreeObserver.OnDrawListener listener = 139 new ViewTreeObserver.OnDrawListener() { 140 @Override 141 public void onDraw() { 142 mViewTreeObserver.removeOnDrawListener(this); 143 } 144 }; 145 mViewTreeObserver.addOnDrawListener(listener); 146 mViewTreeObserver.dispatchOnDraw(); 147 } 148 149 @Test 150 public void testAddOnTouchModeChangeListener() throws Throwable { 151 // let the button be touch mode. 152 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mButton); 153 154 mViewTreeObserver = mButton.getViewTreeObserver(); 155 156 final ViewTreeObserver.OnTouchModeChangeListener listener = 157 mock(ViewTreeObserver.OnTouchModeChangeListener.class); 158 mViewTreeObserver.addOnTouchModeChangeListener(listener); 159 160 mActivityRule.runOnUiThread(mButton::requestFocusFromTouch); 161 mInstrumentation.waitForIdleSync(); 162 163 verify(listener, within(TIMEOUT_MS)).onTouchModeChanged(anyBoolean()); 164 } 165 166 @Test 167 public void testIsAlive() { 168 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 169 assertTrue(mViewTreeObserver.isAlive()); 170 } 171 172 @LargeTest 173 @Test 174 public void testRemoveGlobalOnLayoutListener() { 175 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 176 177 final ViewTreeObserver.OnGlobalLayoutListener listener = 178 mock(ViewTreeObserver.OnGlobalLayoutListener.class); 179 mViewTreeObserver.addOnGlobalLayoutListener(listener); 180 mViewTreeObserver.dispatchOnGlobalLayout(); 181 verify(listener, times(1)).onGlobalLayout(); 182 183 reset(listener); 184 mViewTreeObserver.removeGlobalOnLayoutListener(listener); 185 mViewTreeObserver.dispatchOnGlobalLayout(); 186 // Since we've unregistered our listener, we expect it to not be called even after 187 // we've waited for a couple of seconds 188 SystemClock.sleep(TIMEOUT_MS); 189 verifyZeroInteractions(listener); 190 } 191 192 @LargeTest 193 @Test 194 public void testRemoveOnGlobalLayoutListener() { 195 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 196 197 final ViewTreeObserver.OnGlobalLayoutListener listener = 198 mock(ViewTreeObserver.OnGlobalLayoutListener.class); 199 mViewTreeObserver.addOnGlobalLayoutListener(listener); 200 mViewTreeObserver.dispatchOnGlobalLayout(); 201 verify(listener, times(1)).onGlobalLayout(); 202 203 reset(listener); 204 mViewTreeObserver.removeOnGlobalLayoutListener(listener); 205 mViewTreeObserver.dispatchOnGlobalLayout(); 206 // Since we've unregistered our listener, we expect it to not be called even after 207 // we've waited for a couple of seconds 208 SystemClock.sleep(TIMEOUT_MS); 209 verifyZeroInteractions(listener); 210 } 211 212 @LargeTest 213 @Test 214 public void testRemoveOnGlobalFocusChangeListener() throws Throwable { 215 final View view1 = mActivity.findViewById(R.id.view1); 216 final View view2 = mActivity.findViewById(R.id.view2); 217 218 mActivityRule.runOnUiThread(view1::requestFocus); 219 220 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 221 final ViewTreeObserver.OnGlobalFocusChangeListener listener = 222 mock(ViewTreeObserver.OnGlobalFocusChangeListener.class); 223 mViewTreeObserver.addOnGlobalFocusChangeListener(listener); 224 mActivityRule.runOnUiThread(view2::requestFocus); 225 mInstrumentation.waitForIdleSync(); 226 verify(listener, within(TIMEOUT_MS)).onGlobalFocusChanged(view1, view2); 227 228 reset(listener); 229 mViewTreeObserver.removeOnGlobalFocusChangeListener(listener); 230 mActivityRule.runOnUiThread(view1::requestFocus); 231 mInstrumentation.waitForIdleSync(); 232 // Since we've unregistered our listener, we expect it to not be called even after 233 // we've waited for a couple of seconds 234 SystemClock.sleep(TIMEOUT_MS); 235 verifyZeroInteractions(listener); 236 } 237 238 @LargeTest 239 @Test 240 public void testRemoveOnPreDrawListener() { 241 mViewTreeObserver = mLinearLayout.getViewTreeObserver(); 242 243 final ViewTreeObserver.OnPreDrawListener listener = 244 mock(ViewTreeObserver.OnPreDrawListener.class); 245 mViewTreeObserver.addOnPreDrawListener(listener); 246 mViewTreeObserver.dispatchOnPreDraw(); 247 verify(listener, times(1)).onPreDraw(); 248 249 reset(listener); 250 mViewTreeObserver.removeOnPreDrawListener(listener); 251 mViewTreeObserver.dispatchOnPreDraw(); 252 // Since we've unregistered our listener, we expect it to not be called even after 253 // we've waited for a couple of seconds 254 SystemClock.sleep(TIMEOUT_MS); 255 verifyZeroInteractions(listener); 256 } 257 258 @LargeTest 259 @Test 260 public void testRemoveOnTouchModeChangeListener() throws Throwable { 261 // let the button be touch mode. 262 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mButton); 263 264 mViewTreeObserver = mButton.getViewTreeObserver(); 265 266 final ViewTreeObserver.OnTouchModeChangeListener listener = 267 mock(ViewTreeObserver.OnTouchModeChangeListener.class); 268 mViewTreeObserver.addOnTouchModeChangeListener(listener); 269 mActivityRule.runOnUiThread(mButton::requestFocusFromTouch); 270 mInstrumentation.waitForIdleSync(); 271 272 verify(listener, within(TIMEOUT_MS)).onTouchModeChanged(anyBoolean()); 273 274 reset(listener); 275 mViewTreeObserver.removeOnTouchModeChangeListener(listener); 276 mActivityRule.runOnUiThread(mButton::requestFocusFromTouch); 277 mInstrumentation.waitForIdleSync(); 278 279 // Since we've unregistered our listener we expect it to not be called even after 280 // we've waited for a couple of seconds 281 SystemClock.sleep(TIMEOUT_MS); 282 verifyZeroInteractions(listener); 283 } 284 285 @LargeTest 286 @Test 287 public void testAccessOnScrollChangedListener() throws Throwable { 288 layout(R.layout.scrollview_layout); 289 final ScrollView scrollView = (ScrollView) mActivity.findViewById(R.id.scroll_view); 290 291 mViewTreeObserver = scrollView.getViewTreeObserver(); 292 293 final ViewTreeObserver.OnScrollChangedListener listener = 294 mock(ViewTreeObserver.OnScrollChangedListener.class); 295 mViewTreeObserver.addOnScrollChangedListener(listener); 296 297 mActivityRule.runOnUiThread(() -> scrollView.fullScroll(View.FOCUS_DOWN)); 298 mInstrumentation.waitForIdleSync(); 299 verify(listener, within(TIMEOUT_MS)).onScrollChanged(); 300 301 reset(listener); 302 303 mViewTreeObserver.removeOnScrollChangedListener(listener); 304 mActivityRule.runOnUiThread(() -> scrollView.fullScroll(View.FOCUS_UP)); 305 // Since we've unregistered our listener, we expect it to not be called even after 306 // we've waited for a couple of seconds 307 SystemClock.sleep(TIMEOUT_MS); 308 verifyZeroInteractions(listener); 309 } 310 } 311