1 /* 2 * Copyright (C) 2016 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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 23 import android.app.Activity; 24 import android.app.Instrumentation; 25 import android.os.SystemClock; 26 import android.support.test.InstrumentationRegistry; 27 import android.support.test.filters.LargeTest; 28 import android.support.test.rule.ActivityTestRule; 29 import android.support.test.runner.AndroidJUnit4; 30 import android.util.Log; 31 import android.view.InputDevice; 32 import android.view.KeyEvent; 33 import android.view.MotionEvent; 34 import android.view.View; 35 import android.view.ViewConfiguration; 36 import android.view.ViewGroup; 37 38 import com.android.compatibility.common.util.CtsTouchUtils; 39 import com.android.compatibility.common.util.PollingCheck; 40 41 import org.junit.Before; 42 import org.junit.Rule; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 /** 47 * Test {@link View}. 48 */ 49 @LargeTest 50 @RunWith(AndroidJUnit4.class) 51 public class TooltipTest { 52 private static final String LOG_TAG = "TooltipTest"; 53 54 private static final long TIMEOUT_DELTA = 10000; 55 private static final long WAIT_MARGIN = 100; 56 57 private Instrumentation mInstrumentation; 58 private Activity mActivity; 59 private ViewGroup mTopmostView; 60 private ViewGroup mGroupView; 61 private View mNoTooltipView; 62 private View mTooltipView; 63 private View mNoTooltipView2; 64 private View mEmptyGroup; 65 66 @Rule 67 public ActivityTestRule<TooltipActivity> mActivityRule = 68 new ActivityTestRule<>(TooltipActivity.class); 69 70 @Rule 71 public ActivityTestRule<CtsActivity> mCtsActivityRule = 72 new ActivityTestRule<>(CtsActivity.class, false, false); 73 74 @Before setup()75 public void setup() { 76 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 77 mActivity = mActivityRule.getActivity(); 78 mTopmostView = (ViewGroup) mActivity.findViewById(R.id.tooltip_layout); 79 mGroupView = (ViewGroup) mActivity.findViewById(R.id.tooltip_group); 80 mNoTooltipView = mActivity.findViewById(R.id.no_tooltip); 81 mTooltipView = mActivity.findViewById(R.id.has_tooltip); 82 mNoTooltipView2 = mActivity.findViewById(R.id.no_tooltip2); 83 mEmptyGroup = mActivity.findViewById(R.id.empty_group); 84 85 PollingCheck.waitFor(TIMEOUT_DELTA, mActivity::hasWindowFocus); 86 } 87 waitOut(long msDelay)88 private void waitOut(long msDelay) { 89 try { 90 Thread.sleep(msDelay + WAIT_MARGIN); 91 } catch (InterruptedException e) { 92 Log.e(LOG_TAG, "Wait interrupted. Test may fail!", e); 93 } 94 } 95 setTooltipText(View view, CharSequence tooltipText)96 private void setTooltipText(View view, CharSequence tooltipText) throws Throwable { 97 mActivityRule.runOnUiThread(() -> view.setTooltipText(tooltipText)); 98 } 99 hasTooltip(View view)100 private boolean hasTooltip(View view) { 101 final View tooltipView = view.getTooltipView(); 102 return tooltipView != null && tooltipView.getParent() != null; 103 } 104 105 addView(ViewGroup parent, View view)106 private void addView(ViewGroup parent, View view) throws Throwable { 107 mActivityRule.runOnUiThread(() -> parent.addView(view)); 108 mInstrumentation.waitForIdleSync(); 109 } 110 removeView(View view)111 private void removeView(View view) throws Throwable { 112 mActivityRule.runOnUiThread(() -> ((ViewGroup) (view.getParent())).removeView(view)); 113 mInstrumentation.waitForIdleSync(); 114 } 115 setVisibility(View view, int visibility)116 private void setVisibility(View view, int visibility) throws Throwable { 117 mActivityRule.runOnUiThread(() -> view.setVisibility(visibility)); 118 } 119 setClickable(View view)120 private void setClickable(View view) throws Throwable { 121 mActivityRule.runOnUiThread(() -> view.setClickable(true)); 122 } 123 setLongClickable(View view)124 private void setLongClickable(View view) throws Throwable { 125 mActivityRule.runOnUiThread(() -> view.setLongClickable(true)); 126 } 127 setContextClickable(View view)128 private void setContextClickable(View view) throws Throwable { 129 mActivityRule.runOnUiThread(() -> view.setContextClickable(true)); 130 } 131 callPerformLongClick(View view)132 private void callPerformLongClick(View view) throws Throwable { 133 mActivityRule.runOnUiThread(() -> view.performLongClick(0, 0)); 134 } 135 requestLowProfileSystemUi()136 private void requestLowProfileSystemUi() throws Throwable { 137 final int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; 138 mActivityRule.runOnUiThread(() -> mTooltipView.setSystemUiVisibility(flag)); 139 PollingCheck.waitFor(TIMEOUT_DELTA, 140 () -> (mTooltipView.getWindowSystemUiVisibility() & flag) == flag); 141 } 142 injectKeyPress(View target, int keyCode, int duration)143 private void injectKeyPress(View target, int keyCode, int duration) throws Throwable { 144 if (target != null) { 145 mActivityRule.runOnUiThread(() -> { 146 target.setFocusableInTouchMode(true); 147 target.requestFocus(); 148 }); 149 mInstrumentation.waitForIdleSync(); 150 assertTrue(target.isFocused()); 151 } 152 mInstrumentation.sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); 153 waitOut(duration); 154 mInstrumentation.sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); 155 } 156 injectArbitraryShortKeyPress()157 private void injectArbitraryShortKeyPress() throws Throwable { 158 injectKeyPress(null, KeyEvent.KEYCODE_0, 0); 159 } 160 injectLongKeyPress(View target, int keyCode)161 private void injectLongKeyPress(View target, int keyCode) throws Throwable { 162 injectKeyPress(target, keyCode, ViewConfiguration.getLongPressTimeout()); 163 } 164 injectLongEnter(View target)165 private void injectLongEnter(View target) throws Throwable { 166 injectLongKeyPress(target, KeyEvent.KEYCODE_ENTER); 167 } 168 injectShortClick(View target)169 private void injectShortClick(View target) { 170 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, target); 171 } 172 injectLongClick(View target)173 private void injectLongClick(View target) { 174 CtsTouchUtils.emulateLongPressOnView(mInstrumentation, target, 175 target.getWidth() / 2, target.getHeight() / 2); 176 } 177 injectMotionEvent(MotionEvent event)178 private void injectMotionEvent(MotionEvent event) { 179 mInstrumentation.sendPointerSync(event); 180 } 181 injectHoverMove(View target, int offsetX, int offsetY)182 private void injectHoverMove(View target, int offsetX, int offsetY) { 183 injectMotionEvent(obtainMouseEvent( 184 target, MotionEvent.ACTION_HOVER_MOVE, offsetX, offsetY)); 185 } 186 injectHoverMove(View target)187 private void injectHoverMove(View target) { 188 injectHoverMove(target, 0, 0); 189 } 190 injectLongHoverMove(View target)191 private void injectLongHoverMove(View target) { 192 injectHoverMove(target); 193 waitOut(ViewConfiguration.getHoverTooltipShowTimeout()); 194 } 195 obtainMouseEvent(View target, int action, int offsetX, int offsetY)196 private static MotionEvent obtainMouseEvent(View target, int action, int offsetX, int offsetY) { 197 final long eventTime = SystemClock.uptimeMillis(); 198 final int[] xy = new int[2]; 199 target.getLocationOnScreen(xy); 200 MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, 201 xy[0] + target.getWidth() / 2 + offsetX, xy[1] + target.getHeight() / 2 + offsetY, 202 0); 203 event.setSource(InputDevice.SOURCE_MOUSE); 204 return event; 205 } 206 207 @Test testGetSetTooltip()208 public void testGetSetTooltip() throws Throwable { 209 // No tooltip set in resource 210 assertEquals(null, mNoTooltipView.getTooltipText()); 211 212 // Set the tooltip, read it back 213 final String tooltipText1 = "new tooltip"; 214 setTooltipText(mNoTooltipView, tooltipText1); 215 assertEquals(tooltipText1, mNoTooltipView.getTooltipText()); 216 217 // Clear the tooltip. 218 setTooltipText(mNoTooltipView, null); 219 assertEquals(null, mNoTooltipView.getTooltipText()); 220 221 // Check the tooltip set in resource 222 assertEquals("tooltip text", mTooltipView.getTooltipText()); 223 224 // Clear the tooltip set in resource 225 setTooltipText(mTooltipView, null); 226 assertEquals(null, mTooltipView.getTooltipText()); 227 228 // Set the tooltip again, read it back 229 final String tooltipText2 = "new tooltip 2"; 230 setTooltipText(mTooltipView, tooltipText2); 231 assertEquals(tooltipText2, mTooltipView.getTooltipText()); 232 } 233 234 @Test testNoTooltipWhenNotSet()235 public void testNoTooltipWhenNotSet() throws Throwable { 236 callPerformLongClick(mNoTooltipView); 237 assertFalse(hasTooltip(mNoTooltipView)); 238 239 injectLongClick(mNoTooltipView); 240 assertFalse(hasTooltip(mNoTooltipView)); 241 242 injectLongEnter(mNoTooltipView); 243 assertFalse(hasTooltip(mNoTooltipView)); 244 245 injectLongHoverMove(mNoTooltipView); 246 assertFalse(hasTooltip(mNoTooltipView)); 247 } 248 249 @Test testNoTooltipOnDisabledView()250 public void testNoTooltipOnDisabledView() throws Throwable { 251 mActivityRule.runOnUiThread(() -> mTooltipView.setEnabled(false)); 252 253 injectLongClick(mTooltipView); 254 assertFalse(hasTooltip(mTooltipView)); 255 256 injectLongEnter(mTooltipView); 257 assertFalse(hasTooltip(mTooltipView)); 258 259 injectLongHoverMove(mTooltipView); 260 assertFalse(hasTooltip(mTooltipView)); 261 } 262 263 @Test testUpdateOpenTooltip()264 public void testUpdateOpenTooltip() throws Throwable { 265 callPerformLongClick(mTooltipView); 266 assertTrue(hasTooltip(mTooltipView)); 267 268 setTooltipText(mTooltipView, "updated tooltip"); 269 assertTrue(hasTooltip(mTooltipView)); 270 271 setTooltipText(mTooltipView, null); 272 assertFalse(hasTooltip(mTooltipView)); 273 } 274 275 @Test testTooltipHidesOnActivityFocusChange()276 public void testTooltipHidesOnActivityFocusChange() throws Throwable { 277 callPerformLongClick(mTooltipView); 278 assertTrue(hasTooltip(mTooltipView)); 279 280 CtsActivity activity = mCtsActivityRule.launchActivity(null); 281 PollingCheck.waitFor(TIMEOUT_DELTA, () -> !mActivity.hasWindowFocus()); 282 assertFalse(hasTooltip(mTooltipView)); 283 activity.finish(); 284 } 285 286 @Test testTooltipHidesOnWindowFocusChange()287 public void testTooltipHidesOnWindowFocusChange() throws Throwable { 288 callPerformLongClick(mTooltipView); 289 assertTrue(hasTooltip(mTooltipView)); 290 291 // Show a context menu on another widget. 292 mActivity.registerForContextMenu(mNoTooltipView); 293 mActivityRule.runOnUiThread(() -> mNoTooltipView.showContextMenu(0, 0)); 294 295 PollingCheck.waitFor(TIMEOUT_DELTA, () -> !mTooltipView.hasWindowFocus()); 296 mInstrumentation.waitForIdleSync(); 297 assertFalse(hasTooltip(mTooltipView)); 298 } 299 300 // Tests for tooltips triggered by long click. 301 302 @Test testShortClickDoesNotShowTooltip()303 public void testShortClickDoesNotShowTooltip() throws Throwable { 304 injectShortClick(mTooltipView); 305 assertFalse(hasTooltip(mTooltipView)); 306 } 307 308 @Test testPerformLongClickShowsTooltipImmediately()309 public void testPerformLongClickShowsTooltipImmediately() throws Throwable { 310 callPerformLongClick(mTooltipView); 311 assertTrue(hasTooltip(mTooltipView)); 312 } 313 314 @Test testLongClickTooltipBlockedByLongClickListener()315 public void testLongClickTooltipBlockedByLongClickListener() throws Throwable { 316 mTooltipView.setOnLongClickListener(v -> true); 317 injectLongClick(mTooltipView); 318 assertFalse(hasTooltip(mTooltipView)); 319 } 320 321 @Test testLongClickTooltipBlockedByContextMenu()322 public void testLongClickTooltipBlockedByContextMenu() throws Throwable { 323 mActivity.registerForContextMenu(mTooltipView); 324 injectLongClick(mTooltipView); 325 assertFalse(hasTooltip(mTooltipView)); 326 } 327 328 @Test testLongClickTooltipOnNonClickableView()329 public void testLongClickTooltipOnNonClickableView() throws Throwable { 330 injectLongClick(mTooltipView); 331 assertTrue(hasTooltip(mTooltipView)); 332 } 333 334 @Test testLongClickTooltipOnClickableView()335 public void testLongClickTooltipOnClickableView() throws Throwable { 336 setClickable(mTooltipView); 337 injectLongClick(mTooltipView); 338 assertTrue(hasTooltip(mTooltipView)); 339 } 340 341 @Test testLongClickTooltipOnLongClickableView()342 public void testLongClickTooltipOnLongClickableView() throws Throwable { 343 setLongClickable(mTooltipView); 344 injectLongClick(mTooltipView); 345 assertTrue(hasTooltip(mTooltipView)); 346 } 347 348 @Test testLongClickTooltipOnContextClickableView()349 public void testLongClickTooltipOnContextClickableView() throws Throwable { 350 setContextClickable(mTooltipView); 351 injectLongClick(mTooltipView); 352 assertTrue(hasTooltip(mTooltipView)); 353 } 354 355 @Test testLongClickTooltipStaysOnMouseMove()356 public void testLongClickTooltipStaysOnMouseMove() throws Throwable { 357 injectLongClick(mTooltipView); 358 assertTrue(hasTooltip(mTooltipView)); 359 360 // Tooltip stays while the mouse moves over the widget. 361 injectHoverMove(mTooltipView); 362 assertTrue(hasTooltip(mTooltipView)); 363 364 // Long-click-triggered tooltip stays while the mouse to another widget. 365 injectHoverMove(mNoTooltipView); 366 assertTrue(hasTooltip(mTooltipView)); 367 } 368 369 @Test testLongClickTooltipHidesAfterUp()370 public void testLongClickTooltipHidesAfterUp() throws Throwable { 371 injectLongClick(mTooltipView); 372 assertTrue(hasTooltip(mTooltipView)); 373 374 // Long-click-triggered tooltip hides after ACTION_UP (with a delay). 375 waitOut(ViewConfiguration.getLongPressTooltipHideTimeout()); 376 assertFalse(hasTooltip(mTooltipView)); 377 } 378 379 @Test testLongClickTooltipHidesOnClick()380 public void testLongClickTooltipHidesOnClick() throws Throwable { 381 injectLongClick(mTooltipView); 382 assertTrue(hasTooltip(mTooltipView)); 383 384 injectShortClick(mTooltipView); 385 assertFalse(hasTooltip(mTooltipView)); 386 } 387 388 @Test testLongClickTooltipHidesOnClickElsewhere()389 public void testLongClickTooltipHidesOnClickElsewhere() throws Throwable { 390 injectLongClick(mTooltipView); 391 assertTrue(hasTooltip(mTooltipView)); 392 393 injectShortClick(mNoTooltipView); 394 assertFalse(hasTooltip(mTooltipView)); 395 } 396 397 @Test testLongClickTooltipHidesOnKey()398 public void testLongClickTooltipHidesOnKey() throws Throwable { 399 injectLongClick(mTooltipView); 400 assertTrue(hasTooltip(mTooltipView)); 401 402 injectArbitraryShortKeyPress(); 403 assertFalse(hasTooltip(mTooltipView)); 404 } 405 406 // Tests for tooltips triggered by long key press. 407 408 @Test testShortKeyPressDoesNotShowTooltip()409 public void testShortKeyPressDoesNotShowTooltip() throws Throwable { 410 injectKeyPress(null, KeyEvent.KEYCODE_ENTER, 0); 411 assertFalse(hasTooltip(mTooltipView)); 412 413 injectKeyPress(mTooltipView, KeyEvent.KEYCODE_ENTER, 0); 414 assertFalse(hasTooltip(mTooltipView)); 415 } 416 417 @Test testLongArbitraryKeyPressDoesNotShowTooltip()418 public void testLongArbitraryKeyPressDoesNotShowTooltip() throws Throwable { 419 injectLongKeyPress(mTooltipView, KeyEvent.KEYCODE_0); 420 assertFalse(hasTooltip(mTooltipView)); 421 } 422 423 @Test testLongKeyPressWithoutFocusDoesNotShowTooltip()424 public void testLongKeyPressWithoutFocusDoesNotShowTooltip() throws Throwable { 425 injectLongEnter(null); 426 assertFalse(hasTooltip(mTooltipView)); 427 } 428 429 @Test testLongKeyPressOnAnotherViewDoesNotShowTooltip()430 public void testLongKeyPressOnAnotherViewDoesNotShowTooltip() throws Throwable { 431 injectLongEnter(mNoTooltipView); 432 assertFalse(hasTooltip(mTooltipView)); 433 } 434 435 @Test testLongKeyPressTooltipOnNonClickableView()436 public void testLongKeyPressTooltipOnNonClickableView() throws Throwable { 437 injectLongEnter(mTooltipView); 438 assertTrue(hasTooltip(mTooltipView)); 439 } 440 441 @Test testLongKeyPressTooltipOnClickableView()442 public void testLongKeyPressTooltipOnClickableView() throws Throwable { 443 setClickable(mTooltipView); 444 injectLongEnter(mTooltipView); 445 assertTrue(hasTooltip(mTooltipView)); 446 } 447 448 @Test testLongKeyPressTooltipOnLongClickableView()449 public void testLongKeyPressTooltipOnLongClickableView() throws Throwable { 450 setLongClickable(mTooltipView); 451 injectLongEnter(mTooltipView); 452 assertTrue(hasTooltip(mTooltipView)); 453 } 454 455 @Test testLongKeyPressTooltipOnContextClickableView()456 public void testLongKeyPressTooltipOnContextClickableView() throws Throwable { 457 setContextClickable(mTooltipView); 458 injectLongEnter(mTooltipView); 459 assertTrue(hasTooltip(mTooltipView)); 460 } 461 462 @Test testLongKeyPressTooltipStaysOnMouseMove()463 public void testLongKeyPressTooltipStaysOnMouseMove() throws Throwable { 464 injectLongEnter(mTooltipView); 465 assertTrue(hasTooltip(mTooltipView)); 466 467 // Tooltip stays while the mouse moves over the widget. 468 injectHoverMove(mTooltipView); 469 assertTrue(hasTooltip(mTooltipView)); 470 471 // Long-keypress-triggered tooltip stays while the mouse to another widget. 472 injectHoverMove(mNoTooltipView); 473 assertTrue(hasTooltip(mTooltipView)); 474 } 475 476 @Test testLongKeyPressTooltipHidesAfterUp()477 public void testLongKeyPressTooltipHidesAfterUp() throws Throwable { 478 injectLongEnter(mTooltipView); 479 assertTrue(hasTooltip(mTooltipView)); 480 481 // Long-keypress-triggered tooltip hides after ACTION_UP (with a delay). 482 waitOut(ViewConfiguration.getLongPressTooltipHideTimeout()); 483 assertFalse(hasTooltip(mTooltipView)); 484 } 485 486 @Test testLongKeyPressTooltipHidesOnClick()487 public void testLongKeyPressTooltipHidesOnClick() throws Throwable { 488 injectLongEnter(mTooltipView); 489 assertTrue(hasTooltip(mTooltipView)); 490 491 injectShortClick(mTooltipView); 492 assertFalse(hasTooltip(mTooltipView)); 493 } 494 495 @Test testLongKeyPressTooltipHidesOnClickElsewhere()496 public void testLongKeyPressTooltipHidesOnClickElsewhere() throws Throwable { 497 injectLongEnter(mTooltipView); 498 assertTrue(hasTooltip(mTooltipView)); 499 500 injectShortClick(mNoTooltipView); 501 assertFalse(hasTooltip(mTooltipView)); 502 } 503 504 @Test testLongKeyPressTooltipHidesOnKey()505 public void testLongKeyPressTooltipHidesOnKey() throws Throwable { 506 injectLongEnter(mTooltipView); 507 assertTrue(hasTooltip(mTooltipView)); 508 509 injectArbitraryShortKeyPress(); 510 assertFalse(hasTooltip(mTooltipView)); 511 } 512 513 // Tests for tooltips triggered by mouse hover. 514 515 @Test testMouseClickDoesNotShowTooltip()516 public void testMouseClickDoesNotShowTooltip() throws Throwable { 517 injectMotionEvent(obtainMouseEvent(mTooltipView, MotionEvent.ACTION_DOWN, 0, 0)); 518 injectMotionEvent(obtainMouseEvent(mTooltipView, MotionEvent.ACTION_BUTTON_PRESS, 0, 0)); 519 injectMotionEvent(obtainMouseEvent(mTooltipView, MotionEvent.ACTION_BUTTON_RELEASE, 0, 0)); 520 injectMotionEvent(obtainMouseEvent(mTooltipView, MotionEvent.ACTION_UP, 0, 0)); 521 assertFalse(hasTooltip(mTooltipView)); 522 } 523 524 @Test testMouseHoverDoesNotShowTooltipImmediately()525 public void testMouseHoverDoesNotShowTooltipImmediately() throws Throwable { 526 injectHoverMove(mTooltipView, 0, 0); 527 assertFalse(hasTooltip(mTooltipView)); 528 529 injectHoverMove(mTooltipView, 1, 1); 530 assertFalse(hasTooltip(mTooltipView)); 531 532 injectHoverMove(mTooltipView, 2, 2); 533 assertFalse(hasTooltip(mTooltipView)); 534 } 535 536 @Test testMouseHoverExitCancelsPendingTooltip()537 public void testMouseHoverExitCancelsPendingTooltip() throws Throwable { 538 injectHoverMove(mTooltipView); 539 assertFalse(hasTooltip(mTooltipView)); 540 541 injectLongHoverMove(mNoTooltipView); 542 assertFalse(hasTooltip(mTooltipView)); 543 } 544 545 @Test testMouseHoverTooltipOnClickableView()546 public void testMouseHoverTooltipOnClickableView() throws Throwable { 547 setClickable(mTooltipView); 548 injectLongHoverMove(mTooltipView); 549 assertTrue(hasTooltip(mTooltipView)); 550 } 551 552 @Test testMouseHoverTooltipOnLongClickableView()553 public void testMouseHoverTooltipOnLongClickableView() throws Throwable { 554 setLongClickable(mTooltipView); 555 injectLongHoverMove(mTooltipView); 556 assertTrue(hasTooltip(mTooltipView)); 557 } 558 559 @Test testMouseHoverTooltipOnContextClickableView()560 public void testMouseHoverTooltipOnContextClickableView() throws Throwable { 561 setContextClickable(mTooltipView); 562 injectLongHoverMove(mTooltipView); 563 assertTrue(hasTooltip(mTooltipView)); 564 } 565 566 @Test testMouseHoverTooltipStaysOnMouseMove()567 public void testMouseHoverTooltipStaysOnMouseMove() throws Throwable { 568 injectLongHoverMove(mTooltipView); 569 assertTrue(hasTooltip(mTooltipView)); 570 571 // Tooltip stays while the mouse moves over the widget. 572 injectHoverMove(mTooltipView, 1, 1); 573 assertTrue(hasTooltip(mTooltipView)); 574 575 injectHoverMove(mTooltipView, 2, 2); 576 assertTrue(hasTooltip(mTooltipView)); 577 } 578 579 @Test testMouseHoverTooltipHidesOnExit()580 public void testMouseHoverTooltipHidesOnExit() throws Throwable { 581 injectLongHoverMove(mTooltipView); 582 assertTrue(hasTooltip(mTooltipView)); 583 584 // Tooltip hides once the mouse moves out of the widget. 585 injectHoverMove(mNoTooltipView); 586 assertFalse(hasTooltip(mTooltipView)); 587 } 588 589 @Test testMouseHoverTooltipHidesOnClick()590 public void testMouseHoverTooltipHidesOnClick() throws Throwable { 591 injectLongHoverMove(mTooltipView); 592 assertTrue(hasTooltip(mTooltipView)); 593 594 injectShortClick(mTooltipView); 595 assertFalse(hasTooltip(mTooltipView)); 596 } 597 598 @Test testMouseHoverTooltipHidesOnClickOnElsewhere()599 public void testMouseHoverTooltipHidesOnClickOnElsewhere() throws Throwable { 600 injectLongHoverMove(mTooltipView); 601 assertTrue(hasTooltip(mTooltipView)); 602 603 injectShortClick(mNoTooltipView); 604 assertFalse(hasTooltip(mTooltipView)); 605 } 606 607 @Test testMouseHoverTooltipHidesOnKey()608 public void testMouseHoverTooltipHidesOnKey() throws Throwable { 609 injectLongHoverMove(mTooltipView); 610 assertTrue(hasTooltip(mTooltipView)); 611 612 injectArbitraryShortKeyPress(); 613 assertFalse(hasTooltip(mTooltipView)); 614 } 615 616 @Test testMouseHoverTooltipHidesOnTimeout()617 public void testMouseHoverTooltipHidesOnTimeout() throws Throwable { 618 injectLongHoverMove(mTooltipView); 619 assertTrue(hasTooltip(mTooltipView)); 620 621 waitOut(ViewConfiguration.getHoverTooltipHideTimeout()); 622 assertFalse(hasTooltip(mTooltipView)); 623 } 624 625 @Test testMouseHoverTooltipHidesOnShortTimeout()626 public void testMouseHoverTooltipHidesOnShortTimeout() throws Throwable { 627 requestLowProfileSystemUi(); 628 629 injectLongHoverMove(mTooltipView); 630 assertTrue(hasTooltip(mTooltipView)); 631 632 waitOut(ViewConfiguration.getHoverTooltipHideShortTimeout()); 633 assertFalse(hasTooltip(mTooltipView)); 634 } 635 636 @Test testMouseHoverTooltipWithHoverListener()637 public void testMouseHoverTooltipWithHoverListener() throws Throwable { 638 mTooltipView.setOnHoverListener((v, event) -> true); 639 injectLongHoverMove(mTooltipView); 640 assertTrue(hasTooltip(mTooltipView)); 641 } 642 643 @Test testMouseHoverTooltipUnsetWhileHovering()644 public void testMouseHoverTooltipUnsetWhileHovering() throws Throwable { 645 injectHoverMove(mTooltipView); 646 setTooltipText(mTooltipView, null); 647 waitOut(ViewConfiguration.getHoverTooltipShowTimeout()); 648 assertFalse(hasTooltip(mTooltipView)); 649 } 650 651 @Test testMouseHoverTooltipDisableWhileHovering()652 public void testMouseHoverTooltipDisableWhileHovering() throws Throwable { 653 injectHoverMove(mTooltipView); 654 mActivityRule.runOnUiThread(() -> mTooltipView.setEnabled(false)); 655 waitOut(ViewConfiguration.getHoverTooltipShowTimeout()); 656 assertFalse(hasTooltip(mTooltipView)); 657 } 658 659 @Test testMouseHoverTooltipFromParent()660 public void testMouseHoverTooltipFromParent() throws Throwable { 661 // Hover listeners should not interfere with tooltip dispatch. 662 mNoTooltipView.setOnHoverListener((v, event) -> true); 663 mTooltipView.setOnHoverListener((v, event) -> true); 664 665 setTooltipText(mTopmostView, "tooltip"); 666 667 // Hover over a child with a tooltip works normally. 668 injectLongHoverMove(mTooltipView); 669 assertFalse(hasTooltip(mTopmostView)); 670 assertTrue(hasTooltip(mTooltipView)); 671 injectShortClick(mTopmostView); 672 assertFalse(hasTooltip(mTooltipView)); 673 674 // Hover over a child with no tooltip triggers a tooltip on its parent. 675 injectLongHoverMove(mNoTooltipView2); 676 assertFalse(hasTooltip(mNoTooltipView2)); 677 assertTrue(hasTooltip(mTopmostView)); 678 injectShortClick(mTopmostView); 679 assertFalse(hasTooltip(mTopmostView)); 680 681 // Same but the child is and empty view group. 682 injectLongHoverMove(mEmptyGroup); 683 assertFalse(hasTooltip(mEmptyGroup)); 684 assertTrue(hasTooltip(mTopmostView)); 685 injectShortClick(mTopmostView); 686 assertFalse(hasTooltip(mTopmostView)); 687 688 // Hover over a grandchild with no tooltip triggers a tooltip on its grandparent. 689 injectLongHoverMove(mNoTooltipView); 690 assertFalse(hasTooltip(mNoTooltipView)); 691 assertTrue(hasTooltip(mTopmostView)); 692 // Move to another child one level up, the tooltip stays. 693 injectHoverMove(mNoTooltipView2); 694 assertTrue(hasTooltip(mTopmostView)); 695 injectShortClick(mTopmostView); 696 assertFalse(hasTooltip(mTopmostView)); 697 698 // Set a tooltip on the intermediate parent, now it is showing tooltips. 699 setTooltipText(mGroupView, "tooltip"); 700 injectLongHoverMove(mNoTooltipView); 701 assertFalse(hasTooltip(mNoTooltipView)); 702 assertFalse(hasTooltip(mTopmostView)); 703 assertTrue(hasTooltip(mGroupView)); 704 705 // Move out of this group, the tooltip is now back on the grandparent. 706 injectLongHoverMove(mNoTooltipView2); 707 assertFalse(hasTooltip(mGroupView)); 708 assertTrue(hasTooltip(mTopmostView)); 709 injectShortClick(mTopmostView); 710 assertFalse(hasTooltip(mTopmostView)); 711 } 712 713 @Test testMouseHoverTooltipRemoveWhileWaiting()714 public void testMouseHoverTooltipRemoveWhileWaiting() throws Throwable { 715 // Remove the view while hovering. 716 injectHoverMove(mTooltipView); 717 removeView(mTooltipView); 718 waitOut(ViewConfiguration.getHoverTooltipShowTimeout()); 719 assertFalse(hasTooltip(mTooltipView)); 720 addView(mGroupView, mTooltipView); 721 722 // Remove and re-add the view while hovering. 723 injectHoverMove(mTooltipView); 724 removeView(mTooltipView); 725 addView(mGroupView, mTooltipView); 726 waitOut(ViewConfiguration.getHoverTooltipShowTimeout()); 727 assertFalse(hasTooltip(mTooltipView)); 728 729 // Remove the view's parent while hovering. 730 injectHoverMove(mTooltipView); 731 removeView(mGroupView); 732 waitOut(ViewConfiguration.getHoverTooltipShowTimeout()); 733 assertFalse(hasTooltip(mTooltipView)); 734 addView(mTopmostView, mGroupView); 735 736 // Remove and re-add view's parent while hovering. 737 injectHoverMove(mTooltipView); 738 removeView(mGroupView); 739 addView(mTopmostView, mGroupView); 740 waitOut(ViewConfiguration.getHoverTooltipShowTimeout()); 741 assertFalse(hasTooltip(mTooltipView)); 742 } 743 744 @Test testMouseHoverTooltipRemoveWhileShowing()745 public void testMouseHoverTooltipRemoveWhileShowing() throws Throwable { 746 // Remove the view while showing the tooltip. 747 injectLongHoverMove(mTooltipView); 748 assertTrue(hasTooltip(mTooltipView)); 749 removeView(mTooltipView); 750 assertFalse(hasTooltip(mTooltipView)); 751 addView(mGroupView, mTooltipView); 752 assertFalse(hasTooltip(mTooltipView)); 753 754 // Remove the view's parent while showing the tooltip. 755 injectLongHoverMove(mTooltipView); 756 assertTrue(hasTooltip(mTooltipView)); 757 removeView(mGroupView); 758 assertFalse(hasTooltip(mTooltipView)); 759 addView(mTopmostView, mGroupView); 760 assertFalse(hasTooltip(mTooltipView)); 761 } 762 763 @Test testMouseHoverOverlap()764 public void testMouseHoverOverlap() throws Throwable { 765 final View parent = mActivity.findViewById(R.id.overlap_group); 766 final View child1 = mActivity.findViewById(R.id.overlap1); 767 final View child2 = mActivity.findViewById(R.id.overlap2); 768 final View child3 = mActivity.findViewById(R.id.overlap3); 769 770 injectLongHoverMove(parent); 771 assertTrue(hasTooltip(child3)); 772 773 setVisibility(child3, View.GONE); 774 injectLongHoverMove(parent); 775 assertTrue(hasTooltip(child2)); 776 777 setTooltipText(child2, null); 778 injectLongHoverMove(parent); 779 assertTrue(hasTooltip(child1)); 780 781 setVisibility(child1, View.INVISIBLE); 782 injectLongHoverMove(parent); 783 assertTrue(hasTooltip(parent)); 784 } 785 } 786