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.pm.PackageManager; 36 import android.content.res.Configuration; 37 import android.graphics.Color; 38 import android.graphics.Point; 39 import android.graphics.Rect; 40 import android.graphics.drawable.ColorDrawable; 41 import android.graphics.drawable.Drawable; 42 import android.os.SystemClock; 43 import android.transition.Transition; 44 import android.transition.Transition.TransitionListener; 45 import android.transition.TransitionValues; 46 import android.util.AttributeSet; 47 import android.util.DisplayMetrics; 48 import android.view.Display; 49 import android.view.Gravity; 50 import android.view.MotionEvent; 51 import android.view.View; 52 import android.view.View.OnTouchListener; 53 import android.view.ViewGroup; 54 import android.view.ViewGroup.LayoutParams; 55 import android.view.ViewTreeObserver; 56 import android.view.WindowInsets; 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 androidx.test.InstrumentationRegistry; 64 import androidx.test.annotation.UiThreadTest; 65 import androidx.test.filters.FlakyTest; 66 import androidx.test.filters.SmallTest; 67 import androidx.test.rule.ActivityTestRule; 68 import androidx.test.runner.AndroidJUnit4; 69 70 import com.android.compatibility.common.util.WidgetTestUtils; 71 72 import org.junit.Before; 73 import org.junit.Rule; 74 import org.junit.Test; 75 import org.junit.runner.RunWith; 76 import org.mockito.ArgumentCaptor; 77 78 import java.util.concurrent.CountDownLatch; 79 import java.util.concurrent.TimeUnit; 80 81 @FlakyTest 82 @SmallTest 83 @RunWith(AndroidJUnit4.class) 84 public class PopupWindowTest { 85 private static final int WINDOW_SIZE_DP = 50; 86 private static final int CONTENT_SIZE_DP = 30; 87 private static final boolean IGNORE_BOTTOM_DECOR = true; 88 89 private Instrumentation mInstrumentation; 90 private Context mContext; 91 private PopupWindowCtsActivity mActivity; 92 private PopupWindow mPopupWindow; 93 private TextView mTextView; 94 95 @Rule 96 public ActivityTestRule<PopupWindowCtsActivity> mActivityRule = 97 new ActivityTestRule<>(PopupWindowCtsActivity.class); 98 99 @Before setup()100 public void setup() { 101 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 102 mContext = InstrumentationRegistry.getContext(); 103 mActivity = mActivityRule.getActivity(); 104 } 105 106 @Test testConstructor()107 public void testConstructor() { 108 new PopupWindow(mActivity); 109 110 new PopupWindow(mActivity, null); 111 112 new PopupWindow(mActivity, null, android.R.attr.popupWindowStyle); 113 114 new PopupWindow(mActivity, null, 0, android.R.style.Widget_DeviceDefault_PopupWindow); 115 116 new PopupWindow(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_PopupWindow); 117 118 new PopupWindow(mActivity, null, 0, android.R.style.Widget_Material_PopupWindow); 119 120 new PopupWindow(mActivity, null, 0, android.R.style.Widget_Material_Light_PopupWindow); 121 } 122 123 @UiThreadTest 124 @Test testSize()125 public void testSize() { 126 mPopupWindow = new PopupWindow(); 127 assertEquals(0, mPopupWindow.getWidth()); 128 assertEquals(0, mPopupWindow.getHeight()); 129 130 mPopupWindow = new PopupWindow(50, 50); 131 assertEquals(50, mPopupWindow.getWidth()); 132 assertEquals(50, mPopupWindow.getHeight()); 133 134 mPopupWindow = new PopupWindow(-1, -1); 135 assertEquals(-1, mPopupWindow.getWidth()); 136 assertEquals(-1, mPopupWindow.getHeight()); 137 138 TextView contentView = new TextView(mActivity); 139 mPopupWindow = new PopupWindow(contentView); 140 assertSame(contentView, mPopupWindow.getContentView()); 141 142 mPopupWindow = new PopupWindow(contentView, 0, 0); 143 assertEquals(0, mPopupWindow.getWidth()); 144 assertEquals(0, mPopupWindow.getHeight()); 145 assertSame(contentView, mPopupWindow.getContentView()); 146 147 mPopupWindow = new PopupWindow(contentView, 50, 50); 148 assertEquals(50, mPopupWindow.getWidth()); 149 assertEquals(50, mPopupWindow.getHeight()); 150 assertSame(contentView, mPopupWindow.getContentView()); 151 152 mPopupWindow = new PopupWindow(contentView, -1, -1); 153 assertEquals(-1, mPopupWindow.getWidth()); 154 assertEquals(-1, mPopupWindow.getHeight()); 155 assertSame(contentView, mPopupWindow.getContentView()); 156 157 mPopupWindow = new PopupWindow(contentView, 0, 0, true); 158 assertEquals(0, mPopupWindow.getWidth()); 159 assertEquals(0, mPopupWindow.getHeight()); 160 assertSame(contentView, mPopupWindow.getContentView()); 161 assertTrue(mPopupWindow.isFocusable()); 162 163 mPopupWindow = new PopupWindow(contentView, 50, 50, false); 164 assertEquals(50, mPopupWindow.getWidth()); 165 assertEquals(50, mPopupWindow.getHeight()); 166 assertSame(contentView, mPopupWindow.getContentView()); 167 assertFalse(mPopupWindow.isFocusable()); 168 169 mPopupWindow = new PopupWindow(contentView, -1, -1, true); 170 assertEquals(-1, mPopupWindow.getWidth()); 171 assertEquals(-1, mPopupWindow.getHeight()); 172 assertSame(contentView, mPopupWindow.getContentView()); 173 assertTrue(mPopupWindow.isFocusable()); 174 } 175 176 @Test testAccessEnterExitTransitions()177 public void testAccessEnterExitTransitions() { 178 PopupWindow w = new PopupWindow(mActivity, null, 0, 0); 179 assertNull(w.getEnterTransition()); 180 assertNull(w.getExitTransition()); 181 182 w = new PopupWindow(mActivity, null, 0, R.style.PopupWindow_NullTransitions); 183 assertNull(w.getEnterTransition()); 184 assertNull(w.getExitTransition()); 185 186 w = new PopupWindow(mActivity, null, 0, R.style.PopupWindow_CustomTransitions); 187 assertTrue(w.getEnterTransition() instanceof CustomTransition); 188 assertTrue(w.getExitTransition() instanceof CustomTransition); 189 190 Transition enterTransition = new CustomTransition(); 191 Transition exitTransition = new CustomTransition(); 192 w = new PopupWindow(mActivity, null, 0, 0); 193 w.setEnterTransition(enterTransition); 194 w.setExitTransition(exitTransition); 195 assertEquals(enterTransition, w.getEnterTransition()); 196 assertEquals(exitTransition, w.getExitTransition()); 197 198 w.setEnterTransition(null); 199 w.setExitTransition(null); 200 assertNull(w.getEnterTransition()); 201 assertNull(w.getExitTransition()); 202 } 203 204 public static class CustomTransition extends Transition { CustomTransition()205 public CustomTransition() { 206 } 207 208 // This constructor is needed for reflection-based creation of a transition when 209 // the transition is defined in layout XML via attribute. 210 @SuppressWarnings("unused") CustomTransition(Context context, AttributeSet attrs)211 public CustomTransition(Context context, AttributeSet attrs) { 212 super(context, attrs); 213 } 214 215 @Override captureStartValues(TransitionValues transitionValues)216 public void captureStartValues(TransitionValues transitionValues) {} 217 218 @Override captureEndValues(TransitionValues transitionValues)219 public void captureEndValues(TransitionValues transitionValues) {} 220 } 221 222 @Test testAccessBackground()223 public void testAccessBackground() { 224 mPopupWindow = new PopupWindow(mActivity); 225 226 Drawable drawable = new ColorDrawable(); 227 mPopupWindow.setBackgroundDrawable(drawable); 228 assertSame(drawable, mPopupWindow.getBackground()); 229 230 mPopupWindow.setBackgroundDrawable(null); 231 assertNull(mPopupWindow.getBackground()); 232 } 233 234 @Test testAccessAnimationStyle()235 public void testAccessAnimationStyle() { 236 mPopupWindow = new PopupWindow(mActivity); 237 // default is -1 238 assertEquals(-1, mPopupWindow.getAnimationStyle()); 239 240 mPopupWindow.setAnimationStyle(android.R.style.Animation_Toast); 241 assertEquals(android.R.style.Animation_Toast, 242 mPopupWindow.getAnimationStyle()); 243 244 // abnormal values 245 mPopupWindow.setAnimationStyle(-100); 246 assertEquals(-100, mPopupWindow.getAnimationStyle()); 247 } 248 249 @Test testAccessContentView()250 public void testAccessContentView() throws Throwable { 251 mPopupWindow = new PopupWindow(mActivity); 252 assertNull(mPopupWindow.getContentView()); 253 254 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 255 mInstrumentation.waitForIdleSync(); 256 mPopupWindow.setContentView(mTextView); 257 assertSame(mTextView, mPopupWindow.getContentView()); 258 259 mPopupWindow.setContentView(null); 260 assertNull(mPopupWindow.getContentView()); 261 262 // can not set the content if the old content is shown 263 mPopupWindow.setContentView(mTextView); 264 assertFalse(mPopupWindow.isShowing()); 265 showPopup(); 266 ImageView img = new ImageView(mActivity); 267 assertTrue(mPopupWindow.isShowing()); 268 mPopupWindow.setContentView(img); 269 assertSame(mTextView, mPopupWindow.getContentView()); 270 dismissPopup(); 271 } 272 273 @Test testAccessFocusable()274 public void testAccessFocusable() { 275 mPopupWindow = new PopupWindow(mActivity); 276 assertFalse(mPopupWindow.isFocusable()); 277 278 mPopupWindow.setFocusable(true); 279 assertTrue(mPopupWindow.isFocusable()); 280 281 mPopupWindow.setFocusable(false); 282 assertFalse(mPopupWindow.isFocusable()); 283 } 284 285 @Test testAccessHeight()286 public void testAccessHeight() { 287 mPopupWindow = new PopupWindow(mActivity); 288 assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getHeight()); 289 290 int height = getDisplay().getHeight() / 2; 291 mPopupWindow.setHeight(height); 292 assertEquals(height, mPopupWindow.getHeight()); 293 294 height = getDisplay().getHeight(); 295 mPopupWindow.setHeight(height); 296 assertEquals(height, mPopupWindow.getHeight()); 297 298 mPopupWindow.setHeight(0); 299 assertEquals(0, mPopupWindow.getHeight()); 300 301 height = getDisplay().getHeight() * 2; 302 mPopupWindow.setHeight(height); 303 assertEquals(height, mPopupWindow.getHeight()); 304 305 height = -getDisplay().getHeight() / 2; 306 mPopupWindow.setHeight(height); 307 assertEquals(height, mPopupWindow.getHeight()); 308 } 309 310 /** 311 * Gets the display. 312 * 313 * @return the display 314 */ getDisplay()315 private Display getDisplay() { 316 WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE); 317 return wm.getDefaultDisplay(); 318 } 319 320 @Test testAccessWidth()321 public void testAccessWidth() { 322 mPopupWindow = new PopupWindow(mActivity); 323 assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getWidth()); 324 325 int width = getDisplay().getWidth() / 2; 326 mPopupWindow.setWidth(width); 327 assertEquals(width, mPopupWindow.getWidth()); 328 329 width = getDisplay().getWidth(); 330 mPopupWindow.setWidth(width); 331 assertEquals(width, mPopupWindow.getWidth()); 332 333 mPopupWindow.setWidth(0); 334 assertEquals(0, mPopupWindow.getWidth()); 335 336 width = getDisplay().getWidth() * 2; 337 mPopupWindow.setWidth(width); 338 assertEquals(width, mPopupWindow.getWidth()); 339 340 width = - getDisplay().getWidth() / 2; 341 mPopupWindow.setWidth(width); 342 assertEquals(width, mPopupWindow.getWidth()); 343 } 344 345 private static final int TOP = 0x00; 346 private static final int BOTTOM = 0x01; 347 348 private static final int LEFT = 0x00; 349 private static final int RIGHT = 0x01; 350 351 private static final int GREATER_THAN = 1; 352 private static final int LESS_THAN = -1; 353 private static final int EQUAL_TO = 0; 354 355 @Test testShowAsDropDown()356 public void testShowAsDropDown() throws Throwable { 357 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 358 CONTENT_SIZE_DP)); 359 popup.setIsClippedToScreen(false); 360 popup.setOverlapAnchor(false); 361 popup.setAnimationStyle(0); 362 popup.setExitTransition(null); 363 popup.setEnterTransition(null); 364 365 verifyPosition(popup, R.id.anchor_upper_left, 366 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 367 verifyPosition(popup, R.id.anchor_upper, 368 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 369 verifyPosition(popup, R.id.anchor_upper_right, 370 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 371 372 verifyPosition(popup, R.id.anchor_middle_left, 373 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 374 verifyPosition(popup, R.id.anchor_middle, 375 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 376 verifyPosition(popup, R.id.anchor_middle_right, 377 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 378 379 verifyPosition(popup, R.id.anchor_lower_left, 380 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 381 verifyPosition(popup, R.id.anchor_lower, 382 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 383 verifyPosition(popup, R.id.anchor_lower_right, 384 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 385 } 386 387 @Test testShowAsDropDown_ClipToScreen()388 public void testShowAsDropDown_ClipToScreen() throws Throwable { 389 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 390 CONTENT_SIZE_DP)); 391 popup.setIsClippedToScreen(true); 392 popup.setOverlapAnchor(false); 393 popup.setAnimationStyle(0); 394 popup.setExitTransition(null); 395 popup.setEnterTransition(null); 396 397 verifyPosition(popup, R.id.anchor_upper_left, 398 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 399 verifyPosition(popup, R.id.anchor_upper, 400 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 401 verifyPosition(popup, R.id.anchor_upper_right, 402 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 403 404 verifyPosition(popup, R.id.anchor_middle_left, 405 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 406 verifyPosition(popup, R.id.anchor_middle, 407 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 408 verifyPosition(popup, R.id.anchor_middle_right, 409 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 410 411 verifyPosition(popup, R.id.anchor_lower_left, 412 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 413 verifyPosition(popup, R.id.anchor_lower, 414 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 415 verifyPosition(popup, R.id.anchor_lower_right, 416 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 417 } 418 419 @Test testShowAsDropDown_ClipToScreen_Overlap()420 public void testShowAsDropDown_ClipToScreen_Overlap() throws Throwable { 421 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 422 CONTENT_SIZE_DP)); 423 popup.setIsClippedToScreen(true); 424 popup.setOverlapAnchor(true); 425 popup.setAnimationStyle(0); 426 popup.setExitTransition(null); 427 popup.setEnterTransition(null); 428 429 verifyPosition(popup, R.id.anchor_upper_left, 430 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 431 verifyPosition(popup, R.id.anchor_upper, 432 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 433 verifyPosition(popup, R.id.anchor_upper_right, 434 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 435 436 verifyPosition(popup, R.id.anchor_middle_left, 437 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 438 verifyPosition(popup, R.id.anchor_middle, 439 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 440 verifyPosition(popup, R.id.anchor_middle_right, 441 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 442 443 verifyPosition(popup, R.id.anchor_lower_left, 444 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 445 verifyPosition(popup, R.id.anchor_lower, 446 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 447 verifyPosition(popup, R.id.anchor_lower_right, 448 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 449 } 450 451 @Test testShowAsDropDown_ClipToScreen_Overlap_Offset()452 public void testShowAsDropDown_ClipToScreen_Overlap_Offset() throws Throwable { 453 final PopupWindow popup = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 454 CONTENT_SIZE_DP)); 455 popup.setIsClippedToScreen(true); 456 popup.setOverlapAnchor(true); 457 popup.setAnimationStyle(0); 458 popup.setExitTransition(null); 459 popup.setEnterTransition(null); 460 461 final int offsetX = mActivity.findViewById(R.id.anchor_upper).getWidth() / 2; 462 final int offsetY = mActivity.findViewById(R.id.anchor_upper).getHeight() / 2; 463 final int gravity = Gravity.TOP | Gravity.START; 464 465 verifyPosition(popup, R.id.anchor_upper_left, 466 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 467 offsetX, offsetY, gravity); 468 verifyPosition(popup, R.id.anchor_upper, 469 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 470 offsetX, offsetY, gravity); 471 verifyPosition(popup, R.id.anchor_upper_right, 472 RIGHT, EQUAL_TO, RIGHT, TOP, GREATER_THAN, TOP, 473 offsetX, offsetY, gravity); 474 475 verifyPosition(popup, R.id.anchor_middle_left, 476 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 477 offsetX, offsetY, gravity); 478 verifyPosition(popup, R.id.anchor_middle, 479 LEFT, GREATER_THAN, LEFT, TOP, GREATER_THAN, TOP, 480 offsetX, offsetY, gravity); 481 verifyPosition(popup, R.id.anchor_middle_right, 482 RIGHT, EQUAL_TO, RIGHT, TOP, GREATER_THAN, TOP, 483 offsetX, offsetY, gravity); 484 485 verifyPosition(popup, R.id.anchor_lower_left, 486 LEFT, GREATER_THAN, LEFT, BOTTOM, LESS_THAN, BOTTOM, 487 offsetX, offsetY, gravity); 488 verifyPosition(popup, R.id.anchor_lower, 489 LEFT, GREATER_THAN, LEFT, BOTTOM, LESS_THAN, BOTTOM, 490 offsetX, offsetY, gravity); 491 verifyPosition(popup, R.id.anchor_lower_right, 492 RIGHT, EQUAL_TO, RIGHT, BOTTOM, LESS_THAN, BOTTOM, 493 offsetX, offsetY, gravity); 494 } 495 496 @Test testShowAsDropDown_ClipToScreen_TooBig()497 public void testShowAsDropDown_ClipToScreen_TooBig() throws Throwable { 498 final View rootView = mActivity.findViewById(R.id.anchor_upper_left).getRootView(); 499 final int width = rootView.getWidth() * 2; 500 final int height = rootView.getHeight() * 2; 501 502 final PopupWindow popup = createPopupWindow(createPopupContent(width, height)); 503 popup.setWidth(width); 504 popup.setHeight(height); 505 506 popup.setIsClippedToScreen(true); 507 popup.setOverlapAnchor(false); 508 popup.setAnimationStyle(0); 509 popup.setExitTransition(null); 510 popup.setEnterTransition(null); 511 512 verifyPosition(popup, R.id.anchor_upper_left, 513 LEFT, EQUAL_TO, LEFT, TOP, LESS_THAN, TOP); 514 verifyPosition(popup, R.id.anchor_upper, 515 LEFT, LESS_THAN, LEFT, TOP, LESS_THAN, TOP); 516 verifyPosition(popup, R.id.anchor_upper_right, 517 RIGHT, EQUAL_TO, RIGHT, TOP, LESS_THAN, TOP); 518 519 verifyPosition(popup, R.id.anchor_middle_left, 520 LEFT, EQUAL_TO, LEFT, TOP, LESS_THAN, TOP); 521 verifyPosition(popup, R.id.anchor_middle, 522 LEFT, LESS_THAN, LEFT, TOP, LESS_THAN, TOP); 523 verifyPosition(popup, R.id.anchor_middle_right, 524 RIGHT, EQUAL_TO, RIGHT, TOP, LESS_THAN, TOP); 525 526 verifyPosition(popup, R.id.anchor_lower_left, 527 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, BOTTOM); 528 verifyPosition(popup, R.id.anchor_lower, 529 LEFT, LESS_THAN, LEFT, BOTTOM, EQUAL_TO, BOTTOM); 530 verifyPosition(popup, R.id.anchor_lower_right, 531 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, BOTTOM); 532 } 533 verifyPosition(PopupWindow popup, int anchorId, int contentEdgeX, int operatorX, int anchorEdgeX, int contentEdgeY, int operatorY, int anchorEdgeY)534 private void verifyPosition(PopupWindow popup, int anchorId, 535 int contentEdgeX, int operatorX, int anchorEdgeX, 536 int contentEdgeY, int operatorY, int anchorEdgeY) throws Throwable { 537 verifyPosition(popup, mActivity.findViewById(anchorId), 538 contentEdgeX, operatorX, anchorEdgeX, 539 contentEdgeY, operatorY, anchorEdgeY, 540 0, 0, Gravity.TOP | Gravity.START); 541 } 542 verifyPosition(PopupWindow popup, int anchorId, int contentEdgeX, int operatorX, int anchorEdgeX, int contentEdgeY, int operatorY, int anchorEdgeY, int offsetX, int offsetY, int gravity)543 private void verifyPosition(PopupWindow popup, int anchorId, 544 int contentEdgeX, int operatorX, int anchorEdgeX, 545 int contentEdgeY, int operatorY, int anchorEdgeY, 546 int offsetX, int offsetY, int gravity) throws Throwable { 547 verifyPosition(popup, mActivity.findViewById(anchorId), 548 contentEdgeX, operatorX, anchorEdgeX, 549 contentEdgeY, operatorY, anchorEdgeY, offsetX, offsetY, gravity); 550 } 551 verifyPosition(PopupWindow popup, View anchor, int contentEdgeX, int operatorX, int anchorEdgeX, int contentEdgeY, int operatorY, int anchorEdgeY)552 private void verifyPosition(PopupWindow popup, View anchor, 553 int contentEdgeX, int operatorX, int anchorEdgeX, 554 int contentEdgeY, int operatorY, int anchorEdgeY) throws Throwable { 555 verifyPosition(popup, anchor, 556 contentEdgeX, operatorX, anchorEdgeX, 557 contentEdgeY, operatorY, anchorEdgeY, 558 0, 0, Gravity.TOP | Gravity.START); 559 } 560 verifyPosition(PopupWindow popup, View anchor, int contentEdgeX, int operatorX, int anchorEdgeX, int contentEdgeY, int operatorY, int anchorEdgeY, int offsetX, int offsetY, int gravity)561 private void verifyPosition(PopupWindow popup, View anchor, 562 int contentEdgeX, int operatorX, int anchorEdgeX, 563 int contentEdgeY, int operatorY, int anchorEdgeY, 564 int offsetX, int offsetY, int gravity) throws Throwable { 565 final View content = popup.getContentView(); 566 567 mActivityRule.runOnUiThread(() -> popup.showAsDropDown( 568 anchor, offsetX, offsetY, gravity)); 569 mInstrumentation.waitForIdleSync(); 570 571 assertTrue(popup.isShowing()); 572 verifyPositionX(content, contentEdgeX, operatorX, anchor, anchorEdgeX); 573 verifyPositionY(content, contentEdgeY, operatorY, anchor, anchorEdgeY); 574 575 // Make sure it fits in the display frame. 576 final Rect displayFrame = new Rect(); 577 anchor.getWindowVisibleDisplayFrame(displayFrame); 578 final Rect contentFrame = new Rect(); 579 content.getBoundsOnScreen(contentFrame); 580 assertTrue("Content (" + contentFrame + ") extends outside display (" 581 + displayFrame + ")", displayFrame.contains(contentFrame)); 582 583 mActivityRule.runOnUiThread(popup::dismiss); 584 mInstrumentation.waitForIdleSync(); 585 586 assertFalse(popup.isShowing()); 587 } 588 verifyPositionY(View content, int contentEdge, int flags, View anchor, int anchorEdge)589 private void verifyPositionY(View content, int contentEdge, int flags, 590 View anchor, int anchorEdge) { 591 final int[] anchorOnScreenXY = new int[2]; 592 anchor.getLocationOnScreen(anchorOnScreenXY); 593 int anchorY = anchorOnScreenXY[1]; 594 if ((anchorEdge & BOTTOM) == BOTTOM) { 595 anchorY += anchor.getHeight(); 596 } 597 598 final int[] contentOnScreenXY = new int[2]; 599 content.getLocationOnScreen(contentOnScreenXY); 600 int contentY = contentOnScreenXY[1]; 601 if ((contentEdge & BOTTOM) == BOTTOM) { 602 contentY += content.getHeight(); 603 } 604 605 assertComparison(contentY, flags, anchorY); 606 } 607 verifyPositionX(View content, int contentEdge, int flags, View anchor, int anchorEdge)608 private void verifyPositionX(View content, int contentEdge, int flags, 609 View anchor, int anchorEdge) { 610 final int[] anchorOnScreenXY = new int[2]; 611 anchor.getLocationOnScreen(anchorOnScreenXY); 612 int anchorX = anchorOnScreenXY[0]; 613 if ((anchorEdge & RIGHT) == RIGHT) { 614 anchorX += anchor.getWidth(); 615 } 616 617 final int[] contentOnScreenXY = new int[2]; 618 content.getLocationOnScreen(contentOnScreenXY); 619 int contentX = contentOnScreenXY[0]; 620 if ((contentEdge & RIGHT) == RIGHT) { 621 contentX += content.getWidth(); 622 } 623 624 assertComparison(contentX, flags, anchorX); 625 } 626 assertComparison(int left, int operator, int right)627 private void assertComparison(int left, int operator, int right) { 628 switch (operator) { 629 case GREATER_THAN: 630 assertTrue(left + " <= " + right, left > right); 631 break; 632 case LESS_THAN: 633 assertTrue(left + " >= " + right, left < right); 634 break; 635 case EQUAL_TO: 636 assertTrue(left + " != " + right, left == right); 637 break; 638 } 639 } 640 641 @Test testShowAtLocation()642 public void testShowAtLocation() throws Throwable { 643 int[] popupContentViewInWindowXY = new int[2]; 644 int[] popupContentViewOnScreenXY = new int[2]; 645 Rect containingRect = new Rect(); 646 647 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 648 // Do not attach within the decor; we will be measuring location 649 // with regard to screen coordinates. 650 mPopupWindow.setAttachedInDecor(false); 651 assertFalse(mPopupWindow.isAttachedInDecor()); 652 653 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 654 final WindowInsets windowInsets = upperAnchor.getRootWindowInsets(); 655 final int xOff = windowInsets.getSystemWindowInsetLeft() + 10; 656 final int yOff = windowInsets.getSystemWindowInsetTop() + 21; 657 assertFalse(mPopupWindow.isShowing()); 658 mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY); 659 assertEquals(0, popupContentViewInWindowXY[0]); 660 assertEquals(0, popupContentViewInWindowXY[1]); 661 662 mActivityRule.runOnUiThread( 663 () -> mPopupWindow.showAtLocation(upperAnchor, Gravity.NO_GRAVITY, xOff, yOff)); 664 mInstrumentation.waitForIdleSync(); 665 666 assertTrue(mPopupWindow.isShowing()); 667 mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY); 668 mPopupWindow.getContentView().getLocationOnScreen(popupContentViewOnScreenXY); 669 upperAnchor.getWindowDisplayFrame(containingRect); 670 671 assertTrue(popupContentViewInWindowXY[0] >= 0); 672 assertTrue(popupContentViewInWindowXY[1] >= 0); 673 assertEquals(containingRect.left + popupContentViewInWindowXY[0] + xOff, popupContentViewOnScreenXY[0]); 674 assertEquals(containingRect.top + popupContentViewInWindowXY[1] + yOff, popupContentViewOnScreenXY[1]); 675 676 dismissPopup(); 677 } 678 679 @Test testShowAsDropDownWithOffsets()680 public void testShowAsDropDownWithOffsets() throws Throwable { 681 int[] anchorXY = new int[2]; 682 int[] viewOnScreenXY = new int[2]; 683 int[] viewInWindowXY = new int[2]; 684 685 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 686 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 687 upperAnchor.getLocationOnScreen(anchorXY); 688 int height = upperAnchor.getHeight(); 689 690 final int xOff = 11; 691 final int yOff = 12; 692 693 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(upperAnchor, xOff, yOff)); 694 mInstrumentation.waitForIdleSync(); 695 696 mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY); 697 mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY); 698 assertEquals(anchorXY[0] + xOff + viewInWindowXY[0], viewOnScreenXY[0]); 699 assertEquals(anchorXY[1] + height + yOff + viewInWindowXY[1], viewOnScreenXY[1]); 700 701 dismissPopup(); 702 } 703 704 @Test testOverlapAnchor()705 public void testOverlapAnchor() throws Throwable { 706 int[] anchorXY = new int[2]; 707 int[] viewOnScreenXY = new int[2]; 708 int[] viewInWindowXY = new int[2]; 709 710 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 711 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 712 upperAnchor.getLocationOnScreen(anchorXY); 713 714 assertFalse(mPopupWindow.getOverlapAnchor()); 715 mPopupWindow.setOverlapAnchor(true); 716 assertTrue(mPopupWindow.getOverlapAnchor()); 717 718 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(upperAnchor, 0, 0)); 719 mInstrumentation.waitForIdleSync(); 720 721 mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY); 722 mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY); 723 assertEquals(anchorXY[0] + viewInWindowXY[0], viewOnScreenXY[0]); 724 assertEquals(anchorXY[1] + viewInWindowXY[1], viewOnScreenXY[1]); 725 } 726 727 @Test testAccessWindowLayoutType()728 public void testAccessWindowLayoutType() { 729 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 730 assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, 731 mPopupWindow.getWindowLayoutType()); 732 mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); 733 assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, 734 mPopupWindow.getWindowLayoutType()); 735 } 736 737 // TODO: Remove this test as it is now broken down into individual tests. 738 @Test testGetMaxAvailableHeight()739 public void testGetMaxAvailableHeight() { 740 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 741 742 final View upperAnchorView = mActivity.findViewById(R.id.anchor_upper); 743 final Rect visibleDisplayFrame = getVisibleDisplayFrame(upperAnchorView); 744 final Rect displayFrame = getDisplayFrame(upperAnchorView); 745 746 final int bottomDecorationHeight = displayFrame.bottom - visibleDisplayFrame.bottom; 747 final int availableBelowTopAnchor = 748 visibleDisplayFrame.bottom - getViewBottom(upperAnchorView); 749 final int availableAboveTopAnchor = getLoc(upperAnchorView).y - visibleDisplayFrame.top; 750 751 final int maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(upperAnchorView); 752 final int maxAvailableHeightIgnoringBottomDecoration = 753 mPopupWindow.getMaxAvailableHeight(upperAnchorView, 0, IGNORE_BOTTOM_DECOR); 754 assertTrue(maxAvailableHeight > 0); 755 assertTrue(maxAvailableHeight <= availableBelowTopAnchor); 756 assertTrue(maxAvailableHeightIgnoringBottomDecoration >= maxAvailableHeight); 757 assertTrue(maxAvailableHeightIgnoringBottomDecoration 758 <= availableBelowTopAnchor + bottomDecorationHeight); 759 760 final int maxAvailableHeightWithOffset2 = 761 mPopupWindow.getMaxAvailableHeight(upperAnchorView, 2); 762 assertEquals(maxAvailableHeight - 2, maxAvailableHeightWithOffset2); 763 764 final int maxOffset = maxAvailableHeight; 765 766 final int maxAvailableHeightWithMaxOffset = 767 mPopupWindow.getMaxAvailableHeight(upperAnchorView, maxOffset); 768 assertTrue(maxAvailableHeightWithMaxOffset > 0); 769 assertTrue(maxAvailableHeightWithMaxOffset <= availableAboveTopAnchor + maxOffset); 770 771 final int maxAvailableHeightWithHalfMaxOffset = 772 mPopupWindow.getMaxAvailableHeight(upperAnchorView, maxOffset / 2); 773 assertTrue(maxAvailableHeightWithHalfMaxOffset > 0); 774 assertTrue(maxAvailableHeightWithHalfMaxOffset <= availableBelowTopAnchor); 775 assertTrue(maxAvailableHeightWithHalfMaxOffset 776 <= Math.max( 777 availableAboveTopAnchor + maxOffset / 2, 778 availableBelowTopAnchor - maxOffset / 2)); 779 780 // TODO(b/136178425): A negative offset can return a size that is larger than the display. 781 final int maxAvailableHeightWithNegativeOffset = 782 mPopupWindow.getMaxAvailableHeight(upperAnchorView, -1); 783 assertTrue(maxAvailableHeightWithNegativeOffset > 0); 784 assertTrue(maxAvailableHeightWithNegativeOffset <= availableBelowTopAnchor + 1); 785 786 final int maxAvailableHeightWithOffset2IgnoringBottomDecoration = 787 mPopupWindow.getMaxAvailableHeight(upperAnchorView, 2, IGNORE_BOTTOM_DECOR); 788 assertEquals(maxAvailableHeightIgnoringBottomDecoration - 2, 789 maxAvailableHeightWithOffset2IgnoringBottomDecoration); 790 791 final int maxAvailableHeightWithMaxOffsetIgnoringBottomDecoration = 792 mPopupWindow.getMaxAvailableHeight(upperAnchorView, maxOffset, IGNORE_BOTTOM_DECOR); 793 assertTrue(maxAvailableHeightWithMaxOffsetIgnoringBottomDecoration > 0); 794 assertTrue(maxAvailableHeightWithMaxOffsetIgnoringBottomDecoration 795 <= availableAboveTopAnchor + maxOffset); 796 797 final int maxAvailableHeightWithHalfOffsetIgnoringBottomDecoration = 798 mPopupWindow.getMaxAvailableHeight( 799 upperAnchorView, 800 maxOffset / 2, 801 IGNORE_BOTTOM_DECOR); 802 assertTrue(maxAvailableHeightWithHalfOffsetIgnoringBottomDecoration > 0); 803 assertTrue(maxAvailableHeightWithHalfOffsetIgnoringBottomDecoration 804 <= Math.max( 805 availableAboveTopAnchor + maxOffset / 2, 806 availableBelowTopAnchor + bottomDecorationHeight - maxOffset / 2)); 807 808 final int maxAvailableHeightWithOffsetIgnoringBottomDecoration = 809 mPopupWindow.getMaxAvailableHeight(upperAnchorView, 0, IGNORE_BOTTOM_DECOR); 810 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration > 0); 811 assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration 812 <= availableBelowTopAnchor + bottomDecorationHeight); 813 814 final View lowerAnchorView = mActivity.findViewById(R.id.anchor_lower); 815 final int availableAboveLowerAnchor = getLoc(lowerAnchorView).y - visibleDisplayFrame.top; 816 final int maxAvailableHeightLowerAnchor = 817 mPopupWindow.getMaxAvailableHeight(lowerAnchorView); 818 assertTrue(maxAvailableHeightLowerAnchor > 0); 819 assertTrue(maxAvailableHeightLowerAnchor <= availableAboveLowerAnchor); 820 821 final View middleAnchorView = mActivity.findViewById(R.id.anchor_middle_left); 822 final int availableAboveMiddleAnchor = getLoc(middleAnchorView).y - visibleDisplayFrame.top; 823 final int availableBelowMiddleAnchor = 824 visibleDisplayFrame.bottom - getViewBottom(middleAnchorView); 825 final int maxAvailableHeightMiddleAnchor = 826 mPopupWindow.getMaxAvailableHeight(middleAnchorView); 827 assertTrue(maxAvailableHeightMiddleAnchor > 0); 828 assertTrue(maxAvailableHeightMiddleAnchor 829 <= Math.max(availableAboveMiddleAnchor, availableBelowMiddleAnchor)); 830 } 831 832 @Test testGetMaxAvailableHeight_topAnchor()833 public void testGetMaxAvailableHeight_topAnchor() { 834 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 835 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 836 837 final int expected = getVisibleDisplayFrame(anchorView).bottom - getViewBottom(anchorView); 838 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView); 839 840 assertEquals(expected, actual); 841 } 842 843 @Test testGetMaxAvailableHeight_topAnchor_ignoringBottomDecoration()844 public void testGetMaxAvailableHeight_topAnchor_ignoringBottomDecoration() { 845 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 846 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 847 848 final int expected = getDisplayFrame(anchorView).bottom - getViewBottom(anchorView); 849 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, 0, IGNORE_BOTTOM_DECOR); 850 851 assertEquals(expected, actual); 852 } 853 854 @Test testGetMaxAvailableHeight_topAnchor_offset2()855 public void testGetMaxAvailableHeight_topAnchor_offset2() { 856 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 857 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 858 859 final int expected = 860 getVisibleDisplayFrame(anchorView).bottom - getViewBottom(anchorView) - 2; 861 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, 2); 862 863 assertEquals(expected, actual); 864 } 865 866 @Test testGetMaxAvailableHeight_topAnchor_offset2_ignoringBottomDecoration()867 public void testGetMaxAvailableHeight_topAnchor_offset2_ignoringBottomDecoration() { 868 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 869 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 870 871 final int expected = getDisplayFrame(anchorView).bottom - getViewBottom(anchorView) - 2; 872 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, 2, IGNORE_BOTTOM_DECOR); 873 874 assertEquals(expected, actual); 875 } 876 877 @Test testGetMaxAvailableHeight_topAnchor_largeOffset()878 public void testGetMaxAvailableHeight_topAnchor_largeOffset() { 879 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 880 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 881 final Rect visibleDisplayFrame = getVisibleDisplayFrame(anchorView); 882 final int maxOffset = visibleDisplayFrame.bottom - getViewBottom(anchorView); 883 final int offset = maxOffset / 2; 884 885 final int distanceToTop = getLoc(anchorView).y - visibleDisplayFrame.top + offset; 886 final int distanceToBottom = 887 visibleDisplayFrame.bottom - getViewBottom(anchorView) - offset; 888 889 final int expected = Math.max(distanceToTop, distanceToBottom); 890 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, offset); 891 892 assertEquals(expected, actual); 893 } 894 895 @Test testGetMaxAvailableHeight_topAnchor_largeOffset_ignoringBottomDecoration()896 public void testGetMaxAvailableHeight_topAnchor_largeOffset_ignoringBottomDecoration() { 897 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 898 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 899 final Rect visibleDisplayFrame = getVisibleDisplayFrame(anchorView); 900 final Rect displayFrame = getDisplayFrame(anchorView); 901 902 final int maxOffset = visibleDisplayFrame.bottom - getViewBottom(anchorView); 903 final int offset = maxOffset / 2; 904 905 final int distanceToTop = getLoc(anchorView).y - visibleDisplayFrame.top + offset; 906 final int distanceToBottom = displayFrame.bottom - getViewBottom(anchorView) - offset; 907 908 final int expected = Math.max(distanceToTop, distanceToBottom); 909 final int actual = 910 mPopupWindow.getMaxAvailableHeight(anchorView, offset, IGNORE_BOTTOM_DECOR); 911 912 assertEquals(expected, actual); 913 } 914 915 @Test testGetMaxAvailableHeight_topAnchor_maxOffset()916 public void testGetMaxAvailableHeight_topAnchor_maxOffset() { 917 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 918 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 919 final Rect visibleDisplayFrame = getVisibleDisplayFrame(anchorView); 920 final int offset = visibleDisplayFrame.bottom - getViewBottom(anchorView); 921 922 final int expected = getLoc(anchorView).y - visibleDisplayFrame.top + offset; 923 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, offset); 924 925 assertEquals(expected, actual); 926 } 927 928 @Test testGetMaxAvailableHeight_topAnchor_maxOffset_ignoringBottomDecoration()929 public void testGetMaxAvailableHeight_topAnchor_maxOffset_ignoringBottomDecoration() { 930 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 931 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 932 final Rect visibleDisplayFrame = getVisibleDisplayFrame(anchorView); 933 final int offset = visibleDisplayFrame.bottom - getViewBottom(anchorView); 934 935 final int expected = getLoc(anchorView).y - visibleDisplayFrame.top + offset; 936 final int actual = 937 mPopupWindow.getMaxAvailableHeight(anchorView, offset, IGNORE_BOTTOM_DECOR); 938 939 assertEquals(expected, actual); 940 } 941 942 @Test testGetMaxAvailableHeight_topAnchor_negativeOffset()943 public void testGetMaxAvailableHeight_topAnchor_negativeOffset() { 944 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 945 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 946 947 final int expected = 948 getVisibleDisplayFrame(anchorView).bottom - getViewBottom(anchorView) + 1; 949 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, -1); 950 951 assertEquals(expected, actual); 952 } 953 954 // TODO(b/136178425): A negative offset can return a size that is larger than the display. 955 @Test testGetMaxAvailableHeight_topAnchor_negativeOffset_ignoringBottomDecoration()956 public void testGetMaxAvailableHeight_topAnchor_negativeOffset_ignoringBottomDecoration() { 957 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 958 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 959 960 final int expected = 961 getDisplayFrame(anchorView).bottom - getViewBottom(anchorView) + 1; 962 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, -1, IGNORE_BOTTOM_DECOR); 963 964 assertEquals(expected, actual); 965 } 966 967 @Test testGetMaxAvailableHeight_middleAnchor()968 public void testGetMaxAvailableHeight_middleAnchor() { 969 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 970 final View anchorView = mActivity.findViewById(R.id.anchor_middle_left); 971 final Rect visibleDisplayFrame = getVisibleDisplayFrame(anchorView); 972 973 final int distanceToTop = getLoc(anchorView).y - visibleDisplayFrame.top; 974 final int distanceToBottom = visibleDisplayFrame.bottom - getViewBottom(anchorView); 975 976 final int expected = Math.max(distanceToTop, distanceToBottom); 977 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView); 978 979 assertEquals(expected, actual); 980 } 981 982 @Test testGetMaxAvailableHeight_middleAnchor_ignoreBottomDecoration()983 public void testGetMaxAvailableHeight_middleAnchor_ignoreBottomDecoration() { 984 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 985 final View anchorView = mActivity.findViewById(R.id.anchor_middle_left); 986 final Rect visibleDisplayFrame = getVisibleDisplayFrame(anchorView); 987 final Rect displayFrame = getDisplayFrame(anchorView); 988 989 990 final int distanceToTop = getLoc(anchorView).y - visibleDisplayFrame.top; 991 final int distanceToBottom = displayFrame.bottom - getViewBottom(anchorView); 992 993 final int expected = Math.max(distanceToTop, distanceToBottom); 994 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, 0, IGNORE_BOTTOM_DECOR); 995 996 assertEquals(expected, actual); 997 } 998 999 @Test testGetMaxAvailableHeight_bottomAnchor()1000 public void testGetMaxAvailableHeight_bottomAnchor() { 1001 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1002 final View anchorView = mActivity.findViewById(R.id.anchor_lower); 1003 1004 final int expected = getLoc(anchorView).y - getVisibleDisplayFrame(anchorView).top; 1005 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView); 1006 1007 assertEquals(expected, actual); 1008 } 1009 1010 @Test testGetMaxAvailableHeight_bottomAnchor_ignoreBottomDecoration()1011 public void testGetMaxAvailableHeight_bottomAnchor_ignoreBottomDecoration() { 1012 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1013 final View anchorView = mActivity.findViewById(R.id.anchor_lower); 1014 1015 final int expected = getLoc(anchorView).y - getVisibleDisplayFrame(anchorView).top; 1016 final int actual = mPopupWindow.getMaxAvailableHeight(anchorView, 0, IGNORE_BOTTOM_DECOR); 1017 1018 assertEquals(expected, actual); 1019 } 1020 getLoc(View view)1021 private Point getLoc(View view) { 1022 final int[] anchorPosition = new int[2]; 1023 view.getLocationOnScreen(anchorPosition); 1024 return new Point(anchorPosition[0], anchorPosition[1]); 1025 } 1026 getViewBottom(View view)1027 private int getViewBottom(View view) { 1028 return getLoc(view).y + view.getHeight(); 1029 } 1030 getVisibleDisplayFrame(View view)1031 private Rect getVisibleDisplayFrame(View view) { 1032 final Rect visibleDisplayFrame = new Rect(); 1033 view.getWindowVisibleDisplayFrame(visibleDisplayFrame); 1034 return visibleDisplayFrame; 1035 } 1036 getDisplayFrame(View view)1037 private Rect getDisplayFrame(View view) { 1038 final Rect displayFrame = new Rect(); 1039 view.getWindowDisplayFrame(displayFrame); 1040 return displayFrame; 1041 } 1042 1043 @UiThreadTest 1044 @Test testDismiss()1045 public void testDismiss() { 1046 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1047 assertFalse(mPopupWindow.isShowing()); 1048 View anchorView = mActivity.findViewById(R.id.anchor_upper); 1049 mPopupWindow.showAsDropDown(anchorView); 1050 1051 mPopupWindow.dismiss(); 1052 assertFalse(mPopupWindow.isShowing()); 1053 1054 mPopupWindow.dismiss(); 1055 assertFalse(mPopupWindow.isShowing()); 1056 } 1057 1058 @Test testSetOnDismissListener()1059 public void testSetOnDismissListener() throws Throwable { 1060 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 1061 mInstrumentation.waitForIdleSync(); 1062 mPopupWindow = new PopupWindow(mTextView); 1063 mPopupWindow.setOnDismissListener(null); 1064 1065 OnDismissListener onDismissListener = mock(OnDismissListener.class); 1066 mPopupWindow.setOnDismissListener(onDismissListener); 1067 showPopup(); 1068 dismissPopup(); 1069 verify(onDismissListener, times(1)).onDismiss(); 1070 1071 showPopup(); 1072 dismissPopup(); 1073 verify(onDismissListener, times(2)).onDismiss(); 1074 1075 mPopupWindow.setOnDismissListener(null); 1076 showPopup(); 1077 dismissPopup(); 1078 verify(onDismissListener, times(2)).onDismiss(); 1079 } 1080 1081 @Test testUpdate()1082 public void testUpdate() throws Throwable { 1083 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1084 mPopupWindow.setBackgroundDrawable(null); 1085 showPopup(); 1086 1087 mPopupWindow.setIgnoreCheekPress(); 1088 mPopupWindow.setFocusable(true); 1089 mPopupWindow.setTouchable(false); 1090 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 1091 mPopupWindow.setClippingEnabled(false); 1092 mPopupWindow.setOutsideTouchable(true); 1093 1094 WindowManager.LayoutParams p = (WindowManager.LayoutParams) 1095 mPopupWindow.getContentView().getRootView().getLayoutParams(); 1096 1097 assertEquals(0, WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES & p.flags); 1098 assertEquals(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 1099 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE & p.flags); 1100 assertEquals(0, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE & p.flags); 1101 assertEquals(0, WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH & p.flags); 1102 assertEquals(0, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS & p.flags); 1103 assertEquals(0, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags); 1104 1105 mActivityRule.runOnUiThread(mPopupWindow::update); 1106 mInstrumentation.waitForIdleSync(); 1107 1108 assertEquals(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES, 1109 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES & p.flags); 1110 assertEquals(0, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE & p.flags); 1111 assertEquals(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, 1112 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE & p.flags); 1113 assertEquals(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, 1114 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH & p.flags); 1115 assertEquals(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, 1116 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS & p.flags); 1117 assertEquals(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 1118 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags); 1119 } 1120 1121 @Test testEnterExitInterruption()1122 public void testEnterExitInterruption() throws Throwable { 1123 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1124 verifyEnterExitTransition( 1125 () -> mPopupWindow.showAsDropDown(anchorView, 0, 0), true); 1126 } 1127 1128 @Test testEnterExitTransitionAsDropDown()1129 public void testEnterExitTransitionAsDropDown() throws Throwable { 1130 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1131 verifyEnterExitTransition( 1132 () -> mPopupWindow.showAsDropDown(anchorView, 0, 0), false); 1133 } 1134 1135 @Test testEnterExitTransitionAtLocation()1136 public void testEnterExitTransitionAtLocation() throws Throwable { 1137 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1138 verifyEnterExitTransition( 1139 () -> mPopupWindow.showAtLocation(anchorView, Gravity.BOTTOM, 0, 0), false); 1140 } 1141 1142 @Test testEnterExitTransitionAsDropDownWithCustomBounds()1143 public void testEnterExitTransitionAsDropDownWithCustomBounds() throws Throwable { 1144 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1145 final Rect epicenter = new Rect(20, 50, 22, 80); 1146 verifyTransitionEpicenterChange( 1147 () -> mPopupWindow.showAsDropDown(anchorView, 0, 0), epicenter); 1148 } 1149 verifyTransitionEpicenterChange(Runnable showRunnable, Rect epicenterBounds)1150 private void verifyTransitionEpicenterChange(Runnable showRunnable, Rect epicenterBounds) 1151 throws Throwable { 1152 TransitionListener enterListener = mock(TransitionListener.class); 1153 Transition enterTransition = new BaseTransition(); 1154 enterTransition.addListener(enterListener); 1155 1156 TransitionListener exitListener = mock(TransitionListener.class); 1157 Transition exitTransition = new BaseTransition(); 1158 exitTransition.addListener(exitListener); 1159 1160 OnDismissListener dismissListener = mock(OnDismissListener.class); 1161 1162 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1163 mPopupWindow.setEnterTransition(enterTransition); 1164 mPopupWindow.setExitTransition(exitTransition); 1165 mPopupWindow.setOnDismissListener(dismissListener); 1166 1167 ArgumentCaptor<Transition> captor = ArgumentCaptor.forClass(Transition.class); 1168 1169 mActivityRule.runOnUiThread(showRunnable); 1170 mInstrumentation.waitForIdleSync(); 1171 1172 verify(enterListener, times(1)).onTransitionStart(captor.capture()); 1173 final Rect oldEpicenterStart = new Rect(captor.getValue().getEpicenter()); 1174 1175 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 1176 mInstrumentation.waitForIdleSync(); 1177 1178 verify(exitListener, times(1)).onTransitionStart(captor.capture()); 1179 final Rect oldEpicenterExit = new Rect(captor.getValue().getEpicenter()); 1180 1181 mPopupWindow.setEpicenterBounds(epicenterBounds); 1182 mActivityRule.runOnUiThread(showRunnable); 1183 mInstrumentation.waitForIdleSync(); 1184 1185 verify(enterListener, times(2)).onTransitionStart(captor.capture()); 1186 final Rect newEpicenterStart = new Rect(captor.getValue().getEpicenter()); 1187 1188 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 1189 mInstrumentation.waitForIdleSync(); 1190 1191 verify(exitListener, times(2)).onTransitionStart(captor.capture()); 1192 1193 final Rect newEpicenterExit = new Rect(captor.getValue().getEpicenter()); 1194 1195 verifyEpicenters(oldEpicenterStart, newEpicenterStart, epicenterBounds); 1196 verifyEpicenters(oldEpicenterExit, newEpicenterExit, epicenterBounds); 1197 1198 } 1199 verifyEpicenters(Rect actualOld, Rect actualNew, Rect passed)1200 private void verifyEpicenters(Rect actualOld, Rect actualNew, Rect passed) { 1201 Rect oldCopy = new Rect(actualOld); 1202 int left = oldCopy.left; 1203 int top = oldCopy.top; 1204 oldCopy.set(passed); 1205 oldCopy.offset(left, top); 1206 1207 assertEquals(oldCopy, actualNew); 1208 } 1209 verifyEnterExitTransition(Runnable showRunnable, boolean showAgain)1210 private void verifyEnterExitTransition(Runnable showRunnable, boolean showAgain) 1211 throws Throwable { 1212 TransitionListener enterListener = mock(TransitionListener.class); 1213 Transition enterTransition = new BaseTransition(); 1214 enterTransition.addListener(enterListener); 1215 1216 TransitionListener exitListener = mock(TransitionListener.class); 1217 Transition exitTransition = new BaseTransition(); 1218 exitTransition.addListener(exitListener); 1219 1220 OnDismissListener dismissListener = mock(OnDismissListener.class); 1221 1222 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1223 mPopupWindow.setEnterTransition(enterTransition); 1224 mPopupWindow.setExitTransition(exitTransition); 1225 mPopupWindow.setOnDismissListener(dismissListener); 1226 1227 mActivityRule.runOnUiThread(showRunnable); 1228 mInstrumentation.waitForIdleSync(); 1229 verify(enterListener, times(1)).onTransitionStart(any(Transition.class)); 1230 verify(exitListener, never()).onTransitionStart(any(Transition.class)); 1231 verify(dismissListener, never()).onDismiss(); 1232 1233 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 1234 1235 int times; 1236 if (showAgain) { 1237 // Interrupt dismiss by calling show again, then actually dismiss. 1238 mActivityRule.runOnUiThread(showRunnable); 1239 mInstrumentation.waitForIdleSync(); 1240 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 1241 1242 times = 2; 1243 } else { 1244 times = 1; 1245 } 1246 1247 mInstrumentation.waitForIdleSync(); 1248 verify(enterListener, times(times)).onTransitionStart(any(Transition.class)); 1249 verify(exitListener, times(times)).onTransitionStart(any(Transition.class)); 1250 verify(dismissListener, times(times)).onDismiss(); 1251 } 1252 1253 @Test testUpdatePositionAndDimension()1254 public void testUpdatePositionAndDimension() throws Throwable { 1255 int[] fstXY = new int[2]; 1256 int[] sndXY = new int[2]; 1257 int[] viewInWindowXY = new int[2]; 1258 Rect containingRect = new Rect(); 1259 final Point popupPos = new Point(); 1260 1261 mActivityRule.runOnUiThread(() -> { 1262 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1263 // Do not attach within the decor; we will be measuring location 1264 // with regard to screen coordinates. 1265 mPopupWindow.setAttachedInDecor(false); 1266 }); 1267 1268 mInstrumentation.waitForIdleSync(); 1269 // Do not update if it is not shown 1270 assertFalse(mPopupWindow.isShowing()); 1271 assertFalse(mPopupWindow.isAttachedInDecor()); 1272 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getWidth()); 1273 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getHeight()); 1274 1275 showPopup(); 1276 mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY); 1277 final View containerView = mActivity.findViewById(R.id.main_container); 1278 containerView.getWindowDisplayFrame(containingRect); 1279 1280 // update if it is not shown 1281 mActivityRule.runOnUiThread(() -> mPopupWindow.update(80, 80)); 1282 1283 mInstrumentation.waitForIdleSync(); 1284 assertTrue(mPopupWindow.isShowing()); 1285 assertEquals(80, mPopupWindow.getWidth()); 1286 assertEquals(80, mPopupWindow.getHeight()); 1287 1288 final WindowInsets windowInsets = containerView.getRootWindowInsets(); 1289 popupPos.set(windowInsets.getStableInsetLeft() + 20, windowInsets.getStableInsetTop() + 50); 1290 1291 // update if it is not shown 1292 mActivityRule.runOnUiThread(() -> mPopupWindow.update(popupPos.x, popupPos.y, 50, 50)); 1293 1294 mInstrumentation.waitForIdleSync(); 1295 assertTrue(mPopupWindow.isShowing()); 1296 assertEquals(50, mPopupWindow.getWidth()); 1297 assertEquals(50, mPopupWindow.getHeight()); 1298 1299 mPopupWindow.getContentView().getLocationOnScreen(fstXY); 1300 assertEquals(containingRect.left + popupPos.x + viewInWindowXY[0], fstXY[0]); 1301 assertEquals(containingRect.top + popupPos.y + viewInWindowXY[1], fstXY[1]); 1302 1303 popupPos.set(windowInsets.getStableInsetLeft() + 4, windowInsets.getStableInsetTop()); 1304 1305 // ignore if width or height is -1 1306 mActivityRule.runOnUiThread( 1307 () -> mPopupWindow.update(popupPos.x, popupPos.y, -1, -1, true)); 1308 mInstrumentation.waitForIdleSync(); 1309 1310 assertTrue(mPopupWindow.isShowing()); 1311 assertEquals(50, mPopupWindow.getWidth()); 1312 assertEquals(50, mPopupWindow.getHeight()); 1313 1314 mPopupWindow.getContentView().getLocationOnScreen(sndXY); 1315 assertEquals(containingRect.left + popupPos.x + viewInWindowXY[0], sndXY[0]); 1316 assertEquals(containingRect.top + popupPos.y + viewInWindowXY[1], sndXY[1]); 1317 1318 dismissPopup(); 1319 } 1320 1321 @Test testUpdateDimensionAndAlignAnchorView()1322 public void testUpdateDimensionAndAlignAnchorView() throws Throwable { 1323 mActivityRule.runOnUiThread( 1324 () -> mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, 1325 CONTENT_SIZE_DP))); 1326 mInstrumentation.waitForIdleSync(); 1327 1328 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1329 mPopupWindow.update(anchorView, 50, 50); 1330 // Do not update if it is not shown 1331 assertFalse(mPopupWindow.isShowing()); 1332 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getWidth()); 1333 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getHeight()); 1334 1335 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(anchorView)); 1336 mInstrumentation.waitForIdleSync(); 1337 // update if it is shown 1338 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, 50, 50)); 1339 mInstrumentation.waitForIdleSync(); 1340 assertTrue(mPopupWindow.isShowing()); 1341 assertEquals(50, mPopupWindow.getWidth()); 1342 assertEquals(50, mPopupWindow.getHeight()); 1343 1344 // ignore if width or height is -1 1345 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, -1, -1)); 1346 mInstrumentation.waitForIdleSync(); 1347 assertTrue(mPopupWindow.isShowing()); 1348 assertEquals(50, mPopupWindow.getWidth()); 1349 assertEquals(50, mPopupWindow.getHeight()); 1350 1351 mActivityRule.runOnUiThread(mPopupWindow::dismiss); 1352 mInstrumentation.waitForIdleSync(); 1353 } 1354 1355 @Test testUpdateDimensionAndAlignAnchorViewWithOffsets()1356 public void testUpdateDimensionAndAlignAnchorViewWithOffsets() throws Throwable { 1357 int[] anchorXY = new int[2]; 1358 int[] viewInWindowOff = new int[2]; 1359 int[] viewXY = new int[2]; 1360 1361 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1362 final View anchorView = mActivity.findViewById(R.id.anchor_upper); 1363 // Do not update if it is not shown 1364 assertFalse(mPopupWindow.isShowing()); 1365 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getWidth()); 1366 assertEquals(WINDOW_SIZE_DP, mPopupWindow.getHeight()); 1367 1368 showPopup(); 1369 anchorView.getLocationOnScreen(anchorXY); 1370 mPopupWindow.getContentView().getLocationInWindow(viewInWindowOff); 1371 1372 // update if it is not shown 1373 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, 20, 50, 50, 50)); 1374 1375 mInstrumentation.waitForIdleSync(); 1376 1377 assertTrue(mPopupWindow.isShowing()); 1378 assertEquals(50, mPopupWindow.getWidth()); 1379 assertEquals(50, mPopupWindow.getHeight()); 1380 1381 mPopupWindow.getContentView().getLocationOnScreen(viewXY); 1382 1383 // The popup should appear below and to right with an offset. 1384 assertEquals(anchorXY[0] + 20 + viewInWindowOff[0], viewXY[0]); 1385 assertEquals(anchorXY[1] + anchorView.getHeight() + 50 + viewInWindowOff[1], viewXY[1]); 1386 1387 // ignore width and height but change location 1388 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anchorView, 10, 50, -1, -1)); 1389 mInstrumentation.waitForIdleSync(); 1390 1391 assertTrue(mPopupWindow.isShowing()); 1392 assertEquals(50, mPopupWindow.getWidth()); 1393 assertEquals(50, mPopupWindow.getHeight()); 1394 1395 mPopupWindow.getContentView().getLocationOnScreen(viewXY); 1396 1397 // The popup should appear below and to right with an offset. 1398 assertEquals(anchorXY[0] + 10 + viewInWindowOff[0], viewXY[0]); 1399 assertEquals(anchorXY[1] + anchorView.getHeight() + 50 + viewInWindowOff[1], viewXY[1]); 1400 1401 final View anotherView = mActivity.findViewById(R.id.anchor_middle_left); 1402 mActivityRule.runOnUiThread(() -> mPopupWindow.update(anotherView, 0, 0, 60, 60)); 1403 mInstrumentation.waitForIdleSync(); 1404 1405 assertTrue(mPopupWindow.isShowing()); 1406 assertEquals(60, mPopupWindow.getWidth()); 1407 assertEquals(60, mPopupWindow.getHeight()); 1408 1409 int[] newXY = new int[2]; 1410 anotherView.getLocationOnScreen(newXY); 1411 mPopupWindow.getContentView().getLocationOnScreen(viewXY); 1412 1413 // The popup should appear below and to the right. 1414 assertEquals(newXY[0] + viewInWindowOff[0], viewXY[0]); 1415 assertEquals(newXY[1] + anotherView.getHeight() + viewInWindowOff[1], viewXY[1]); 1416 1417 dismissPopup(); 1418 } 1419 1420 @Test testAccessInputMethodMode()1421 public void testAccessInputMethodMode() { 1422 mPopupWindow = new PopupWindow(mActivity); 1423 assertEquals(0, mPopupWindow.getInputMethodMode()); 1424 1425 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE); 1426 assertEquals(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE, mPopupWindow.getInputMethodMode()); 1427 1428 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); 1429 assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode()); 1430 1431 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 1432 assertEquals(PopupWindow.INPUT_METHOD_NOT_NEEDED, mPopupWindow.getInputMethodMode()); 1433 1434 mPopupWindow.setInputMethodMode(-1); 1435 assertEquals(-1, mPopupWindow.getInputMethodMode()); 1436 } 1437 1438 @Test testAccessClippingEnabled()1439 public void testAccessClippingEnabled() { 1440 mPopupWindow = new PopupWindow(mActivity); 1441 assertTrue(mPopupWindow.isClippingEnabled()); 1442 1443 mPopupWindow.setClippingEnabled(false); 1444 assertFalse(mPopupWindow.isClippingEnabled()); 1445 } 1446 1447 @Test testAccessIsClippedToScreen()1448 public void testAccessIsClippedToScreen() { 1449 mPopupWindow = new PopupWindow(mActivity); 1450 assertFalse(mPopupWindow.isClippedToScreen()); 1451 1452 mPopupWindow.setIsClippedToScreen(true); 1453 assertTrue(mPopupWindow.isClippedToScreen()); 1454 } 1455 1456 @Test testAccessIsLaidOutInScreen()1457 public void testAccessIsLaidOutInScreen() { 1458 mPopupWindow = new PopupWindow(mActivity); 1459 assertFalse(mPopupWindow.isLaidOutInScreen()); 1460 1461 mPopupWindow.setIsLaidOutInScreen(true); 1462 assertTrue(mPopupWindow.isLaidOutInScreen()); 1463 } 1464 1465 @Test testAccessTouchModal()1466 public void testAccessTouchModal() { 1467 mPopupWindow = new PopupWindow(mActivity); 1468 assertTrue(mPopupWindow.isTouchModal()); 1469 1470 mPopupWindow.setTouchModal(false); 1471 assertFalse(mPopupWindow.isTouchModal()); 1472 } 1473 1474 @Test testAccessEpicenterBounds()1475 public void testAccessEpicenterBounds() { 1476 mPopupWindow = new PopupWindow(mActivity); 1477 assertNull(mPopupWindow.getEpicenterBounds()); 1478 1479 final Rect epicenter = new Rect(5, 10, 15, 20); 1480 1481 mPopupWindow.setEpicenterBounds(epicenter); 1482 assertEquals(mPopupWindow.getEpicenterBounds(), epicenter); 1483 1484 mPopupWindow.setEpicenterBounds(null); 1485 assertNull(mPopupWindow.getEpicenterBounds()); 1486 } 1487 1488 @Test testAccessOutsideTouchable()1489 public void testAccessOutsideTouchable() { 1490 mPopupWindow = new PopupWindow(mActivity); 1491 assertFalse(mPopupWindow.isOutsideTouchable()); 1492 1493 mPopupWindow.setOutsideTouchable(true); 1494 assertTrue(mPopupWindow.isOutsideTouchable()); 1495 } 1496 1497 @Test testAccessTouchable()1498 public void testAccessTouchable() { 1499 mPopupWindow = new PopupWindow(mActivity); 1500 assertTrue(mPopupWindow.isTouchable()); 1501 1502 mPopupWindow.setTouchable(false); 1503 assertFalse(mPopupWindow.isTouchable()); 1504 } 1505 1506 @Test testIsAboveAnchor()1507 public void testIsAboveAnchor() throws Throwable { 1508 mActivityRule.runOnUiThread(() -> mPopupWindow = createPopupWindow(createPopupContent( 1509 CONTENT_SIZE_DP, CONTENT_SIZE_DP))); 1510 mInstrumentation.waitForIdleSync(); 1511 final View upperAnchor = mActivity.findViewById(R.id.anchor_upper); 1512 1513 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(upperAnchor)); 1514 mInstrumentation.waitForIdleSync(); 1515 assertFalse(mPopupWindow.isAboveAnchor()); 1516 dismissPopup(); 1517 1518 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1519 final View lowerAnchor = mActivity.findViewById(R.id.anchor_lower); 1520 1521 mActivityRule.runOnUiThread(() -> mPopupWindow.showAsDropDown(lowerAnchor, 0, 0)); 1522 mInstrumentation.waitForIdleSync(); 1523 assertTrue(mPopupWindow.isAboveAnchor()); 1524 dismissPopup(); 1525 } 1526 1527 @Test testSetTouchInterceptor()1528 public void testSetTouchInterceptor() throws Throwable { 1529 final CountDownLatch latch = new CountDownLatch(1); 1530 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 1531 mActivityRule.runOnUiThread(() -> mTextView.setText("Testing")); 1532 ViewTreeObserver observer = mTextView.getViewTreeObserver(); 1533 observer.addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() { 1534 @Override 1535 public void onWindowFocusChanged(boolean hasFocus) { 1536 if (hasFocus) { 1537 ViewTreeObserver currentObserver = mTextView.getViewTreeObserver(); 1538 currentObserver.removeOnWindowFocusChangeListener(this); 1539 latch.countDown(); 1540 } 1541 } 1542 }); 1543 mPopupWindow = new PopupWindow(mTextView, LayoutParams.WRAP_CONTENT, 1544 LayoutParams.WRAP_CONTENT, true /* focusable */); 1545 1546 OnTouchListener onTouchListener = mock(OnTouchListener.class); 1547 when(onTouchListener.onTouch(any(View.class), any(MotionEvent.class))).thenReturn(true); 1548 1549 mPopupWindow.setTouchInterceptor(onTouchListener); 1550 mPopupWindow.setOutsideTouchable(true); 1551 Drawable drawable = new ColorDrawable(); 1552 mPopupWindow.setBackgroundDrawable(drawable); 1553 mPopupWindow.setAnimationStyle(0); 1554 showPopup(); 1555 mInstrumentation.waitForIdleSync(); 1556 1557 latch.await(2000, TimeUnit.MILLISECONDS); 1558 // Extra delay to allow input system to get fully set up (b/113686346) 1559 SystemClock.sleep(500); 1560 int[] xy = new int[2]; 1561 mPopupWindow.getContentView().getLocationOnScreen(xy); 1562 final int viewWidth = mPopupWindow.getContentView().getWidth(); 1563 final int viewHeight = mPopupWindow.getContentView().getHeight(); 1564 final float x = xy[0] + (viewWidth / 2.0f); 1565 float y = xy[1] + (viewHeight / 2.0f); 1566 1567 long downTime = SystemClock.uptimeMillis(); 1568 long eventTime = SystemClock.uptimeMillis(); 1569 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 1570 MotionEvent.ACTION_DOWN, x, y, 0); 1571 mInstrumentation.sendPointerSync(event); 1572 verify(onTouchListener, times(1)).onTouch(any(View.class), any(MotionEvent.class)); 1573 1574 downTime = SystemClock.uptimeMillis(); 1575 eventTime = SystemClock.uptimeMillis(); 1576 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 1577 mInstrumentation.sendPointerSync(event); 1578 verify(onTouchListener, times(2)).onTouch(any(View.class), any(MotionEvent.class)); 1579 1580 mPopupWindow.setTouchInterceptor(null); 1581 downTime = SystemClock.uptimeMillis(); 1582 eventTime = SystemClock.uptimeMillis(); 1583 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0); 1584 mInstrumentation.sendPointerSync(event); 1585 verify(onTouchListener, times(2)).onTouch(any(View.class), any(MotionEvent.class)); 1586 } 1587 1588 @Test testSetWindowLayoutMode()1589 public void testSetWindowLayoutMode() throws Throwable { 1590 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 1591 mInstrumentation.waitForIdleSync(); 1592 mPopupWindow = new PopupWindow(mTextView); 1593 showPopup(); 1594 1595 ViewGroup.LayoutParams p = mPopupWindow.getContentView().getRootView().getLayoutParams(); 1596 assertEquals(0, p.width); 1597 assertEquals(0, p.height); 1598 1599 mPopupWindow.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); 1600 mActivityRule.runOnUiThread(() -> mPopupWindow.update(20, 50, 50, 50)); 1601 1602 assertEquals(LayoutParams.WRAP_CONTENT, p.width); 1603 assertEquals(LayoutParams.MATCH_PARENT, p.height); 1604 } 1605 1606 @Test testAccessElevation()1607 public void testAccessElevation() throws Throwable { 1608 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1609 mActivityRule.runOnUiThread(() -> mPopupWindow.setElevation(2.0f)); 1610 1611 showPopup(); 1612 assertEquals(2.0f, mPopupWindow.getElevation(), 0.0f); 1613 1614 dismissPopup(); 1615 mActivityRule.runOnUiThread(() -> mPopupWindow.setElevation(4.0f)); 1616 showPopup(); 1617 assertEquals(4.0f, mPopupWindow.getElevation(), 0.0f); 1618 1619 dismissPopup(); 1620 mActivityRule.runOnUiThread(() -> mPopupWindow.setElevation(10.0f)); 1621 showPopup(); 1622 assertEquals(10.0f, mPopupWindow.getElevation(), 0.0f); 1623 } 1624 1625 @Test testAccessSoftInputMode()1626 public void testAccessSoftInputMode() throws Throwable { 1627 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1628 mActivityRule.runOnUiThread( 1629 () -> mPopupWindow.setSoftInputMode( 1630 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)); 1631 1632 showPopup(); 1633 assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE, 1634 mPopupWindow.getSoftInputMode()); 1635 1636 dismissPopup(); 1637 mActivityRule.runOnUiThread( 1638 () -> mPopupWindow.setSoftInputMode( 1639 WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)); 1640 showPopup(); 1641 assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN, 1642 mPopupWindow.getSoftInputMode()); 1643 } 1644 1645 @Test testAccessSplitTouchEnabled()1646 public void testAccessSplitTouchEnabled() throws Throwable { 1647 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1648 mActivityRule.runOnUiThread(() -> mPopupWindow.setSplitTouchEnabled(true)); 1649 1650 showPopup(); 1651 assertTrue(mPopupWindow.isSplitTouchEnabled()); 1652 1653 dismissPopup(); 1654 mActivityRule.runOnUiThread(() -> mPopupWindow.setSplitTouchEnabled(false)); 1655 showPopup(); 1656 assertFalse(mPopupWindow.isSplitTouchEnabled()); 1657 1658 dismissPopup(); 1659 mActivityRule.runOnUiThread(() -> mPopupWindow.setSplitTouchEnabled(true)); 1660 showPopup(); 1661 assertTrue(mPopupWindow.isSplitTouchEnabled()); 1662 } 1663 1664 @Test testVerticallyClippedBeforeAdjusted()1665 public void testVerticallyClippedBeforeAdjusted() throws Throwable { 1666 View parentWindowView = mActivity.getWindow().getDecorView(); 1667 int parentWidth = parentWindowView.getMeasuredWidth(); 1668 int parentHeight = parentWindowView.getMeasuredHeight(); 1669 1670 // We make a popup which is too large to fit within the parent window. 1671 // After showing it, we verify that it is shrunk to fit the window, 1672 // rather than adjusted up. 1673 mPopupWindow = createPopupWindow(createPopupContent(parentWidth*2, parentHeight*2)); 1674 mPopupWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT); 1675 mPopupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); 1676 1677 showPopup(R.id.anchor_middle); 1678 1679 View popupRoot = mPopupWindow.getContentView(); 1680 int measuredWidth = popupRoot.getMeasuredWidth(); 1681 int measuredHeight = popupRoot.getMeasuredHeight(); 1682 View anchor = mActivity.findViewById(R.id.anchor_middle); 1683 1684 // The popup should occupy all available vertical space, except the system bars. 1685 int[] anchorLocationInWindowXY = new int[2]; 1686 anchor.getLocationInWindow(anchorLocationInWindowXY); 1687 assertEquals(measuredHeight, 1688 parentHeight - (anchorLocationInWindowXY[1] + anchor.getHeight()) 1689 - parentWindowView.getRootWindowInsets().getSystemWindowInsetBottom()); 1690 1691 // The popup should be vertically aligned to the anchor's bottom edge. 1692 int[] anchorLocationOnScreenXY = new int[2]; 1693 anchor.getLocationOnScreen(anchorLocationOnScreenXY); 1694 int[] popupLocationOnScreenXY = new int[2]; 1695 popupRoot.getLocationOnScreen(popupLocationOnScreenXY); 1696 assertEquals(anchorLocationOnScreenXY[1] + anchor.getHeight(), popupLocationOnScreenXY[1]); 1697 } 1698 1699 @Test testClipToScreenClipsToInsets()1700 public void testClipToScreenClipsToInsets() throws Throwable { 1701 int[] orientationValues = {ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, 1702 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}; 1703 int currentOrientation = mActivity.getResources().getConfiguration().orientation; 1704 if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) { 1705 orientationValues[0] = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 1706 orientationValues[1] = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 1707 } 1708 1709 for (int i = 0; i < 2; i++) { 1710 final int orientation = orientationValues[i]; 1711 if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 1712 && !hasDeviceFeature(PackageManager.FEATURE_SCREEN_PORTRAIT)) { 1713 // skip test for devices not supporting portrait orientation 1714 continue; 1715 } 1716 mActivity.runOnUiThread(() -> 1717 mActivity.setRequestedOrientation(orientation)); 1718 mActivity.waitForConfigurationChanged(); 1719 // Wait for main thread to be idle to make sure layout and draw have been performed 1720 // before continuing. 1721 mInstrumentation.waitForIdleSync(); 1722 1723 View parentWindowView = mActivity.getWindow().getDecorView(); 1724 int parentWidth = parentWindowView.getMeasuredWidth(); 1725 int parentHeight = parentWindowView.getMeasuredHeight(); 1726 1727 mPopupWindow = createPopupWindow(createPopupContent(parentWidth*2, parentHeight*2)); 1728 mPopupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT); 1729 mPopupWindow.setHeight(WindowManager.LayoutParams.MATCH_PARENT); 1730 mPopupWindow.setIsClippedToScreen(true); 1731 1732 showPopup(R.id.anchor_upper_left); 1733 1734 View popupRoot = mPopupWindow.getContentView().getRootView(); 1735 int measuredWidth = popupRoot.getMeasuredWidth(); 1736 int measuredHeight = popupRoot.getMeasuredHeight(); 1737 1738 // The visible frame will not include the insets. 1739 Rect visibleFrame = new Rect(); 1740 parentWindowView.getWindowVisibleDisplayFrame(visibleFrame); 1741 1742 assertEquals(measuredWidth, visibleFrame.width()); 1743 assertEquals(measuredHeight, visibleFrame.height()); 1744 } 1745 } 1746 1747 @Test testPositionAfterParentScroll()1748 public void testPositionAfterParentScroll() throws Throwable { 1749 View.OnScrollChangeListener scrollChangeListener = mock( 1750 View.OnScrollChangeListener.class); 1751 1752 mActivityRule.runOnUiThread(() -> { 1753 mActivity.setContentView(R.layout.popup_window_scrollable); 1754 1755 View anchor = mActivity.findViewById(R.id.anchor_upper); 1756 PopupWindow window = createPopupWindow(); 1757 window.showAsDropDown(anchor); 1758 }); 1759 1760 mActivityRule.runOnUiThread(() -> { 1761 View parent = mActivity.findViewById(R.id.main_container); 1762 parent.scrollBy(0, 500); 1763 parent.setOnScrollChangeListener(scrollChangeListener); 1764 }); 1765 1766 verify(scrollChangeListener, never()).onScrollChange( 1767 any(View.class), anyInt(), anyInt(), anyInt(), anyInt()); 1768 } 1769 1770 @Test testPositionAfterAnchorRemoval()1771 public void testPositionAfterAnchorRemoval() throws Throwable { 1772 mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1773 showPopup(R.id.anchor_middle); 1774 1775 final ViewGroup container = (ViewGroup) mActivity.findViewById(R.id.main_container); 1776 final View anchor = mActivity.findViewById(R.id.anchor_middle); 1777 final LayoutParams anchorLayoutParams = anchor.getLayoutParams(); 1778 1779 final int[] originalLocation = new int[2]; 1780 mPopupWindow.getContentView().getLocationOnScreen(originalLocation); 1781 1782 final int deltaX = 30; 1783 final int deltaY = 20; 1784 1785 // Scroll the container, the popup should move along with the anchor. 1786 WidgetTestUtils.runOnMainAndLayoutSync( 1787 mActivityRule, 1788 mPopupWindow.getContentView().getRootView(), 1789 () -> container.scrollBy(deltaX, deltaY), 1790 false /* force layout */); 1791 // Since the first layout might have been caused by the original scroll event (and not by 1792 // the anchor change), we need to wait until all traversals are done. 1793 mInstrumentation.waitForIdleSync(); 1794 assertPopupLocation(originalLocation, deltaX, deltaY); 1795 1796 // Detach the anchor, the popup should stay in the same location. 1797 WidgetTestUtils.runOnMainAndLayoutSync( 1798 mActivityRule, 1799 mActivity.getWindow().getDecorView(), 1800 () -> container.removeView(anchor), 1801 false /* force layout */); 1802 assertPopupLocation(originalLocation, deltaX, deltaY); 1803 1804 // Scroll the container while the anchor is detached, the popup should not move. 1805 WidgetTestUtils.runOnMainAndLayoutSync( 1806 mActivityRule, 1807 mActivity.getWindow().getDecorView(), 1808 () -> container.scrollBy(deltaX, deltaY), 1809 true /* force layout */); 1810 mInstrumentation.waitForIdleSync(); 1811 assertPopupLocation(originalLocation, deltaX, deltaY); 1812 1813 // Re-attach the anchor, the popup should snap back to the new anchor location. 1814 WidgetTestUtils.runOnMainAndLayoutSync( 1815 mActivityRule, 1816 mPopupWindow.getContentView().getRootView(), 1817 () -> container.addView(anchor, anchorLayoutParams), 1818 false /* force layout */); 1819 assertPopupLocation(originalLocation, deltaX * 2, deltaY * 2); 1820 } 1821 1822 @Test testAnchorInPopup()1823 public void testAnchorInPopup() throws Throwable { 1824 DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); 1825 float dpWidth = displayMetrics.widthPixels / displayMetrics.density; 1826 float dpHeight = displayMetrics.heightPixels / displayMetrics.density; 1827 final int minDisplaySize = 320; 1828 if (dpWidth < minDisplaySize || dpHeight < minDisplaySize) { 1829 // On smaller screens the popups that this test is creating 1830 // are not guaranteed to be properly aligned to their anchors. 1831 return; 1832 } 1833 1834 mPopupWindow = createPopupWindow( 1835 mActivity.getLayoutInflater().inflate(R.layout.popup_window, null)); 1836 1837 final PopupWindow subPopup = 1838 createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP)); 1839 1840 // Check alignment without overlapping the anchor. 1841 assertFalse(subPopup.getOverlapAnchor()); 1842 1843 verifySubPopupPosition(subPopup, R.id.anchor_upper_left, R.id.anchor_lower_right, 1844 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1845 verifySubPopupPosition(subPopup, R.id.anchor_middle_left, R.id.anchor_lower_right, 1846 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1847 verifySubPopupPosition(subPopup, R.id.anchor_lower_left, R.id.anchor_lower_right, 1848 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1849 1850 verifySubPopupPosition(subPopup, R.id.anchor_upper, R.id.anchor_lower_right, 1851 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1852 verifySubPopupPosition(subPopup, R.id.anchor_middle, R.id.anchor_lower_right, 1853 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, BOTTOM); 1854 verifySubPopupPosition(subPopup, R.id.anchor_lower, R.id.anchor_lower_right, 1855 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1856 1857 verifySubPopupPosition(subPopup, R.id.anchor_upper_right, R.id.anchor_lower_right, 1858 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 1859 verifySubPopupPosition(subPopup, R.id.anchor_middle_right, R.id.anchor_lower_right, 1860 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, BOTTOM); 1861 verifySubPopupPosition(subPopup, R.id.anchor_lower_right, R.id.anchor_lower_right, 1862 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 1863 1864 // Check alignment while overlapping the anchor. 1865 subPopup.setOverlapAnchor(true); 1866 1867 final int anchorHeight = mActivity.findViewById(R.id.anchor_lower_right).getHeight(); 1868 // To simplify the math assert that all three lower anchors are the same height. 1869 assertEquals(anchorHeight, mActivity.findViewById(R.id.anchor_lower_left).getHeight()); 1870 assertEquals(anchorHeight, mActivity.findViewById(R.id.anchor_lower).getHeight()); 1871 1872 final int verticalSpaceBelowAnchor = anchorHeight * 2; 1873 // Ensure that the subpopup is flipped vertically. 1874 subPopup.setHeight(verticalSpaceBelowAnchor + 1); 1875 1876 verifySubPopupPosition(subPopup, R.id.anchor_upper_left, R.id.anchor_lower_right, 1877 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1878 verifySubPopupPosition(subPopup, R.id.anchor_middle_left, R.id.anchor_lower_right, 1879 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1880 verifySubPopupPosition(subPopup, R.id.anchor_lower_left, R.id.anchor_lower_right, 1881 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1882 1883 verifySubPopupPosition(subPopup, R.id.anchor_upper, R.id.anchor_lower_right, 1884 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1885 verifySubPopupPosition(subPopup, R.id.anchor_middle, R.id.anchor_lower_right, 1886 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1887 verifySubPopupPosition(subPopup, R.id.anchor_lower, R.id.anchor_lower_right, 1888 LEFT, EQUAL_TO, LEFT, BOTTOM, EQUAL_TO, TOP); 1889 1890 verifySubPopupPosition(subPopup, R.id.anchor_upper_right, R.id.anchor_lower_right, 1891 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 1892 verifySubPopupPosition(subPopup, R.id.anchor_middle_right, R.id.anchor_lower_right, 1893 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 1894 verifySubPopupPosition(subPopup, R.id.anchor_lower_right, R.id.anchor_lower_right, 1895 RIGHT, EQUAL_TO, RIGHT, BOTTOM, EQUAL_TO, TOP); 1896 1897 // Re-test for the bottom anchor row ensuring that the subpopup not flipped vertically. 1898 subPopup.setHeight(verticalSpaceBelowAnchor - 1); 1899 1900 verifySubPopupPosition(subPopup, R.id.anchor_lower_left, R.id.anchor_lower_right, 1901 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1902 verifySubPopupPosition(subPopup, R.id.anchor_lower, R.id.anchor_lower_right, 1903 LEFT, EQUAL_TO, LEFT, TOP, EQUAL_TO, TOP); 1904 verifySubPopupPosition(subPopup, R.id.anchor_lower_right, R.id.anchor_lower_right, 1905 RIGHT, EQUAL_TO, RIGHT, TOP, EQUAL_TO, TOP); 1906 1907 // Check that scrolling scrolls the sub popup along with the main popup. 1908 showPopup(R.id.anchor_middle); 1909 1910 mActivityRule.runOnUiThread(() -> subPopup.showAsDropDown( 1911 mPopupWindow.getContentView().findViewById(R.id.anchor_middle))); 1912 mInstrumentation.waitForIdleSync(); 1913 1914 final int[] popupLocation = new int[2]; 1915 mPopupWindow.getContentView().getLocationOnScreen(popupLocation); 1916 final int[] subPopupLocation = new int[2]; 1917 subPopup.getContentView().getLocationOnScreen(subPopupLocation); 1918 1919 final int deltaX = 20; 1920 final int deltaY = 30; 1921 1922 final ViewGroup container = (ViewGroup) mActivity.findViewById(R.id.main_container); 1923 WidgetTestUtils.runOnMainAndLayoutSync( 1924 mActivityRule, 1925 subPopup.getContentView().getRootView(), 1926 () -> container.scrollBy(deltaX, deltaY), 1927 false /* force layout */); 1928 1929 // Since the first layout might have been caused by the original scroll event (and not by 1930 // the anchor change), we need to wait until all traversals are done. 1931 mInstrumentation.waitForIdleSync(); 1932 1933 final int[] newPopupLocation = new int[2]; 1934 mPopupWindow.getContentView().getLocationOnScreen(newPopupLocation); 1935 assertEquals(popupLocation[0] - deltaX, newPopupLocation[0]); 1936 assertEquals(popupLocation[1] - deltaY, newPopupLocation[1]); 1937 1938 final int[] newSubPopupLocation = new int[2]; 1939 subPopup.getContentView().getLocationOnScreen(newSubPopupLocation); 1940 assertEquals(subPopupLocation[0] - deltaX, newSubPopupLocation[0]); 1941 assertEquals(subPopupLocation[1] - deltaY, newSubPopupLocation[1]); 1942 } 1943 verifySubPopupPosition(PopupWindow subPopup, int mainAnchorId, int subAnchorId, int contentEdgeX, int operatorX, int anchorEdgeX, int contentEdgeY, int operatorY, int anchorEdgeY)1944 private void verifySubPopupPosition(PopupWindow subPopup, int mainAnchorId, int subAnchorId, 1945 int contentEdgeX, int operatorX, int anchorEdgeX, 1946 int contentEdgeY, int operatorY, int anchorEdgeY) throws Throwable { 1947 showPopup(mainAnchorId); 1948 verifyPosition(subPopup, mPopupWindow.getContentView().findViewById(subAnchorId), 1949 contentEdgeX, operatorX, anchorEdgeX, contentEdgeY, operatorY, anchorEdgeY); 1950 dismissPopup(); 1951 } 1952 assertPopupLocation(int[] originalLocation, int deltaX, int deltaY)1953 private void assertPopupLocation(int[] originalLocation, int deltaX, int deltaY) { 1954 final int[] actualLocation = new int[2]; 1955 mPopupWindow.getContentView().getLocationOnScreen(actualLocation); 1956 assertEquals(originalLocation[0] - deltaX, actualLocation[0]); 1957 assertEquals(originalLocation[1] - deltaY, actualLocation[1]); 1958 } 1959 1960 private static class BaseTransition extends Transition { 1961 @Override captureStartValues(TransitionValues transitionValues)1962 public void captureStartValues(TransitionValues transitionValues) {} 1963 1964 @Override captureEndValues(TransitionValues transitionValues)1965 public void captureEndValues(TransitionValues transitionValues) {} 1966 } 1967 createPopupContent(int width, int height)1968 private View createPopupContent(int width, int height) { 1969 final View popupView = new View(mActivity); 1970 popupView.setLayoutParams(new ViewGroup.LayoutParams(width, height)); 1971 popupView.setBackgroundColor(Color.MAGENTA); 1972 1973 return popupView; 1974 } 1975 createPopupWindow()1976 private PopupWindow createPopupWindow() { 1977 PopupWindow window = new PopupWindow(mActivity); 1978 window.setWidth(WINDOW_SIZE_DP); 1979 window.setHeight(WINDOW_SIZE_DP); 1980 window.setBackgroundDrawable(new ColorDrawable(Color.YELLOW)); 1981 return window; 1982 } 1983 createPopupWindow(View content)1984 private PopupWindow createPopupWindow(View content) { 1985 PopupWindow window = createPopupWindow(); 1986 window.setContentView(content); 1987 return window; 1988 } 1989 hasDeviceFeature(final String requiredFeature)1990 private boolean hasDeviceFeature(final String requiredFeature) { 1991 return mContext.getPackageManager().hasSystemFeature(requiredFeature); 1992 } 1993 showPopup(int resourceId)1994 private void showPopup(int resourceId) throws Throwable { 1995 mActivityRule.runOnUiThread(() -> { 1996 if (mPopupWindow == null || mPopupWindow.isShowing()) { 1997 return; 1998 } 1999 View anchor = mActivity.findViewById(resourceId); 2000 mPopupWindow.showAsDropDown(anchor); 2001 assertTrue(mPopupWindow.isShowing()); 2002 }); 2003 mInstrumentation.waitForIdleSync(); 2004 } 2005 showPopup()2006 private void showPopup() throws Throwable { 2007 showPopup(R.id.anchor_upper_left); 2008 } 2009 dismissPopup()2010 private void dismissPopup() throws Throwable { 2011 mActivityRule.runOnUiThread(() -> { 2012 if (mPopupWindow == null || !mPopupWindow.isShowing()) { 2013 return; 2014 } 2015 mPopupWindow.dismiss(); 2016 }); 2017 mInstrumentation.waitForIdleSync(); 2018 } 2019 } 2020