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