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.widget.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertSame; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.Matchers.anyInt; 25 import static org.mockito.Mockito.any; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.never; 28 import static org.mockito.Mockito.times; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 32 import android.app.Instrumentation; 33 import android.content.Context; 34 import android.content.pm.ActivityInfo; 35 import android.content.res.Configuration; 36 import android.graphics.Color; 37 import android.graphics.Rect; 38 import android.graphics.drawable.ColorDrawable; 39 import android.graphics.drawable.Drawable; 40 import android.os.SystemClock; 41 import android.support.test.InstrumentationRegistry; 42 import android.support.test.annotation.UiThreadTest; 43 import android.support.test.filters.SmallTest; 44 import android.support.test.rule.ActivityTestRule; 45 import android.support.test.runner.AndroidJUnit4; 46 import android.transition.Transition; 47 import android.transition.Transition.TransitionListener; 48 import android.transition.TransitionValues; 49 import android.util.AttributeSet; 50 import android.view.Display; 51 import android.view.Gravity; 52 import android.view.MotionEvent; 53 import android.view.View; 54 import android.view.View.OnTouchListener; 55 import android.view.ViewGroup; 56 import android.view.ViewGroup.LayoutParams; 57 import android.view.WindowManager; 58 import android.widget.ImageView; 59 import android.widget.PopupWindow; 60 import android.widget.PopupWindow.OnDismissListener; 61 import android.widget.TextView; 62 63 import com.android.compatibility.common.util.WidgetTestUtils; 64 65 import org.junit.Before; 66 import org.junit.Rule; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 70 @SmallTest 71 @RunWith(AndroidJUnit4.class) 72 public class PopupWindowTest { 73 private static final int WINDOW_SIZE_DP = 50; 74 private static final int CONTENT_SIZE_DP = 30; 75 76 private Instrumentation mInstrumentation; 77 private PopupWindowCtsActivity mActivity; 78 private PopupWindow mPopupWindow; 79 private TextView mTextView; 80 81 @Rule 82 public ActivityTestRule<PopupWindowCtsActivity> mActivityRule = 83 new ActivityTestRule<>(PopupWindowCtsActivity.class); 84 85 @Before 86 public void setup() { 87 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 88 mActivity = mActivityRule.getActivity(); 89 } 90 91 @Test 92 public void testConstructor() { 93 new PopupWindow(mActivity); 94 95 new PopupWindow(mActivity, null); 96 97 new PopupWindow(mActivity, null, android.R.attr.popupWindowStyle); 98 99 new PopupWindow(mActivity, null, 0, android.R.style.Widget_DeviceDefault_PopupWindow); 100 101 new PopupWindow(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_PopupWindow); 102 103 new PopupWindow(mActivity, null, 0, android.R.style.Widget_Material_PopupWindow); 104 105 new PopupWindow(mActivity, null, 0, android.R.style.Widget_Material_Light_PopupWindow); 106 } 107 108 @UiThreadTest 109 @Test 110 public void testSize() { 111 mPopupWindow = new PopupWindow(); 112 assertEquals(0, mPopupWindow.getWidth()); 113 assertEquals(0, mPopupWindow.getHeight()); 114 115 mPopupWindow = new PopupWindow(50, 50); 116 assertEquals(50, mPopupWindow.getWidth()); 117 assertEquals(50, mPopupWindow.getHeight()); 118 119 mPopupWindow = new PopupWindow(-1, -1); 120 assertEquals(-1, mPopupWindow.getWidth()); 121 assertEquals(-1, mPopupWindow.getHeight()); 122 123 TextView contentView = new TextView(mActivity); 124 mPopupWindow = new PopupWindow(contentView); 125 assertSame(contentView, mPopupWindow.getContentView()); 126 127 mPopupWindow = new PopupWindow(contentView, 0, 0); 128 assertEquals(0, mPopupWindow.getWidth()); 129 assertEquals(0, mPopupWindow.getHeight()); 130 assertSame(contentView, mPopupWindow.getContentView()); 131 132 mPopupWindow = new PopupWindow(contentView, 50, 50); 133 assertEquals(50, mPopupWindow.getWidth()); 134 assertEquals(50, mPopupWindow.getHeight()); 135 assertSame(contentView, mPopupWindow.getContentView()); 136 137 mPopupWindow = new PopupWindow(contentView, -1, -1); 138 assertEquals(-1, mPopupWindow.getWidth()); 139 assertEquals(-1, mPopupWindow.getHeight()); 140 assertSame(contentView, mPopupWindow.getContentView()); 141 142 mPopupWindow = new PopupWindow(contentView, 0, 0, true); 143 assertEquals(0, mPopupWindow.getWidth()); 144 assertEquals(0, mPopupWindow.getHeight()); 145 assertSame(contentView, mPopupWindow.getContentView()); 146 assertTrue(mPopupWindow.isFocusable()); 147 148 mPopupWindow = new PopupWindow(contentView, 50, 50, false); 149 assertEquals(50, mPopupWindow.getWidth()); 150 assertEquals(50, mPopupWindow.getHeight()); 151 assertSame(contentView, mPopupWindow.getContentView()); 152 assertFalse(mPopupWindow.isFocusable()); 153 154 mPopupWindow = new PopupWindow(contentView, -1, -1, true); 155 assertEquals(-1, mPopupWindow.getWidth()); 156 assertEquals(-1, mPopupWindow.getHeight()); 157 assertSame(contentView, mPopupWindow.getContentView()); 158 assertTrue(mPopupWindow.isFocusable()); 159 } 160 161 @Test 162 public void testAccessEnterExitTransitions() { 163 PopupWindow w = new PopupWindow(mActivity, null, 0, 0); 164 assertNull(w.getEnterTransition()); 165 assertNull(w.getExitTransition()); 166 167 w = new PopupWindow(mActivity, null, 0, R.style.PopupWindow_NullTransitions); 168 assertNull(w.getEnterTransition()); 169 assertNull(w.getExitTransition()); 170 171 w = new PopupWindow(mActivity, null, 0, R.style.PopupWindow_CustomTransitions); 172 assertTrue(w.getEnterTransition() instanceof CustomTransition); 173 assertTrue(w.getExitTransition() instanceof CustomTransition); 174 175 Transition enterTransition = new CustomTransition(); 176 Transition exitTransition = new CustomTransition(); 177 w = new PopupWindow(mActivity, null, 0, 0); 178 w.setEnterTransition(enterTransition); 179 w.setExitTransition(exitTransition); 180 assertEquals(enterTransition, w.getEnterTransition()); 181 assertEquals(exitTransition, w.getExitTransition()); 182 183 w.setEnterTransition(null); 184 w.setExitTransition(null); 185 assertNull(w.getEnterTransition()); 186 assertNull(w.getExitTransition()); 187 } 188 189 public static class CustomTransition extends Transition { 190 public CustomTransition() { 191 } 192 193 // This constructor is needed for reflection-based creation of a transition when 194 // the transition is defined in layout XML via attribute. 195 @SuppressWarnings("unused") 196 public CustomTransition(Context context, AttributeSet attrs) { 197 super(context, attrs); 198 } 199 200 @Override 201 public void captureStartValues(TransitionValues transitionValues) {} 202 203 @Override 204 public void captureEndValues(TransitionValues transitionValues) {} 205 } 206 207 @Test 208 public void testAccessBackground() { 209 mPopupWindow = new PopupWindow(mActivity); 210 211 Drawable drawable = new ColorDrawable(); 212 mPopupWindow.setBackgroundDrawable(drawable); 213 assertSame(drawable, mPopupWindow.getBackground()); 214 215 mPopupWindow.setBackgroundDrawable(null); 216 assertNull(mPopupWindow.getBackground()); 217 } 218 219 @Test 220 public void testAccessAnimationStyle() { 221 mPopupWindow = new PopupWindow(mActivity); 222 // default is -1 223 assertEquals(-1, mPopupWindow.getAnimationStyle()); 224 225 mPopupWindow.setAnimationStyle(android.R.style.Animation_Toast); 226 assertEquals(android.R.style.Animation_Toast, 227 mPopupWindow.getAnimationStyle()); 228 229 // abnormal values 230 mPopupWindow.setAnimationStyle(-100); 231 assertEquals(-100, mPopupWindow.getAnimationStyle()); 232 } 233 234 @Test 235 public void testAccessContentView() throws Throwable { 236 mPopupWindow = new PopupWindow(mActivity); 237 assertNull(mPopupWindow.getContentView()); 238 239 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 240 mInstrumentation.waitForIdleSync(); 241 mPopupWindow.setContentView(mTextView); 242 assertSame(mTextView, mPopupWindow.getContentView()); 243 244 mPopupWindow.setContentView(null); 245 assertNull(mPopupWindow.getContentView()); 246 247 // can not set the content if the old content is shown 248 mPopupWindow.setContentView(mTextView); 249 assertFalse(mPopupWindow.isShowing()); 250 showPopup(); 251 ImageView img = new ImageView(mActivity); 252 assertTrue(mPopupWindow.isShowing()); 253 mPopupWindow.setContentView(img); 254 assertSame(mTextView, mPopupWindow.getContentView()); 255 dismissPopup(); 256 } 257 258 @Test 259 public void testAccessFocusable() { 260 mPopupWindow = new PopupWindow(mActivity); 261 assertFalse(mPopupWindow.isFocusable()); 262 263 mPopupWindow.setFocusable(true); 264 assertTrue(mPopupWindow.isFocusable()); 265 266 mPopupWindow.setFocusable(false); 267 assertFalse(mPopupWindow.isFocusable()); 268 } 269 270 @Test 271 public void testAccessHeight() { 272 mPopupWindow = new PopupWindow(mActivity); 273 assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getHeight()); 274 275 int height = getDisplay().getHeight() / 2; 276 mPopupWindow.setHeight(height); 277 assertEquals(height, mPopupWindow.getHeight()); 278 279 height = getDisplay().getHeight(); 280 mPopupWindow.setHeight(height); 281 assertEquals(height, mPopupWindow.getHeight()); 282 283 mPopupWindow.setHeight(0); 284 assertEquals(0, mPopupWindow.getHeight()); 285 286 height = getDisplay().getHeight() * 2; 287 mPopupWindow.setHeight(height); 288 assertEquals(height, mPopupWindow.getHeight()); 289 290 height = -getDisplay().getHeight() / 2; 291 mPopupWindow.setHeight(height); 292 assertEquals(height, mPopupWindow.getHeight()); 293 } 294 295 /** 296 * Gets the display. 297 * 298 * @return the display 299 */ 300 private Display getDisplay() { 301 WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE); 302 return wm.getDefaultDisplay(); 303 } 304 305 @Test 306 public void testAccessWidth() { 307 mPopupWindow = new PopupWindow(mActivity); 308 assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getWidth()); 309 310 int width = getDisplay().getWidth() / 2; 311 mPopupWindow.setWidth(width); 312 assertEquals(width, mPopupWindow.getWidth()); 313 314 width = getDisplay().getWidth(); 315 mPopupWindow.setWidth(width); 316 assertEquals(width, mPopupWindow.getWidth()); 317 318 mPopupWindow.setWidth(0); 319 assertEquals(0, mPopupWindow.getWidth()); 320 321 width = getDisplay().getWidth() * 2; 322 mPopupWindow.setWidth(width); 323 assertEquals(width, mPopupWindow.getWidth()); 324 325 width = - getDisplay().getWidth() / 2; 326 mPopupWindow.setWidth(width); 327 assertEquals(width, mPopupWindow.getWidth()); 328 } 329 330 private static final int TOP = 0x00; 331 private static final int BOTTOM = 0x01; 332 333 private static final int LEFT = 0x00; 334 private static final int RIGHT = 0x01; 335 336 private static final int GREATER_THAN = 1; 337 private static final int LESS_THAN = -1; 338 private static final int EQUAL_TO = 0; 339 340 @Test 341 public void testShowAsDropDown() throws Throwable { 342 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 343 CONTENT_SIZE_DP)); 344 popup.setClipToScreenEnabled(false); 345 popup.setOverlapAnchor(false); 346 popup.setAnimationStyle(0); 347 popup.setExitTransition(null); 348 popup.setEnterTransition(null); 349 350 verifyPosition(popup, R.id.anchor_upper_left, 351 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 352 verifyPosition(popup, R.id.anchor_upper, 353 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 354 verifyPosition(popup, R.id.anchor_upper_right, 355 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 356 357 verifyPosition(popup, R.id.anchor_middle_left, 358 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 359 verifyPosition(popup, R.id.anchor_middle, 360 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 361 verifyPosition(popup, R.id.anchor_middle_right, 362 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 363 364 verifyPosition(popup, R.id.anchor_lower_left, 365 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 366 verifyPosition(popup, R.id.anchor_lower, 367 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 368 verifyPosition(popup, R.id.anchor_lower_right, 369 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 370 } 371 372 @Test 373 public void testShowAsDropDown_ClipToScreen() throws Throwable { 374 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 375 CONTENT_SIZE_DP)); 376 popup.setClipToScreenEnabled(true); 377 popup.setOverlapAnchor(false); 378 popup.setAnimationStyle(0); 379 popup.setExitTransition(null); 380 popup.setEnterTransition(null); 381 382 verifyPosition(popup, R.id.anchor_upper_left, 383 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 384 verifyPosition(popup, R.id.anchor_upper, 385 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 386 verifyPosition(popup, R.id.anchor_upper_right, 387 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 388 389 verifyPosition(popup, R.id.anchor_middle_left, 390 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 391 verifyPosition(popup, R.id.anchor_middle, 392 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 393 verifyPosition(popup, R.id.anchor_middle_right, 394 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 395 396 verifyPosition(popup, R.id.anchor_lower_left, 397 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 398 verifyPosition(popup, R.id.anchor_lower, 399 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 400 verifyPosition(popup, R.id.anchor_lower_right, 401 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 402 } 403 404 @Test 405 public void testShowAsDropDown_ClipToScreen_Overlap() throws Throwable { 406 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 407 CONTENT_SIZE_DP)); 408 popup.setClipToScreenEnabled(true); 409 popup.setOverlapAnchor(true); 410 popup.setAnimationStyle(0); 411 popup.setExitTransition(null); 412 popup.setEnterTransition(null); 413 414 verifyPosition(popup, R.id.anchor_upper_left, 415 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 416 verifyPosition(popup, R.id.anchor_upper, 417 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 418 verifyPosition(popup, R.id.anchor_upper_right, 419 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 420 421 verifyPosition(popup, R.id.anchor_middle_left, 422 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 423 verifyPosition(popup, R.id.anchor_middle, 424 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 425 verifyPosition(popup, R.id.anchor_middle_right, 426 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 427 428 verifyPosition(popup, R.id.anchor_lower_left, 429 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 430 verifyPosition(popup, R.id.anchor_lower, 431 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 432 verifyPosition(popup, R.id.anchor_lower_right, 433 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 434 } 435 436 @Test 437 public void testShowAsDropDown_ClipToScreen_Overlap_Offset() throws Throwable { 438 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 439 CONTENT_SIZE_DP)); 440 popup.setClipToScreenEnabled(true); 441 popup.setOverlapAnchor(true); 442 popup.setAnimationStyle(0); 443 popup.setExitTransition(null); 444 popup.setEnterTransition(null); 445 446 final int offsetX = mActivity.findViewById(R.id.anchor_upper).getWidth() / 2; 447 final int offsetY = mActivity.findViewById(R.id.anchor_upper).getHeight() / 2; 448 final int gravity = Gravity.TOP | Gravity.START; 449 450 verifyPosition(popup, R.id.anchor_upper_left, 451 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 452 offsetX, offsetY, gravity); 453 verifyPosition(popup, R.id.anchor_upper, 454 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 455 offsetX, offsetY, gravity); 456 verifyPosition(popup, R.id.anchor_upper_right, 457 RIGHT, EQUAL_TO, RIGHT, TOP, GREATER_THAN, TOP, 458 offsetX, offsetY, gravity); 459 460 verifyPosition(popup, R.id.anchor_middle_left, 461 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 462 offsetX, offsetY, gravity); 463 verifyPosition(popup, R.id.anchor_middle, 464 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 465 offsetX, offsetY, gravity); 466 verifyPosition(popup, R.id.anchor_middle_right, 467 RIGHT, EQUAL_TO, RIGHT, TOP, GREATER_THAN, TOP, 468 offsetX, offsetY, gravity); 469 470 verifyPosition(popup, R.id.anchor_lower_left, 471 LEFT, GREATER_THAN, LEFT, BOTTOM, LESS_THAN, BOTTOM, 472 offsetX, offsetY, gravity); 473 verifyPosition(popup, R.id.anchor_lower, 474 LEFT, GREATER_THAN, LEFT, BOTTOM, LESS_THAN, BOTTOM, 475 offsetX, offsetY, gravity); 476 verifyPosition(popup, R.id.anchor_lower_right, 477 RIGHT, EQUAL_TO, RIGHT, BOTTOM, LESS_THAN, BOTTOM, 478 offsetX, offsetY, gravity); 479 } 480 481 @Test 482 public void testShowAsDropDown_ClipToScreen_TooBig() throws Throwable { 483 final View rootView = mActivity.findViewById(R.id.anchor_upper_left).getRootView(); 484 final int width = rootView.getWidth() * 2; 485 final int height = rootView.getHeight() * 2; 486 487 final PopupWindow popup = createPopupWindow(createPopupContent(width, height)); 488 popup.setWidth(width); 489 popup.setHeight(height); 490 491 popup.setClipToScreenEnabled(true); 492 popup.setOverlapAnchor(false); 493 popup.setAnimationStyle(0); 494 popup.setExitTransition(null); 495 popup.setEnterTransition(null); 496 497 verifyPosition(popup, R.id.anchor_upper_left, 498 LEFT, EQUAL_TO, LEFT, TOP, LESS_THAN, TOP); 499 verifyPosition(popup, R.id.anchor_upper, 500 LEFT, LESS_THAN, LEFT, TOP, LESS_THAN, TOP); 501 verifyPosition(popup, R.id.anchor_upper_right, 502 RIGHT, EQUAL_TO, RIGHT, TOP, LESS_THAN, TOP); 503 504 verifyPosition(popup, R.id.anchor_middle_left, 505 LEFT, EQUAL_TO, LEFT, TOP, LESS_THAN, TOP); 506 verifyPosition(popup, R.id.anchor_middle, 507 LEFT, LESS_THAN, LEFT, TOP, LESS_THAN, TOP); 508 verifyPosition(popup, R.id.anchor_middle_right, 509 RIGHT, EQUAL_TO, RIGHT, TOP, LESS_THAN, TOP); 510 511 verifyPosition(popup, R.id.anchor_lower_left, 512 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, BOTTOM); 513 verifyPosition(popup, R.id.anchor_lower, 514 LEFT, LESS_THAN, LEFT, BOTTOM, EQUAL_TO, BOTTOM); 515 verifyPosition(popup, R.id.anchor_lower_right, 516 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, BOTTOM); 517 } 518 519 private void verifyPosition(PopupWindow popup, int anchorId, 520 int contentEdgeX, int operatorX, int anchorEdgeX, 521 int contentEdgeY, int operatorY, int anchorEdgeY) throws Throwable { 522 verifyPosition(popup, mActivity.findViewById(anchorId), 523 contentEdgeX, operatorX, anchorEdgeX, 524 contentEdgeY, operatorY, anchorEdgeY, 525 0, 0, Gravity.TOP | Gravity.START); 526 } 527 528 private void verifyPosition(PopupWindow popup, int anchorId, 529 int contentEdgeX, int operatorX, int anchorEdgeX, 530 int contentEdgeY, int operatorY, int anchorEdgeY, 531 int offsetX, int offsetY, int gravity) throws Throwable { 532 verifyPosition(popup, mActivity.findViewById(anchorId), 533 contentEdgeX, operatorX, anchorEdgeX, 534 contentEdgeY, operatorY, anchorEdgeY, offsetX, offsetY, gravity); 535 } 536 537 private void verifyPosition(PopupWindow popup, View anchor, 538 int contentEdgeX, int operatorX, int anchorEdgeX, 539 int contentEdgeY, int operatorY, int anchorEdgeY) throws Throwable { 540 verifyPosition(popup, anchor, 541 contentEdgeX, operatorX, anchorEdgeX, 542 contentEdgeY, operatorY, anchorEdgeY, 543 0, 0, Gravity.TOP | Gravity.START); 544 } 545 546 private void verifyPosition(PopupWindow popup, View anchor, 547 int contentEdgeX, int operatorX, int anchorEdgeX, 548 int contentEdgeY, int operatorY, int anchorEdgeY, 549 int offsetX, int offsetY, int gravity) throws Throwable { 550 final View content = popup.getContentView(); 551 552 mActivityRule.runOnUiThread(() -> popup.showAsDropDown( 553 anchor, offsetX, offsetY, gravity)); 554 mInstrumentation.waitForIdleSync(); 555 556 assertTrue(popup.isShowing()); 557 verifyPositionX(content, contentEdgeX, operatorX, anchor, anchorEdgeX); 558 verifyPositionY(content, contentEdgeY, operatorY, anchor, anchorEdgeY); 559 560 // Make sure it fits in the display frame. 561 final Rect displayFrame = new Rect(); 562 anchor.getWindowVisibleDisplayFrame(displayFrame); 563 final Rect contentFrame = new Rect(); 564 content.getBoundsOnScreen(contentFrame); 565 assertTrue("Content (" + contentFrame + ") extends outside display (" 566 + displayFrame + ")", displayFrame.contains(contentFrame)); 567 568 mActivityRule.runOnUiThread(popup::dismiss); 569 mInstrumentation.waitForIdleSync(); 570 571 assertFalse(popup.isShowing()); 572 } 573 574 private void verifyPositionY(View content, int contentEdge, int flags, 575 View anchor, int anchorEdge) { 576 final int[] anchorOnScreenXY = new int[2]; 577 anchor.getLocationOnScreen(anchorOnScreenXY); 578 int anchorY = anchorOnScreenXY[1]; 579 if ((anchorEdge & BOTTOM) == BOTTOM) { 580 anchorY += anchor.getHeight(); 581 } 582 583 final int[] contentOnScreenXY = new int[2]; 584 content.getLocationOnScreen(contentOnScreenXY); 585 int contentY = contentOnScreenXY[1]; 586 if ((contentEdge & BOTTOM) == BOTTOM) { 587 contentY += content.getHeight(); 588 } 589 590 assertComparison(contentY, flags, anchorY); 591 } 592 593 private void verifyPositionX(View content, int contentEdge, int flags, 594 View anchor, int anchorEdge) { 595 final int[] anchorOnScreenXY = new int[2]; 596 anchor.getLocationOnScreen(anchorOnScreenXY); 597 int anchorX = anchorOnScreenXY[0]; 598 if ((anchorEdge & RIGHT) == RIGHT) { 599 anchorX += anchor.getWidth(); 600 } 601 602 final int[] contentOnScreenXY = new int[2]; 603 content.getLocationOnScreen(contentOnScreenXY); 604 int contentX = contentOnScreenXY[0]; 605 if ((contentEdge & RIGHT) == RIGHT) { 606 contentX += content.getWidth(); 607 } 608 609 assertComparison(contentX, flags, anchorX); 610 } 611 612 private void assertComparison(int left, int operator, int right) { 613 switch (operator) { 614 case GREATER_THAN: 615 assertTrue(left + " <= " + right, left > right); 616 break; 617 case LESS_THAN: 618 assertTrue(left + " >= " + right, left < right); 619 break; 620 case EQUAL_TO: 621 assertTrue(left + " != " + right, left == right); 622 break; 623 } 624 } 625 626 @Test 627 public void testShowAtLocation() throws Throwable { 628 int[] popupContentViewInWindowXY = new int[2]; 629 int[] popupContentViewOnScreenXY = new int[2]; 630 Rect containingRect = new Rect(); 631 632 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 633 // Do not attach within the decor; we will be measuring location 634 // with regard to screen coordinates. 635 mPopupWindow.setAttachedInDecor(false); 636 assertFalse(mPopupWindow.isAttachedInDecor()); 637 638 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 639 640 final int xOff = 10; 641 final int yOff = 21; 642 assertFalse(mPopupWindow.isShowing()); 643 mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY); 644 assertEquals(0, popupContentViewInWindowXY[0]); 645 assertEquals(0, popupContentViewInWindowXY[1]); 646 647 mActivityRule.runOnUiThread( 648 () -> mPopupWindow.showAtLocation(upperAnchor, Gravity.NO_GRAVITY, xOff, yOff)); 649 mInstrumentation.waitForIdleSync(); 650 651 assertTrue(mPopupWindow.isShowing()); 652 mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY); 653 mPopupWindow.getContentView().getLocationOnScreen(popupContentViewOnScreenXY); 654 upperAnchor.getWindowDisplayFrame(containingRect); 655 656 assertTrue(popupContentViewInWindowXY[0] >= 0); 657 assertTrue(popupContentViewInWindowXY[1] >= 0); 658 assertEquals(containingRect.left + popupContentViewInWindowXY[0] + xOff, 659 popupContentViewOnScreenXY[0]); 660 assertEquals(containingRect.top + popupContentViewInWindowXY[1] + yOff, 661 popupContentViewOnScreenXY[1]); 662 663 dismissPopup(); 664 } 665 666 @Test 667 public void testShowAsDropDownWithOffsets() throws Throwable { 668 int[] anchorXY = new int[2]; 669 int[] viewOnScreenXY = new int[2]; 670 int[] viewInWindowXY = new int[2]; 671 672 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 673 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 674 upperAnchor.getLocationOnScreen(anchorXY); 675 int height = upperAnchor.getHeight(); 676 677 final int xOff = 11; 678 final int yOff = 12; 679 680 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(upperAnchor, xOff, yOff)); 681 mInstrumentation.waitForIdleSync(); 682 683 mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY); 684 mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY); 685 assertEquals(anchorXY[0] + xOff + viewInWindowXY[0], viewOnScreenXY[0]); 686 assertEquals(anchorXY[1] + height + yOff + viewInWindowXY[1], viewOnScreenXY[1]); 687 688 dismissPopup(); 689 } 690 691 @Test 692 public void testOverlapAnchor() throws Throwable { 693 int[] anchorXY = new int[2]; 694 int[] viewOnScreenXY = new int[2]; 695 int[] viewInWindowXY = new int[2]; 696 697 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 698 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 699 upperAnchor.getLocationOnScreen(anchorXY); 700 701 assertFalse(mPopupWindow.getOverlapAnchor()); 702 mPopupWindow.setOverlapAnchor(true); 703 assertTrue(mPopupWindow.getOverlapAnchor()); 704 705 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(upperAnchor, 0, 0)); 706 mInstrumentation.waitForIdleSync(); 707 708 mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY); 709 mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY); 710 assertEquals(anchorXY[0] + viewInWindowXY[0], viewOnScreenXY[0]); 711 assertEquals(anchorXY[1] + viewInWindowXY[1], viewOnScreenXY[1]); 712 } 713 714 @Test 715 public void testAccessWindowLayoutType() { 716 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 717 assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, 718 mPopupWindow.getWindowLayoutType()); 719 mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); 720 assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, 721 mPopupWindow.getWindowLayoutType()); 722 } 723 724 @Test 725 public void testGetMaxAvailableHeight() { 726 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 727 Rect displayFrame = new Rect(); 728 View anchorView = mActivity.findViewById(R.id.anchor_upper); 729 anchorView.getWindowVisibleDisplayFrame(displayFrame); 730 int displayHeight = displayFrame.height(); 731 int available = displayHeight - anchorView.getHeight(); 732 int maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView); 733 int maxAvailableHeightIgnoringBottomDecoration = 734 mPopupWindow.getMaxAvailableHeight(anchorView, 0, true); 735 assertTrue(maxAvailableHeight > 0); 736 assertTrue(maxAvailableHeight <= available); 737 assertTrue(maxAvailableHeightIgnoringBottomDecoration >= maxAvailableHeight); 738 assertTrue(maxAvailableHeightIgnoringBottomDecoration <= available); 739 740 int maxAvailableHeightWithOffset = mPopupWindow.getMaxAvailableHeight(anchorView, 2); 741 assertEquals(maxAvailableHeight - 2, maxAvailableHeightWithOffset); 742 743 maxAvailableHeightWithOffset = 744 mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight); 745 assertTrue(maxAvailableHeightWithOffset > 0); 746 assertTrue(maxAvailableHeightWithOffset <= available); 747 748 maxAvailableHeightWithOffset = 749 mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight / 2 - 1); 750 assertTrue(maxAvailableHeightWithOffset > 0); 751 assertTrue(maxAvailableHeightWithOffset <= available); 752 753 maxAvailableHeightWithOffset = mPopupWindow.getMaxAvailableHeight(anchorView, -1); 754 assertTrue(maxAvailableHeightWithOffset > 0); 755 assertTrue(maxAvailableHeightWithOffset <= available); 756 757 int maxAvailableHeightWithOffsetIgnoringBottomDecoration = 758 mPopupWindow.getMaxAvailableHeight(anchorView, 2, true); 759 assertEquals(maxAvailableHeightIgnoringBottomDecoration - 2, 760 maxAvailableHeightWithOffsetIgnoringBottomDecoration); 761 762 maxAvailableHeightWithOffsetIgnoringBottomDecoration = 763 mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight, true); 764 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration > 0); 765 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration <= available); 766 767 maxAvailableHeightWithOffsetIgnoringBottomDecoration = 768 mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight / 2 - 1, true); 769 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration > 0); 770 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration <= available); 771 772 maxAvailableHeightWithOffsetIgnoringBottomDecoration = 773 mPopupWindow.getMaxAvailableHeight(anchorView, -1, true); 774 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration > 0); 775 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration <= available); 776 777 anchorView = mActivity.findViewById(R.id.anchor_lower); 778 // On some devices the view might actually have larger size than the physical display 779 // due to chin and content will be laid out as if outside of the display. We need to use 780 // larger from the display height and the main view height. 781 available = Math.max(displayHeight, 782 mActivity.findViewById(android.R.id.content).getHeight()) - anchorView.getHeight(); 783 maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView); 784 assertTrue(maxAvailableHeight > 0); 785 assertTrue(maxAvailableHeight <= available); 786 787 anchorView = mActivity.findViewById(R.id.anchor_middle_left); 788 available = displayHeight - anchorView.getHeight() 789 - mActivity.findViewById(R.id.anchor_upper).getHeight(); 790 maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView); 791 assertTrue(maxAvailableHeight > 0); 792 assertTrue(maxAvailableHeight <= available); 793 } 794 795 @UiThreadTest 796 @Test 797 public void testDismiss() { 798 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 799 assertFalse(mPopupWindow.isShowing()); 800 View anchorView = mActivity.findViewById(R.id.anchor_upper); 801 mPopupWindow.showAsDropDown(anchorView); 802 803 mPopupWindow.dismiss(); 804 assertFalse(mPopupWindow.isShowing()); 805 806 mPopupWindow.dismiss(); 807 assertFalse(mPopupWindow.isShowing()); 808 } 809 810 @Test 811 public void testSetOnDismissListener() throws Throwable { 812 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 813 mInstrumentation.waitForIdleSync(); 814 mPopupWindow = new PopupWindow(mTextView); 815 mPopupWindow.setOnDismissListener(null); 816 817 OnDismissListener onDismissListener = mock(OnDismissListener.class); 818 mPopupWindow.setOnDismissListener(onDismissListener); 819 showPopup(); 820 dismissPopup(); 821 verify(onDismissListener, times(1)).onDismiss(); 822 823 showPopup(); 824 dismissPopup(); 825 verify(onDismissListener, times(2)).onDismiss(); 826 827 mPopupWindow.setOnDismissListener(null); 828 showPopup(); 829 dismissPopup(); 830 verify(onDismissListener, times(2)).onDismiss(); 831 } 832 833 @Test 834 public void testUpdate() throws Throwable { 835 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 836 mPopupWindow.setBackgroundDrawable(null); 837 showPopup(); 838 839 mPopupWindow.setIgnoreCheekPress(); 840 mPopupWindow.setFocusable(true); 841 mPopupWindow.setTouchable(false); 842 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 843 mPopupWindow.setClippingEnabled(false); 844 mPopupWindow.setOutsideTouchable(true); 845 846 WindowManager.LayoutParams p = (WindowManager.LayoutParams) 847 mPopupWindow.getContentView().getRootView().getLayoutParams(); 848 849 assertEquals(0, WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES & p.flags); 850 assertEquals(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 851 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE & p.flags); 852 assertEquals(0, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE & p.flags); 853 assertEquals(0, WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH & p.flags); 854 assertEquals(0, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS & p.flags); 855 assertEquals(0, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags); 856 857 mActivityRule.runOnUiThread(mPopupWindow::update); 858 mInstrumentation.waitForIdleSync(); 859 860 assertEquals(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES, 861 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES & p.flags); 862 assertEquals(0, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE & p.flags); 863 assertEquals(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, 864 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE & p.flags); 865 assertEquals(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, 866 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH & p.flags); 867 assertEquals(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, 868 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS & p.flags); 869 assertEquals(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 870 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags); 871 } 872 873 @Test 874 public void testEnterExitInterruption() throws Throwable { 875 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 876 verifyEnterExitTransition( 877 () -> mPopupWindow.showAsDropDown(anchorView, 0, 0), true); 878 } 879 880 @Test 881 public void testEnterExitTransitionAsDropDown() throws Throwable { 882 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 883 verifyEnterExitTransition( 884 () -> mPopupWindow.showAsDropDown(anchorView, 0, 0), false); 885 } 886 887 @Test 888 public void testEnterExitTransitionAtLocation() throws Throwable { 889 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 890 verifyEnterExitTransition( 891 () -> mPopupWindow.showAtLocation(anchorView, Gravity.BOTTOM, 0, 0), false); 892 } 893 894 private void verifyEnterExitTransition(Runnable showRunnable, boolean showAgain) 895 throws Throwable { 896 TransitionListener enterListener = mock(TransitionListener.class); 897 Transition enterTransition = new BaseTransition(); 898 enterTransition.addListener(enterListener); 899 900 TransitionListener exitListener = mock(TransitionListener.class); 901 Transition exitTransition = new BaseTransition(); 902 exitTransition.addListener(exitListener); 903 904 OnDismissListener dismissListener = mock(OnDismissListener.class); 905 906 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 907 mPopupWindow.setEnterTransition(enterTransition); 908 mPopupWindow.setExitTransition(exitTransition); 909 mPopupWindow.setOnDismissListener(dismissListener); 910 911 mActivityRule.runOnUiThread(showRunnable); 912 mInstrumentation.waitForIdleSync(); 913 verify(enterListener, times(1)).onTransitionStart(any(Transition.class)); 914 verify(exitListener, never()).onTransitionStart(any(Transition.class)); 915 verify(dismissListener, never()).onDismiss(); 916 917 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 918 919 int times; 920 if (showAgain) { 921 // Interrupt dismiss by calling show again, then actually dismiss. 922 mActivityRule.runOnUiThread(showRunnable); 923 mInstrumentation.waitForIdleSync(); 924 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 925 926 times = 2; 927 } else { 928 times = 1; 929 } 930 931 mInstrumentation.waitForIdleSync(); 932 verify(enterListener, times(times)).onTransitionStart(any(Transition.class)); 933 verify(exitListener, times(times)).onTransitionStart(any(Transition.class)); 934 verify(dismissListener, times(times)).onDismiss(); 935 } 936 937 @Test 938 public void testUpdatePositionAndDimension() throws Throwable { 939 int[] fstXY = new int[2]; 940 int[] sndXY = new int[2]; 941 int[] viewInWindowXY = new int[2]; 942 Rect containingRect = new Rect(); 943 944 mActivityRule.runOnUiThread(() -> { 945 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 946 // Do not attach within the decor; we will be measuring location 947 // with regard to screen coordinates. 948 mPopupWindow.setAttachedInDecor(false); 949 }); 950 951 mInstrumentation.waitForIdleSync(); 952 // Do not update if it is not shown 953 assertFalse(mPopupWindow.isShowing()); 954 assertFalse(mPopupWindow.isAttachedInDecor()); 955 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getWidth()); 956 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getHeight()); 957 958 showPopup(); 959 mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY); 960 final View containerView = mActivity.findViewById(R.id.main_container); 961 containerView.getWindowDisplayFrame(containingRect); 962 963 // update if it is not shown 964 mActivityRule.runOnUiThread(() -> mPopupWindow.update(80, 80)); 965 966 mInstrumentation.waitForIdleSync(); 967 assertTrue(mPopupWindow.isShowing()); 968 assertEquals(80, mPopupWindow.getWidth()); 969 assertEquals(80, mPopupWindow.getHeight()); 970 971 // update if it is not shown 972 mActivityRule.runOnUiThread(() -> mPopupWindow.update(20, 50, 50, 50)); 973 974 mInstrumentation.waitForIdleSync(); 975 assertTrue(mPopupWindow.isShowing()); 976 assertEquals(50, mPopupWindow.getWidth()); 977 assertEquals(50, mPopupWindow.getHeight()); 978 979 mPopupWindow.getContentView().getLocationOnScreen(fstXY); 980 assertEquals(containingRect.left + viewInWindowXY[0] + 20, fstXY[0]); 981 assertEquals(containingRect.top + viewInWindowXY[1] + 50, fstXY[1]); 982 983 // ignore if width or height is -1 984 mActivityRule.runOnUiThread(() -> mPopupWindow.update(4, 0, -1, -1, true)); 985 mInstrumentation.waitForIdleSync(); 986 987 assertTrue(mPopupWindow.isShowing()); 988 assertEquals(50, mPopupWindow.getWidth()); 989 assertEquals(50, mPopupWindow.getHeight()); 990 991 mPopupWindow.getContentView().getLocationOnScreen(sndXY); 992 assertEquals(containingRect.left + viewInWindowXY[0] + 4, sndXY[0]); 993 assertEquals(containingRect.top + viewInWindowXY[1], sndXY[1]); 994 995 dismissPopup(); 996 } 997 998 @Test 999 public void testUpdateDimensionAndAlignAnchorView() throws Throwable { 1000 mActivityRule.runOnUiThread( 1001 () -> mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 1002 CONTENT_SIZE_DP))); 1003 mInstrumentation.waitForIdleSync(); 1004 1005 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1006 mPopupWindow.update(anchorView, 50, 50); 1007 // Do not update if it is not shown 1008 assertFalse(mPopupWindow.isShowing()); 1009 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getWidth()); 1010 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getHeight()); 1011 1012 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(anchorView)); 1013 mInstrumentation.waitForIdleSync(); 1014 // update if it is shown 1015 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, 50, 50)); 1016 mInstrumentation.waitForIdleSync(); 1017 assertTrue(mPopupWindow.isShowing()); 1018 assertEquals(50, mPopupWindow.getWidth()); 1019 assertEquals(50, mPopupWindow.getHeight()); 1020 1021 // ignore if width or height is -1 1022 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, -1, -1)); 1023 mInstrumentation.waitForIdleSync(); 1024 assertTrue(mPopupWindow.isShowing()); 1025 assertEquals(50, mPopupWindow.getWidth()); 1026 assertEquals(50, mPopupWindow.getHeight()); 1027 1028 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 1029 mInstrumentation.waitForIdleSync(); 1030 } 1031 1032 @Test 1033 public void testUpdateDimensionAndAlignAnchorViewWithOffsets() throws Throwable { 1034 int[] anchorXY = new int[2]; 1035 int[] viewInWindowOff = new int[2]; 1036 int[] viewXY = new int[2]; 1037 1038 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1039 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1040 // Do not update if it is not shown 1041 assertFalse(mPopupWindow.isShowing()); 1042 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getWidth()); 1043 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getHeight()); 1044 1045 showPopup(); 1046 anchorView.getLocationOnScreen(anchorXY); 1047 mPopupWindow.getContentView().getLocationInWindow(viewInWindowOff); 1048 1049 // update if it is not shown 1050 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, 20, 50, 50, 50)); 1051 1052 mInstrumentation.waitForIdleSync(); 1053 1054 assertTrue(mPopupWindow.isShowing()); 1055 assertEquals(50, mPopupWindow.getWidth()); 1056 assertEquals(50, mPopupWindow.getHeight()); 1057 1058 mPopupWindow.getContentView().getLocationOnScreen(viewXY); 1059 1060 // The popup should appear below and to right with an offset. 1061 assertEquals(anchorXY[0] + 20 + viewInWindowOff[0], viewXY[0]); 1062 assertEquals(anchorXY[1] + anchorView.getHeight() + 50 + viewInWindowOff[1], viewXY[1]); 1063 1064 // ignore width and height but change location 1065 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, 10, 50, -1, -1)); 1066 mInstrumentation.waitForIdleSync(); 1067 1068 assertTrue(mPopupWindow.isShowing()); 1069 assertEquals(50, mPopupWindow.getWidth()); 1070 assertEquals(50, mPopupWindow.getHeight()); 1071 1072 mPopupWindow.getContentView().getLocationOnScreen(viewXY); 1073 1074 // The popup should appear below and to right with an offset. 1075 assertEquals(anchorXY[0] + 10 + viewInWindowOff[0], viewXY[0]); 1076 assertEquals(anchorXY[1] + anchorView.getHeight() + 50 + viewInWindowOff[1], viewXY[1]); 1077 1078 final View anotherView = mActivity.findViewById(R.id.anchor_middle_left); 1079 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anotherView, 0, 0, 60, 60)); 1080 mInstrumentation.waitForIdleSync(); 1081 1082 assertTrue(mPopupWindow.isShowing()); 1083 assertEquals(60, mPopupWindow.getWidth()); 1084 assertEquals(60, mPopupWindow.getHeight()); 1085 1086 int[] newXY = new int[2]; 1087 anotherView.getLocationOnScreen(newXY); 1088 mPopupWindow.getContentView().getLocationOnScreen(viewXY); 1089 1090 // The popup should appear below and to the right. 1091 assertEquals(newXY[0] + viewInWindowOff[0], viewXY[0]); 1092 assertEquals(newXY[1] + anotherView.getHeight() + viewInWindowOff[1], viewXY[1]); 1093 1094 dismissPopup(); 1095 } 1096 1097 @Test 1098 public void testAccessInputMethodMode() { 1099 mPopupWindow = new PopupWindow(mActivity); 1100 assertEquals(0, mPopupWindow.getInputMethodMode()); 1101 1102 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE); 1103 assertEquals(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE, mPopupWindow.getInputMethodMode()); 1104 1105 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); 1106 assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode()); 1107 1108 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 1109 assertEquals(PopupWindow.INPUT_METHOD_NOT_NEEDED, mPopupWindow.getInputMethodMode()); 1110 1111 mPopupWindow.setInputMethodMode(-1); 1112 assertEquals(-1, mPopupWindow.getInputMethodMode()); 1113 } 1114 1115 @Test 1116 public void testAccessClippingEnabled() { 1117 mPopupWindow = new PopupWindow(mActivity); 1118 assertTrue(mPopupWindow.isClippingEnabled()); 1119 1120 mPopupWindow.setClippingEnabled(false); 1121 assertFalse(mPopupWindow.isClippingEnabled()); 1122 } 1123 1124 @Test 1125 public void testAccessOutsideTouchable() { 1126 mPopupWindow = new PopupWindow(mActivity); 1127 assertFalse(mPopupWindow.isOutsideTouchable()); 1128 1129 mPopupWindow.setOutsideTouchable(true); 1130 assertTrue(mPopupWindow.isOutsideTouchable()); 1131 } 1132 1133 @Test 1134 public void testAccessTouchable() { 1135 mPopupWindow = new PopupWindow(mActivity); 1136 assertTrue(mPopupWindow.isTouchable()); 1137 1138 mPopupWindow.setTouchable(false); 1139 assertFalse(mPopupWindow.isTouchable()); 1140 } 1141 1142 @Test 1143 public void testIsAboveAnchor() throws Throwable { 1144 mActivityRule.runOnUiThread(() -> mPopupWindow = createPopupWindow(createPopupContent( 1145 CONTENT_SIZE_DP, CONTENT_SIZE_DP))); 1146 mInstrumentation.waitForIdleSync(); 1147 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 1148 1149 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(upperAnchor)); 1150 mInstrumentation.waitForIdleSync(); 1151 assertFalse(mPopupWindow.isAboveAnchor()); 1152 dismissPopup(); 1153 1154 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1155 final View lowerAnchor = mActivity.findViewById(R.id.anchor_lower); 1156 1157 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(lowerAnchor, 0, 0)); 1158 mInstrumentation.waitForIdleSync(); 1159 assertTrue(mPopupWindow.isAboveAnchor()); 1160 dismissPopup(); 1161 } 1162 1163 @Test 1164 public void testSetTouchInterceptor() throws Throwable { 1165 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 1166 mInstrumentation.waitForIdleSync(); 1167 mPopupWindow = new PopupWindow(mTextView); 1168 1169 OnTouchListener onTouchListener = mock(OnTouchListener.class); 1170 when(onTouchListener.onTouch(any(View.class), any(MotionEvent.class))).thenReturn(true); 1171 1172 mPopupWindow.setTouchInterceptor(onTouchListener); 1173 mPopupWindow.setFocusable(true); 1174 mPopupWindow.setOutsideTouchable(true); 1175 Drawable drawable = new ColorDrawable(); 1176 mPopupWindow.setBackgroundDrawable(drawable); 1177 showPopup(); 1178 1179 int[] xy = new int[2]; 1180 mPopupWindow.getContentView().getLocationOnScreen(xy); 1181 final int viewWidth = mPopupWindow.getContentView().getWidth(); 1182 final int viewHeight = mPopupWindow.getContentView().getHeight(); 1183 final float x = xy[0] + (viewWidth / 2.0f); 1184 float y = xy[1] + (viewHeight / 2.0f); 1185 1186 long downTime = SystemClock.uptimeMillis(); 1187 long eventTime = SystemClock.uptimeMillis(); 1188 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 1189 MotionEvent.ACTION_DOWN, x, y, 0); 1190 mInstrumentation.sendPointerSync(event); 1191 verify(onTouchListener, times(1)).onTouch(any(View.class), any(MotionEvent.class)); 1192 1193 downTime = SystemClock.uptimeMillis(); 1194 eventTime = SystemClock.uptimeMillis(); 1195 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 1196 mInstrumentation.sendPointerSync(event); 1197 verify(onTouchListener, times(2)).onTouch(any(View.class), any(MotionEvent.class)); 1198 1199 mPopupWindow.setTouchInterceptor(null); 1200 downTime = SystemClock.uptimeMillis(); 1201 eventTime = SystemClock.uptimeMillis(); 1202 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0); 1203 mInstrumentation.sendPointerSync(event); 1204 verify(onTouchListener, times(2)).onTouch(any(View.class), any(MotionEvent.class)); 1205 } 1206 1207 @Test 1208 public void testSetWindowLayoutMode() throws Throwable { 1209 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 1210 mInstrumentation.waitForIdleSync(); 1211 mPopupWindow = new PopupWindow(mTextView); 1212 showPopup(); 1213 1214 ViewGroup.LayoutParams p = mPopupWindow.getContentView().getRootView().getLayoutParams(); 1215 assertEquals(0, p.width); 1216 assertEquals(0, p.height); 1217 1218 mPopupWindow.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); 1219 mActivityRule.runOnUiThread(() -> mPopupWindow.update(20, 50, 50, 50)); 1220 1221 assertEquals(LayoutParams.WRAP_CONTENT, p.width); 1222 assertEquals(LayoutParams.MATCH_PARENT, p.height); 1223 } 1224 1225 @Test 1226 public void testAccessElevation() throws Throwable { 1227 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1228 mActivityRule.runOnUiThread(() -> mPopupWindow.setElevation(2.0f)); 1229 1230 showPopup(); 1231 assertEquals(2.0f, mPopupWindow.getElevation(), 0.0f); 1232 1233 dismissPopup(); 1234 mActivityRule.runOnUiThread(() -> mPopupWindow.setElevation(4.0f)); 1235 showPopup(); 1236 assertEquals(4.0f, mPopupWindow.getElevation(), 0.0f); 1237 1238 dismissPopup(); 1239 mActivityRule.runOnUiThread(() -> mPopupWindow.setElevation(10.0f)); 1240 showPopup(); 1241 assertEquals(10.0f, mPopupWindow.getElevation(), 0.0f); 1242 } 1243 1244 @Test 1245 public void testAccessSoftInputMode() throws Throwable { 1246 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1247 mActivityRule.runOnUiThread( 1248 () -> mPopupWindow.setSoftInputMode( 1249 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)); 1250 1251 showPopup(); 1252 assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE, 1253 mPopupWindow.getSoftInputMode()); 1254 1255 dismissPopup(); 1256 mActivityRule.runOnUiThread( 1257 () -> mPopupWindow.setSoftInputMode( 1258 WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)); 1259 showPopup(); 1260 assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN, 1261 mPopupWindow.getSoftInputMode()); 1262 } 1263 1264 @Test 1265 public void testAccessSplitTouchEnabled() throws Throwable { 1266 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1267 mActivityRule.runOnUiThread(() -> mPopupWindow.setSplitTouchEnabled(true)); 1268 1269 showPopup(); 1270 assertTrue(mPopupWindow.isSplitTouchEnabled()); 1271 1272 dismissPopup(); 1273 mActivityRule.runOnUiThread(() -> mPopupWindow.setSplitTouchEnabled(false)); 1274 showPopup(); 1275 assertFalse(mPopupWindow.isSplitTouchEnabled()); 1276 1277 dismissPopup(); 1278 mActivityRule.runOnUiThread(() -> mPopupWindow.setSplitTouchEnabled(true)); 1279 showPopup(); 1280 assertTrue(mPopupWindow.isSplitTouchEnabled()); 1281 } 1282 1283 @Test 1284 public void testVerticallyClippedBeforeAdjusted() throws Throwable { 1285 View parentWindowView = mActivity.getWindow().getDecorView(); 1286 int parentWidth = parentWindowView.getMeasuredWidth(); 1287 int parentHeight = parentWindowView.getMeasuredHeight(); 1288 1289 // We make a popup which is too large to fit within the parent window. 1290 // After showing it, we verify that it is shrunk to fit the window, 1291 // rather than adjusted up. 1292 mPopupWindow = createPopupWindow(createPopupContent(parentWidth*2, parentHeight*2)); 1293 mPopupWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT); 1294 mPopupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); 1295 1296 showPopup(R.id.anchor_middle); 1297 1298 View popupRoot = mPopupWindow.getContentView(); 1299 int measuredWidth = popupRoot.getMeasuredWidth(); 1300 int measuredHeight = popupRoot.getMeasuredHeight(); 1301 View anchor = mActivity.findViewById(R.id.anchor_middle); 1302 1303 // The popup should occupy all available vertical space. 1304 int[] anchorLocationInWindowXY = new int[2]; 1305 anchor.getLocationInWindow(anchorLocationInWindowXY); 1306 assertEquals(measuredHeight, 1307 parentHeight - (anchorLocationInWindowXY[1] + anchor.getHeight())); 1308 1309 // The popup should be vertically aligned to the anchor's bottom edge. 1310 int[] anchorLocationOnScreenXY = new int[2]; 1311 anchor.getLocationOnScreen(anchorLocationOnScreenXY); 1312 int[] popupLocationOnScreenXY = new int[2]; 1313 popupRoot.getLocationOnScreen(popupLocationOnScreenXY); 1314 assertEquals(anchorLocationOnScreenXY[1] + anchor.getHeight(), popupLocationOnScreenXY[1]); 1315 } 1316 1317 @Test 1318 public void testClipToScreenClipsToInsets() throws Throwable { 1319 int[] orientationValues = {ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, 1320 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}; 1321 int currentOrientation = mActivity.getResources().getConfiguration().orientation; 1322 if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) { 1323 orientationValues[0] = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 1324 orientationValues[1] = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 1325 } 1326 1327 for (int i = 0; i < 2; i++) { 1328 final int orientation = orientationValues[i]; 1329 mActivity.runOnUiThread(() -> 1330 mActivity.setRequestedOrientation(orientation)); 1331 mActivity.waitForConfigurationChanged(); 1332 1333 View parentWindowView = mActivity.getWindow().getDecorView(); 1334 int parentWidth = parentWindowView.getMeasuredWidth(); 1335 int parentHeight = parentWindowView.getMeasuredHeight(); 1336 1337 mPopupWindow = createPopupWindow(createPopupContent(parentWidth*2, parentHeight*2)); 1338 mPopupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT); 1339 mPopupWindow.setHeight(WindowManager.LayoutParams.MATCH_PARENT); 1340 mPopupWindow.setClipToScreenEnabled(true); 1341 1342 showPopup(R.id.anchor_upper_left); 1343 1344 View popupRoot = mPopupWindow.getContentView().getRootView(); 1345 int measuredWidth = popupRoot.getMeasuredWidth(); 1346 int measuredHeight = popupRoot.getMeasuredHeight(); 1347 1348 // The visible frame will not include the insets. 1349 Rect visibleFrame = new Rect(); 1350 parentWindowView.getWindowVisibleDisplayFrame(visibleFrame); 1351 1352 assertEquals(measuredWidth, visibleFrame.width()); 1353 assertEquals(measuredHeight, visibleFrame.height()); 1354 } 1355 } 1356 1357 @Test 1358 public void testPositionAfterParentScroll() throws Throwable { 1359 View.OnScrollChangeListener scrollChangeListener = mock( 1360 View.OnScrollChangeListener.class); 1361 1362 mActivityRule.runOnUiThread(() -> { 1363 mActivity.setContentView(R.layout.popup_window_scrollable); 1364 1365 View anchor = mActivity.findViewById(R.id.anchor_upper); 1366 PopupWindow window = createPopupWindow(); 1367 window.showAsDropDown(anchor); 1368 }); 1369 1370 mActivityRule.runOnUiThread(() -> { 1371 View parent = mActivity.findViewById(R.id.main_container); 1372 parent.scrollBy(0, 500); 1373 parent.setOnScrollChangeListener(scrollChangeListener); 1374 }); 1375 1376 verify(scrollChangeListener, never()).onScrollChange( 1377 any(View.class), anyInt(), anyInt(), anyInt(), anyInt()); 1378 } 1379 1380 @Test 1381 public void testPositionAfterAnchorRemoval() throws Throwable { 1382 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1383 showPopup(R.id.anchor_middle); 1384 1385 final ViewGroup container = (ViewGroup) mActivity.findViewById(R.id.main_container); 1386 final View anchor = mActivity.findViewById(R.id.anchor_middle); 1387 final LayoutParams anchorLayoutParams = anchor.getLayoutParams(); 1388 1389 final int[] originalLocation = mPopupWindow.getContentView().getLocationOnScreen(); 1390 1391 final int deltaX = 30; 1392 final int deltaY = 20; 1393 1394 // Scroll the container, the popup should move along with the anchor. 1395 WidgetTestUtils.runOnMainAndLayoutSync( 1396 mActivityRule, 1397 mPopupWindow.getContentView().getRootView(), 1398 () -> container.scrollBy(deltaX, deltaY), 1399 false /* force layout */); 1400 assertPopupLocation(originalLocation, deltaX, deltaY); 1401 1402 // Detach the anchor, the popup should stay in the same location. 1403 WidgetTestUtils.runOnMainAndLayoutSync( 1404 mActivityRule, 1405 mActivity.getWindow().getDecorView(), 1406 () -> container.removeView(anchor), 1407 false /* force layout */); 1408 assertPopupLocation(originalLocation, deltaX, deltaY); 1409 1410 // Scroll the container while the anchor is detached, the popup should not move. 1411 WidgetTestUtils.runOnMainAndLayoutSync( 1412 mActivityRule, 1413 mActivity.getWindow().getDecorView(), 1414 () -> container.scrollBy(deltaX, deltaY), 1415 true /* force layout */); 1416 assertPopupLocation(originalLocation, deltaX, deltaY); 1417 1418 // Re-attach the anchor, the popup should snap back to the new anchor location. 1419 WidgetTestUtils.runOnMainAndLayoutSync( 1420 mActivityRule, 1421 mPopupWindow.getContentView().getRootView(), 1422 () -> container.addView(anchor, anchorLayoutParams), 1423 false /* force layout */); 1424 assertPopupLocation(originalLocation, deltaX * 2, deltaY * 2); 1425 } 1426 1427 @Test 1428 public void testAnchorInPopup() throws Throwable { 1429 mPopupWindow = createPopupWindow( 1430 mActivity.getLayoutInflater().inflate(R.layout.popup_window, null)); 1431 1432 final PopupWindow subPopup = 1433 createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1434 1435 // Check alignment without overlapping the anchor. 1436 assertFalse(subPopup.getOverlapAnchor()); 1437 1438 verifySubPopupPosition(subPopup, R.id.anchor_upper_left, R.id.anchor_lower_right, 1439 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1440 verifySubPopupPosition(subPopup, R.id.anchor_middle_left, R.id.anchor_lower_right, 1441 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1442 verifySubPopupPosition(subPopup, R.id.anchor_lower_left, R.id.anchor_lower_right, 1443 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1444 1445 verifySubPopupPosition(subPopup, R.id.anchor_upper, R.id.anchor_lower_right, 1446 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1447 verifySubPopupPosition(subPopup, R.id.anchor_middle, R.id.anchor_lower_right, 1448 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1449 verifySubPopupPosition(subPopup, R.id.anchor_lower, R.id.anchor_lower_right, 1450 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1451 1452 verifySubPopupPosition(subPopup, R.id.anchor_upper_right, R.id.anchor_lower_right, 1453 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 1454 verifySubPopupPosition(subPopup, R.id.anchor_middle_right, R.id.anchor_lower_right, 1455 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 1456 verifySubPopupPosition(subPopup, R.id.anchor_lower_right, R.id.anchor_lower_right, 1457 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 1458 1459 // Check alignment while overlapping the anchor. 1460 subPopup.setOverlapAnchor(true); 1461 1462 final int anchorHeight = mActivity.findViewById(R.id.anchor_lower_right).getHeight(); 1463 // To simplify the math assert that all three lower anchors are the same height. 1464 assertEquals(anchorHeight, mActivity.findViewById(R.id.anchor_lower_left).getHeight()); 1465 assertEquals(anchorHeight, mActivity.findViewById(R.id.anchor_lower).getHeight()); 1466 1467 final int verticalSpaceBelowAnchor = anchorHeight * 2; 1468 // Ensure that the subpopup is flipped vertically. 1469 subPopup.setHeight(verticalSpaceBelowAnchor + 1); 1470 1471 verifySubPopupPosition(subPopup, R.id.anchor_upper_left, R.id.anchor_lower_right, 1472 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1473 verifySubPopupPosition(subPopup, R.id.anchor_middle_left, R.id.anchor_lower_right, 1474 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1475 verifySubPopupPosition(subPopup, R.id.anchor_lower_left, R.id.anchor_lower_right, 1476 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1477 1478 verifySubPopupPosition(subPopup, R.id.anchor_upper, R.id.anchor_lower_right, 1479 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1480 verifySubPopupPosition(subPopup, R.id.anchor_middle, R.id.anchor_lower_right, 1481 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1482 verifySubPopupPosition(subPopup, R.id.anchor_lower, R.id.anchor_lower_right, 1483 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1484 1485 verifySubPopupPosition(subPopup, R.id.anchor_upper_right, R.id.anchor_lower_right, 1486 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 1487 verifySubPopupPosition(subPopup, R.id.anchor_middle_right, R.id.anchor_lower_right, 1488 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 1489 verifySubPopupPosition(subPopup, R.id.anchor_lower_right, R.id.anchor_lower_right, 1490 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 1491 1492 // Re-test for the bottom anchor row ensuring that the subpopup not flipped vertically. 1493 subPopup.setHeight(verticalSpaceBelowAnchor - 1); 1494 1495 verifySubPopupPosition(subPopup, R.id.anchor_lower_left, R.id.anchor_lower_right, 1496 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1497 verifySubPopupPosition(subPopup, R.id.anchor_lower, R.id.anchor_lower_right, 1498 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1499 verifySubPopupPosition(subPopup, R.id.anchor_lower_right, R.id.anchor_lower_right, 1500 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 1501 1502 // Check that scrolling scrolls the sub popup along with the main popup. 1503 showPopup(R.id.anchor_middle); 1504 1505 mActivityRule.runOnUiThread(() -> subPopup.showAsDropDown( 1506 mPopupWindow.getContentView().findViewById(R.id.anchor_middle))); 1507 mInstrumentation.waitForIdleSync(); 1508 1509 final int[] popupLocation = mPopupWindow.getContentView().getLocationOnScreen(); 1510 final int[] subPopupLocation = subPopup.getContentView().getLocationOnScreen(); 1511 1512 final int deltaX = 20; 1513 final int deltaY = 30; 1514 1515 final ViewGroup container = (ViewGroup) mActivity.findViewById(R.id.main_container); 1516 WidgetTestUtils.runOnMainAndLayoutSync( 1517 mActivityRule, 1518 subPopup.getContentView().getRootView(), 1519 () -> container.scrollBy(deltaX, deltaY), 1520 false /* force layout */); 1521 1522 final int[] newPopupLocation = mPopupWindow.getContentView().getLocationOnScreen(); 1523 assertEquals(popupLocation[0] - deltaX, newPopupLocation[0]); 1524 assertEquals(popupLocation[1] - deltaY, newPopupLocation[1]); 1525 1526 final int[] newSubPopupLocation = subPopup.getContentView().getLocationOnScreen(); 1527 assertEquals(subPopupLocation[0] - deltaX, newSubPopupLocation[0]); 1528 assertEquals(subPopupLocation[1] - deltaY, newSubPopupLocation[1]); 1529 } 1530 1531 private void verifySubPopupPosition(PopupWindow subPopup, int mainAnchorId, int subAnchorId, 1532 int contentEdgeX, int operatorX, int anchorEdgeX, 1533 int contentEdgeY, int operatorY, int anchorEdgeY) throws Throwable { 1534 showPopup(mainAnchorId); 1535 verifyPosition(subPopup, mPopupWindow.getContentView().findViewById(subAnchorId), 1536 contentEdgeX, operatorX, anchorEdgeX, contentEdgeY, operatorY, anchorEdgeY); 1537 dismissPopup(); 1538 } 1539 1540 private void assertPopupLocation(int[] originalLocation, int deltaX, int deltaY) { 1541 final int[] actualLocation = mPopupWindow.getContentView().getLocationOnScreen(); 1542 assertEquals(originalLocation[0] - deltaX, actualLocation[0]); 1543 assertEquals(originalLocation[1] - deltaY, actualLocation[1]); 1544 } 1545 1546 private static class BaseTransition extends Transition { 1547 @Override 1548 public void captureStartValues(TransitionValues transitionValues) {} 1549 1550 @Override 1551 public void captureEndValues(TransitionValues transitionValues) {} 1552 } 1553 1554 private View createPopupContent(int width, int height) { 1555 final View popupView = new View(mActivity); 1556 popupView.setLayoutParams(new ViewGroup.LayoutParams(width, height)); 1557 popupView.setBackgroundColor(Color.MAGENTA); 1558 1559 return popupView; 1560 } 1561 1562 private PopupWindow createPopupWindow() { 1563 PopupWindow window = new PopupWindow(mActivity); 1564 window.setWidth(WINDOW_SIZE_DP); 1565 window.setHeight(WINDOW_SIZE_DP); 1566 window.setBackgroundDrawable(new ColorDrawable(Color.YELLOW)); 1567 return window; 1568 } 1569 1570 private PopupWindow createPopupWindow(View content) { 1571 PopupWindow window = createPopupWindow(); 1572 window.setContentView(content); 1573 return window; 1574 } 1575 1576 private void showPopup(int resourceId) throws Throwable { 1577 mActivityRule.runOnUiThread(() -> { 1578 if (mPopupWindow == null || mPopupWindow.isShowing()) { 1579 return; 1580 } 1581 View anchor = mActivity.findViewById(resourceId); 1582 mPopupWindow.showAsDropDown(anchor); 1583 assertTrue(mPopupWindow.isShowing()); 1584 }); 1585 mInstrumentation.waitForIdleSync(); 1586 } 1587 1588 private void showPopup() throws Throwable { 1589 showPopup(R.id.anchor_upper_left); 1590 } 1591 1592 private void dismissPopup() throws Throwable { 1593 mActivityRule.runOnUiThread(() -> { 1594 if (mPopupWindow == null || !mPopupWindow.isShowing()) { 1595 return; 1596 } 1597 mPopupWindow.dismiss(); 1598 }); 1599 mInstrumentation.waitForIdleSync(); 1600 } 1601 } 1602