• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertSame;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 import static org.mockito.Mockito.any;
27 import static org.mockito.Mockito.doCallRealMethod;
28 import static org.mockito.Mockito.eq;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.verifyNoMoreInteractions;
35 
36 import android.app.Activity;
37 import android.app.Instrumentation;
38 import android.content.Context;
39 import android.graphics.Rect;
40 import android.graphics.drawable.ColorDrawable;
41 import android.graphics.drawable.Drawable;
42 import android.platform.test.annotations.Presubmit;
43 import android.view.Display;
44 import android.view.Gravity;
45 import android.view.KeyEvent;
46 import android.view.LayoutInflater;
47 import android.view.View;
48 import android.view.ViewGroup;
49 import android.view.WindowManager;
50 import android.widget.AdapterView;
51 import android.widget.BaseAdapter;
52 import android.widget.ListAdapter;
53 import android.widget.ListPopupWindow;
54 import android.widget.ListView;
55 import android.widget.PopupWindow;
56 import android.widget.TextView;
57 
58 import androidx.test.InstrumentationRegistry;
59 import androidx.test.filters.LargeTest;
60 import androidx.test.rule.ActivityTestRule;
61 import androidx.test.runner.AndroidJUnit4;
62 
63 import com.android.compatibility.common.util.CtsKeyEventUtil;
64 import com.android.compatibility.common.util.CtsTouchUtils;
65 import com.android.compatibility.common.util.PollingCheck;
66 import com.android.compatibility.common.util.WidgetTestUtils;
67 
68 import junit.framework.Assert;
69 
70 import org.junit.After;
71 import org.junit.Before;
72 import org.junit.Rule;
73 import org.junit.Test;
74 import org.junit.runner.RunWith;
75 
76 import java.util.concurrent.CountDownLatch;
77 import java.util.concurrent.TimeUnit;
78 
79 @LargeTest
80 @RunWith(AndroidJUnit4.class)
81 public class ListPopupWindowTest {
82     private Instrumentation mInstrumentation;
83     private Activity mActivity;
84     private Builder mPopupWindowBuilder;
85     private View promptView;
86 
87     /** The list popup window. */
88     private ListPopupWindow mPopupWindow;
89 
90     private AdapterView.OnItemClickListener mItemClickListener;
91 
92     /**
93      * Item click listener that dismisses our <code>ListPopupWindow</code> when any item
94      * is clicked. Note that this needs to be a separate class that is also protected (not
95      * private) so that Mockito can "spy" on it.
96      */
97     protected class PopupItemClickListener implements AdapterView.OnItemClickListener {
98         @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)99         public void onItemClick(AdapterView<?> parent, View view, int position,
100                 long id) {
101             mPopupWindow.dismiss();
102         }
103     }
104 
105     @Rule
106     public ActivityTestRule<ListPopupWindowCtsActivity> mActivityRule
107             = new ActivityTestRule<>(ListPopupWindowCtsActivity.class);
108 
109     @Before
setup()110     public void setup() {
111         mInstrumentation = InstrumentationRegistry.getInstrumentation();
112         mActivity = mActivityRule.getActivity();
113         mItemClickListener = new PopupItemClickListener();
114     }
115 
116     @After
teardown()117     public void teardown() {
118         if ((mPopupWindow != null) && (mPopupWindow.isShowing())) {
119             final CountDownLatch dismissLatch = new CountDownLatch(1);
120             try {
121                 mPopupWindow.setOnDismissListener(dismissLatch::countDown);
122                 mActivityRule.runOnUiThread(mPopupWindow::dismiss);
123                 Assert.assertTrue("Expected popup dismissal occurred within 5 seconds",
124                         dismissLatch.await(5, TimeUnit.SECONDS));
125             } catch (Throwable t) {
126                 throw new RuntimeException(t);
127             }
128         }
129     }
130 
131     @Test
testConstructor()132     public void testConstructor() {
133         new ListPopupWindow(mActivity);
134 
135         new ListPopupWindow(mActivity, null);
136 
137         new ListPopupWindow(mActivity, null, android.R.attr.popupWindowStyle);
138 
139         new ListPopupWindow(mActivity, null, 0,
140                 android.R.style.Widget_DeviceDefault_ListPopupWindow);
141 
142         new ListPopupWindow(mActivity, null, 0,
143                 android.R.style.Widget_DeviceDefault_Light_ListPopupWindow);
144 
145         new ListPopupWindow(mActivity, null, 0, android.R.style.Widget_Material_ListPopupWindow);
146 
147         new ListPopupWindow(mActivity, null, 0,
148                 android.R.style.Widget_Material_Light_ListPopupWindow);
149     }
150 
151     @Test
testNoDefaultVisibility()152     public void testNoDefaultVisibility() {
153         mPopupWindow = new ListPopupWindow(mActivity);
154         assertFalse(mPopupWindow.isShowing());
155     }
156 
157     @Test
testAccessBackground()158     public void testAccessBackground() {
159         mPopupWindowBuilder = new Builder();
160         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
161                 mPopupWindowBuilder::show);
162 
163         Drawable drawable = new ColorDrawable();
164         mPopupWindow.setBackgroundDrawable(drawable);
165         assertSame(drawable, mPopupWindow.getBackground());
166 
167         mPopupWindow.setBackgroundDrawable(null);
168         assertNull(mPopupWindow.getBackground());
169     }
170 
171     @Test
testAccessAnimationStyle()172     public void testAccessAnimationStyle() {
173         mPopupWindowBuilder = new Builder();
174         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
175                 mPopupWindowBuilder::show);
176         assertEquals(0, mPopupWindow.getAnimationStyle());
177 
178         mPopupWindow.setAnimationStyle(android.R.style.Animation_Toast);
179         assertEquals(android.R.style.Animation_Toast, mPopupWindow.getAnimationStyle());
180 
181         // abnormal values
182         mPopupWindow.setAnimationStyle(-100);
183         assertEquals(-100, mPopupWindow.getAnimationStyle());
184     }
185 
186     @Test
testAccessHeight()187     public void testAccessHeight() {
188         mPopupWindowBuilder = new Builder();
189         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
190                 mPopupWindowBuilder::show);
191 
192         assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getHeight());
193 
194         int height = getDisplay().getHeight() / 2;
195         mPopupWindow.setHeight(height);
196         assertEquals(height, mPopupWindow.getHeight());
197 
198         height = getDisplay().getHeight();
199         mPopupWindow.setHeight(height);
200         assertEquals(height, mPopupWindow.getHeight());
201 
202         mPopupWindow.setHeight(0);
203         assertEquals(0, mPopupWindow.getHeight());
204 
205         height = getDisplay().getHeight() * 2;
206         mPopupWindow.setHeight(height);
207         assertEquals(height, mPopupWindow.getHeight());
208 
209         height = -getDisplay().getHeight() / 2;
210         try {
211             mPopupWindow.setHeight(height);
212             fail("should throw IllegalArgumentException for negative height.");
213         } catch (IllegalArgumentException e) {
214             // expected exception.
215         }
216     }
217 
218     /**
219      * Gets the display.
220      *
221      * @return the display
222      */
getDisplay()223     private Display getDisplay() {
224         WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
225         return wm.getDefaultDisplay();
226     }
227 
228     @Test
testAccessWidth()229     public void testAccessWidth() {
230         mPopupWindowBuilder = new Builder().ignoreContentWidth();
231         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule,
232                 mActivity.getWindow().getDecorView(), mPopupWindowBuilder::show);
233 
234         assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getWidth());
235 
236         int width = getDisplay().getWidth() / 2;
237         mPopupWindow.setWidth(width);
238         assertEquals(width, mPopupWindow.getWidth());
239 
240         width = getDisplay().getWidth();
241         mPopupWindow.setWidth(width);
242         assertEquals(width, mPopupWindow.getWidth());
243 
244         mPopupWindow.setWidth(0);
245         assertEquals(0, mPopupWindow.getWidth());
246 
247         width = getDisplay().getWidth() * 2;
248         mPopupWindow.setWidth(width);
249         assertEquals(width, mPopupWindow.getWidth());
250 
251         width = - getDisplay().getWidth() / 2;
252         mPopupWindow.setWidth(width);
253         assertEquals(width, mPopupWindow.getWidth());
254     }
255 
verifyAnchoring(int horizontalOffset, int verticalOffset, int gravity)256     private void verifyAnchoring(int horizontalOffset, int verticalOffset, int gravity) {
257         final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
258         final ListView listView = mPopupWindow.getListView();
259         int[] anchorXY = new int[2];
260         int[] listViewOnScreenXY = new int[2];
261         int[] listViewInWindowXY = new int[2];
262 
263         assertTrue(mPopupWindow.isShowing());
264         assertEquals(upperAnchor, mPopupWindow.getAnchorView());
265 
266         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
267                 () -> {
268                     listView.getLocationOnScreen(listViewOnScreenXY);
269                     upperAnchor.getLocationOnScreen(anchorXY);
270                     listView.getLocationInWindow(listViewInWindowXY);
271                 });
272 
273         int expectedListViewOnScreenX = anchorXY[0] + listViewInWindowXY[0] + horizontalOffset;
274         final int absoluteGravity =
275                 Gravity.getAbsoluteGravity(gravity, upperAnchor.getLayoutDirection());
276         if (absoluteGravity == Gravity.RIGHT) {
277             expectedListViewOnScreenX -= (listView.getWidth() - upperAnchor.getWidth());
278         } else {
279             // On narrow screens, it's possible for the popup to reach the edge
280             // of the screen.
281             int rightmostX =
282                     getDisplay().getWidth() - mPopupWindow.getWidth() + listViewInWindowXY[0];
283             if (expectedListViewOnScreenX > rightmostX) {
284                 expectedListViewOnScreenX = rightmostX;
285             }
286         }
287         int expectedListViewOnScreenY = anchorXY[1] + listViewInWindowXY[1]
288                 + upperAnchor.getHeight() + verticalOffset;
289         assertEquals(expectedListViewOnScreenX, listViewOnScreenXY[0]);
290         assertEquals(expectedListViewOnScreenY, listViewOnScreenXY[1]);
291     }
292 
293     @Test
testAnchoring()294     public void testAnchoring() {
295         mPopupWindowBuilder = new Builder();
296         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
297                 mPopupWindowBuilder::show);
298 
299         PollingCheck.waitFor(()-> mPopupWindow.isShowing());
300         assertEquals(0, mPopupWindow.getHorizontalOffset());
301         assertEquals(0, mPopupWindow.getVerticalOffset());
302 
303         verifyAnchoring(0, 0, Gravity.NO_GRAVITY);
304     }
305 
306     @Test
testAnchoringWithHorizontalOffset()307     public void testAnchoringWithHorizontalOffset() {
308         mPopupWindowBuilder = new Builder().withHorizontalOffset(50);
309         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
310                 mPopupWindowBuilder::show);
311 
312         PollingCheck.waitFor(()-> mPopupWindow.isShowing());
313         assertEquals(50, mPopupWindow.getHorizontalOffset());
314         assertEquals(0, mPopupWindow.getVerticalOffset());
315 
316         verifyAnchoring(50, 0, Gravity.NO_GRAVITY);
317     }
318 
319     @Test
testAnchoringWithVerticalOffset()320     public void testAnchoringWithVerticalOffset() {
321         mPopupWindowBuilder = new Builder().withVerticalOffset(60);
322         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
323                 mPopupWindowBuilder::show);
324 
325         PollingCheck.waitFor(()-> mPopupWindow.isShowing());
326         assertEquals(0, mPopupWindow.getHorizontalOffset());
327         assertEquals(60, mPopupWindow.getVerticalOffset());
328 
329         verifyAnchoring(0, 60, Gravity.NO_GRAVITY);
330     }
331 
332     @Test
testAnchoringWithRightGravity()333     public void testAnchoringWithRightGravity() {
334         mPopupWindowBuilder = new Builder().withDropDownGravity(Gravity.RIGHT);
335         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
336                 mPopupWindowBuilder::show);
337 
338         PollingCheck.waitFor(()-> mPopupWindow.isShowing());
339         assertEquals(0, mPopupWindow.getHorizontalOffset());
340         assertEquals(0, mPopupWindow.getVerticalOffset());
341 
342         verifyAnchoring(0, 0, Gravity.RIGHT);
343     }
344 
345     @Test
testAnchoringWithEndGravity()346     public void testAnchoringWithEndGravity() {
347         mPopupWindowBuilder = new Builder().withDropDownGravity(Gravity.END);
348         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
349                 mPopupWindowBuilder::show);
350 
351         PollingCheck.waitFor(()-> mPopupWindow.isShowing());
352         assertEquals(0, mPopupWindow.getHorizontalOffset());
353         assertEquals(0, mPopupWindow.getVerticalOffset());
354 
355         verifyAnchoring(0, 0, Gravity.END);
356     }
357 
358     @Test
testSetWindowLayoutType()359     public void testSetWindowLayoutType() {
360         mPopupWindowBuilder = new Builder().withWindowLayoutType(
361                 WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
362         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
363                 mPopupWindowBuilder::show);
364         assertTrue(mPopupWindow.isShowing());
365 
366         WindowManager.LayoutParams p = (WindowManager.LayoutParams)
367                 mPopupWindow.getListView().getRootView().getLayoutParams();
368         assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, p.type);
369     }
370 
371     @Test
testDismiss()372     public void testDismiss() {
373         mPopupWindowBuilder = new Builder();
374         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
375                 mPopupWindowBuilder::show);
376         assertTrue(mPopupWindow.isShowing());
377 
378         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
379                 mPopupWindowBuilder::dismiss);
380         assertFalse(mPopupWindow.isShowing());
381 
382         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
383                 mPopupWindowBuilder::dismiss);
384         assertFalse(mPopupWindow.isShowing());
385     }
386 
387     @Test
testSetOnDismissListener()388     public void testSetOnDismissListener() {
389         mPopupWindowBuilder = new Builder().withDismissListener();
390         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
391                 mPopupWindowBuilder::show);
392         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
393                 mPopupWindowBuilder::dismiss);
394         verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
395 
396         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
397                 mPopupWindowBuilder::showAgain);
398         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
399                 mPopupWindowBuilder::dismiss);
400         verify(mPopupWindowBuilder.mOnDismissListener, times(2)).onDismiss();
401 
402         mPopupWindow.setOnDismissListener(null);
403         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
404                 mPopupWindowBuilder::showAgain);
405         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
406                 mPopupWindowBuilder::dismiss);
407         // Since we've reset the listener to null, we are not expecting any more interactions
408         // on the previously registered listener.
409         verifyNoMoreInteractions(mPopupWindowBuilder.mOnDismissListener);
410     }
411 
412     @Test
testAccessEpicenterBounds()413     public void testAccessEpicenterBounds() {
414         mPopupWindow = new ListPopupWindow(mActivity);
415         assertNull(mPopupWindow.getEpicenterBounds());
416 
417         final Rect epicenter = new Rect(5, 10, 15, 20);
418 
419         mPopupWindow.setEpicenterBounds(epicenter);
420         assertEquals(mPopupWindow.getEpicenterBounds(), epicenter);
421 
422         mPopupWindow.setEpicenterBounds(null);
423         assertNull(mPopupWindow.getEpicenterBounds());
424     }
425 
426     @Test
testAccessInputMethodMode()427     public void testAccessInputMethodMode() {
428         mPopupWindowBuilder = new Builder().withDismissListener();
429         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
430                 mPopupWindowBuilder::show);
431 
432         assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode());
433         assertFalse(mPopupWindow.isInputMethodNotNeeded());
434 
435         mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE);
436         assertEquals(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE, mPopupWindow.getInputMethodMode());
437         assertFalse(mPopupWindow.isInputMethodNotNeeded());
438 
439         mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
440         assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode());
441         assertFalse(mPopupWindow.isInputMethodNotNeeded());
442 
443         mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
444         assertEquals(PopupWindow.INPUT_METHOD_NOT_NEEDED, mPopupWindow.getInputMethodMode());
445         assertTrue(mPopupWindow.isInputMethodNotNeeded());
446 
447         mPopupWindow.setInputMethodMode(-1);
448         assertEquals(-1, mPopupWindow.getInputMethodMode());
449         assertFalse(mPopupWindow.isInputMethodNotNeeded());
450     }
451 
452     @Test
testAccessSoftInputMethodMode()453     public void testAccessSoftInputMethodMode() {
454         mPopupWindowBuilder = new Builder().withDismissListener();
455         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
456                 mPopupWindowBuilder::show);
457 
458         mPopupWindow = new ListPopupWindow(mActivity);
459         assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED,
460                 mPopupWindow.getSoftInputMode());
461 
462         mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
463         assertEquals(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
464                 mPopupWindow.getSoftInputMode());
465 
466         mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
467         assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE,
468                 mPopupWindow.getSoftInputMode());
469     }
470 
verifyDismissalViaTouch(boolean setupAsModal)471     private void verifyDismissalViaTouch(boolean setupAsModal) {
472         // Register a click listener on the top-level container
473         final View mainContainer = mActivity.findViewById(R.id.main_container);
474         final View.OnClickListener mockContainerClickListener = mock(View.OnClickListener.class);
475         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
476                 () -> mainContainer.setOnClickListener(mockContainerClickListener));
477 
478         // Configure a list popup window with requested modality
479         mPopupWindowBuilder = new Builder().setModal(setupAsModal).withDismissListener();
480         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
481                 mPopupWindowBuilder::show);
482 
483         assertTrue("Popup window showing", mPopupWindow.isShowing());
484         // Make sure that the modality of the popup window is set up correctly
485         assertEquals("Popup window modality", setupAsModal, mPopupWindow.isModal());
486 
487         // The logic below uses Instrumentation to emulate a tap outside the bounds of the
488         // displayed list popup window. This tap is then treated by the framework to be "split" as
489         // the ACTION_OUTSIDE for the popup itself, as well as DOWN / MOVE / UP for the underlying
490         // view root if the popup is not modal.
491         // It is not correct to emulate these two sequences separately in the test, as it
492         // wouldn't emulate the user-facing interaction for this test. Also, we don't want to use
493         // View.dispatchTouchEvent directly as that would require emulation of two separate
494         // sequences as well.
495         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
496         final ListView popupListView = mPopupWindow.getListView();
497         final Rect rect = new Rect();
498         mPopupWindow.getBackground().getPadding(rect);
499         CtsTouchUtils.emulateTapOnView(instrumentation, mActivityRule, popupListView,
500                 -rect.left - 20, popupListView.getHeight() + rect.top + rect.bottom + 20);
501 
502         // At this point our popup should not be showing and should have notified its
503         // dismiss listener
504         verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
505         assertFalse("Popup window not showing after outside click", mPopupWindow.isShowing());
506 
507         // Also test that the click outside the popup bounds has been "delivered" to the main
508         // container only if the popup is not modal
509         verify(mockContainerClickListener, times(setupAsModal ? 0 : 1)).onClick(mainContainer);
510     }
511 
512     @Test
testDismissalOutsideNonModal()513     public void testDismissalOutsideNonModal() {
514         verifyDismissalViaTouch(false);
515     }
516 
517     @Test
testDismissalOutsideModal()518     public void testDismissalOutsideModal() {
519         verifyDismissalViaTouch(true);
520     }
521 
522     @Test
testItemClicks()523     public void testItemClicks() {
524         mPopupWindowBuilder = new Builder().withItemClickListener().withDismissListener();
525         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
526                 mPopupWindowBuilder::show);
527         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
528                 () -> mPopupWindow.performItemClick(2));
529 
530         verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
531                 any(AdapterView.class), any(View.class), eq(2), eq(2L));
532         // Also verify that the popup window has been dismissed
533         assertFalse(mPopupWindow.isShowing());
534         verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
535 
536         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
537                 mPopupWindowBuilder::showAgain);
538         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
539                 () -> mPopupWindow.getListView().performItemClick(null, 1, 1));
540 
541         verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
542                 any(AdapterView.class), any(), eq(1), eq(1L));
543         // Also verify that the popup window has been dismissed
544         assertFalse(mPopupWindow.isShowing());
545         verify(mPopupWindowBuilder.mOnDismissListener, times(2)).onDismiss();
546 
547         // Finally verify that our item click listener has only been called twice
548         verifyNoMoreInteractions(mPopupWindowBuilder.mOnItemClickListener);
549     }
550 
551     @Test
testPromptViewAbove()552     public void testPromptViewAbove() {
553         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
554                 () -> {
555                     promptView = LayoutInflater.from(mActivity).inflate(R.layout.popupwindow_prompt,
556                             null);
557                     mPopupWindowBuilder = new Builder().withPrompt(
558                             promptView, ListPopupWindow.POSITION_PROMPT_ABOVE);
559                     mPopupWindowBuilder.show();
560                 });
561 
562         // Verify that our prompt is displayed on the screen and is above the first list item
563         assertTrue(promptView.isAttachedToWindow());
564         assertTrue(promptView.isShown());
565         assertEquals(ListPopupWindow.POSITION_PROMPT_ABOVE, mPopupWindow.getPromptPosition());
566 
567         final ListView listView = mPopupWindow.getListView();
568         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, listView, null);
569 
570         final int[] promptViewOnScreenXY = new int[2];
571         final int[] firstChildOnScreenXY = new int[2];
572 
573         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
574                 () -> {
575                     promptView.getLocationOnScreen(promptViewOnScreenXY);
576 
577                     final View firstListChild = listView.getChildAt(0);
578                     firstListChild.getLocationOnScreen(firstChildOnScreenXY);
579                 });
580 
581         assertTrue(promptViewOnScreenXY[1] + promptView.getHeight() <= firstChildOnScreenXY[1]);
582     }
583 
584     @Test
testPromptViewBelow()585     public void testPromptViewBelow() {
586         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
587                 () -> {
588                     promptView = LayoutInflater.from(mActivity).inflate(R.layout.popupwindow_prompt,
589                             null);
590                     mPopupWindowBuilder = new Builder().withPrompt(
591                             promptView, ListPopupWindow.POSITION_PROMPT_BELOW);
592                     mPopupWindowBuilder.show();
593                 });
594 
595         // Verify that our prompt is displayed on the screen and is below the last list item
596         assertTrue(promptView.isAttachedToWindow());
597         assertTrue(promptView.isShown());
598         assertEquals(ListPopupWindow.POSITION_PROMPT_BELOW, mPopupWindow.getPromptPosition());
599 
600         final ListView listView = mPopupWindow.getListView();
601         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, listView, null);
602 
603         final int[] promptViewOnScreenXY = new int[2];
604         final int[] lastChildOnScreenXY = new int[2];
605 
606         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
607                 () -> {
608                     promptView.getLocationOnScreen(promptViewOnScreenXY);
609 
610                     final View lastListChild = listView.getChildAt(listView.getChildCount() - 1);
611                     lastListChild.getLocationOnScreen(lastChildOnScreenXY);
612                 });
613 
614         // The child is above the prompt. They may overlap, as in the case
615         // when the list items do not all fit on screen, but this is still
616         // correct.
617         assertTrue(lastChildOnScreenXY[1] <= promptViewOnScreenXY[1]);
618     }
619 
620     @Presubmit
621     @Test
testAccessSelection()622     public void testAccessSelection() {
623         mPopupWindowBuilder = new Builder().withItemSelectedListener();
624         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
625                 mPopupWindowBuilder::show);
626         PollingCheck.waitFor(()-> mPopupWindow.isShowing());
627 
628         final ListView listView = mPopupWindow.getListView();
629 
630         // Select an item
631         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, listView,
632                 () -> mPopupWindow.setSelection(1));
633         PollingCheck.waitFor(()-> mPopupWindow.getSelectedItemPosition() == 1);
634 
635         // And verify the current selection state + selection listener invocation
636         verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
637                 any(AdapterView.class), any(View.class), eq(1), eq(1L));
638         assertEquals(1, mPopupWindow.getSelectedItemId());
639         assertEquals(1, mPopupWindow.getSelectedItemPosition());
640         assertEquals("Bob", mPopupWindow.getSelectedItem());
641         View selectedView = mPopupWindow.getSelectedView();
642         assertNotNull(selectedView);
643         assertEquals("Bob",
644                 ((TextView) selectedView.findViewById(android.R.id.text1)).getText());
645 
646         // Select another item
647         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, listView,
648                 () -> mPopupWindow.setSelection(3));
649         PollingCheck.waitFor(()-> mPopupWindow.getSelectedItemPosition() == 3);
650 
651         // And verify the new selection state + selection listener invocation
652         verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
653                 any(AdapterView.class), any(View.class), eq(3), eq(3L));
654         assertEquals(3, mPopupWindow.getSelectedItemId());
655         assertEquals(3, mPopupWindow.getSelectedItemPosition());
656         assertEquals("Deirdre", mPopupWindow.getSelectedItem());
657         selectedView = mPopupWindow.getSelectedView();
658         assertNotNull(selectedView);
659         assertEquals("Deirdre",
660                 ((TextView) selectedView.findViewById(android.R.id.text1)).getText());
661 
662         // Clear selection
663         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, listView,
664                 mPopupWindow::clearListSelection);
665 
666         // And verify empty selection state + no more selection listener invocation
667         verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onNothingSelected(
668                 any(AdapterView.class));
669         assertEquals(AdapterView.INVALID_ROW_ID, mPopupWindow.getSelectedItemId());
670         assertEquals(AdapterView.INVALID_POSITION, mPopupWindow.getSelectedItemPosition());
671         assertNull(mPopupWindow.getSelectedItem());
672         assertNull(mPopupWindow.getSelectedView());
673         verifyNoMoreInteractions(mPopupWindowBuilder.mOnItemSelectedListener);
674     }
675 
676     @Test
testNoDefaultDismissalWithBackButton()677     public void testNoDefaultDismissalWithBackButton() {
678         mPopupWindowBuilder = new Builder().withDismissListener();
679         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
680                 mPopupWindowBuilder::show);
681 
682         // Send BACK key event. As we don't have any custom code that dismisses ListPopupWindow,
683         // and ListPopupWindow doesn't track that system-level key event on its own, ListPopupWindow
684         // should stay visible
685         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
686         verify(mPopupWindowBuilder.mOnDismissListener, never()).onDismiss();
687         assertTrue(mPopupWindow.isShowing());
688     }
689 
690     @Test
testCustomDismissalWithBackButton()691     public void testCustomDismissalWithBackButton() {
692         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
693                 () -> {
694                     mPopupWindowBuilder = new Builder().withAnchor(R.id.anchor_upper_left)
695                             .withDismissListener();
696                     mPopupWindowBuilder.show();
697                 });
698 
699         // "Point" our custom extension of EditText to our ListPopupWindow
700         final MockViewForListPopupWindow anchor =
701                 (MockViewForListPopupWindow) mPopupWindow.getAnchorView();
702         anchor.wireTo(mPopupWindow);
703         // Request focus on our EditText
704         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
705                 anchor::requestFocus);
706         assertTrue(anchor.isFocused());
707 
708         // Send BACK key event. As our custom extension of EditText calls
709         // ListPopupWindow.onKeyPreIme, the end result should be the dismissal of the
710         // ListPopupWindow
711         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
712         verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
713         assertFalse(mPopupWindow.isShowing());
714     }
715 
716     @Test
testListSelectionWithDPad()717     public void testListSelectionWithDPad() {
718         mPopupWindowBuilder = new Builder().withAnchor(R.id.anchor_upper_left)
719                 .withDismissListener().withItemSelectedListener();
720         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
721                 mPopupWindowBuilder::show);
722 
723         final View root = mPopupWindow.getListView().getRootView();
724 
725         // "Point" our custom extension of EditText to our ListPopupWindow
726         final MockViewForListPopupWindow anchor =
727                 (MockViewForListPopupWindow) mPopupWindow.getAnchorView();
728         anchor.wireTo(mPopupWindow);
729         // Request focus on our EditText
730         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
731                 anchor::requestFocus);
732         assertTrue(anchor.isFocused());
733 
734         // Select entry #1 in the popup list
735         final ListView listView = mPopupWindow.getListView();
736         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, listView,
737                 () -> mPopupWindow.setSelection(1));
738         PollingCheck.waitFor(()-> mPopupWindow.getSelectedItemPosition() == 1);
739         verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
740                 any(AdapterView.class), any(View.class), eq(1), eq(1L));
741 
742         // Send DPAD_DOWN key event. As our custom extension of EditText calls
743         // ListPopupWindow.onKeyDown and onKeyUp, the end result should be transfer of selection
744         // down one row
745         CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, listView, KeyEvent.KEYCODE_DPAD_DOWN);
746         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, root, null);
747 
748         // At this point we expect that item #2 was selected
749         verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
750                 any(AdapterView.class), any(View.class), eq(2), eq(2L));
751 
752         // Send a DPAD_UP key event. As our custom extension of EditText calls
753         // ListPopupWindow.onKeyDown and onKeyUp, the end result should be transfer of selection
754         // up one row
755         CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, listView, KeyEvent.KEYCODE_DPAD_UP);
756         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, root, null);
757 
758         // At this point we expect that item #1 was selected
759         verify(mPopupWindowBuilder.mOnItemSelectedListener, times(2)).onItemSelected(
760                 any(AdapterView.class), any(View.class), eq(1), eq(1L));
761 
762         // Send one more DPAD_UP key event. As our custom extension of EditText calls
763         // ListPopupWindow.onKeyDown and onKeyUp, the end result should be transfer of selection
764         // up one more row
765         CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, listView, KeyEvent.KEYCODE_DPAD_UP);
766         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, root, null);
767 
768         // At this point we expect that item #0 was selected
769         verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
770                 any(AdapterView.class), any(View.class), eq(0), eq(0L));
771 
772         // Send ENTER key event. As our custom extension of EditText calls
773         // ListPopupWindow.onKeyDown and onKeyUp, the end result should be dismissal of
774         // the popup window
775         CtsKeyEventUtil.sendKeyDownUp(mInstrumentation,listView, KeyEvent.KEYCODE_ENTER);
776         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
777                 null);
778 
779         verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
780         assertFalse(mPopupWindow.isShowing());
781 
782         verifyNoMoreInteractions(mPopupWindowBuilder.mOnItemSelectedListener);
783         verifyNoMoreInteractions(mPopupWindowBuilder.mOnDismissListener);
784     }
785 
786     @Test
testCreateOnDragListener()787     public void testCreateOnDragListener() {
788         // In this test we want precise control over the height of the popup content since
789         // we need to know by how much to swipe down to end the emulated gesture over the
790         // specific item in the popup. This is why we're using a popup style that removes
791         // all decoration around the popup content, as well as our own row layout with known
792         // height.
793         mPopupWindowBuilder = new Builder()
794                 .withPopupStyleAttr(R.style.PopupEmptyStyle)
795                 .withContentRowLayoutId(R.layout.popup_window_item)
796                 .withItemClickListener().withDismissListener();
797 
798         // Configure ListPopupWindow without showing it
799         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
800                 mPopupWindowBuilder::configure);
801 
802         // Get the anchor view and configure it with ListPopupWindow's drag-to-open listener
803         final View anchor = mActivity.findViewById(mPopupWindowBuilder.mAnchorId);
804         final View.OnTouchListener dragListener = mPopupWindow.createDragToOpenListener(anchor);
805         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
806                 () -> {
807                     anchor.setOnTouchListener(dragListener);
808                     // And also configure it to show the popup window on click
809                     anchor.setOnClickListener((View view) -> mPopupWindow.show());
810                 });
811 
812         // Get the height of a row item in our popup window
813         final int popupRowHeight = mActivity.getResources().getDimensionPixelSize(
814                 R.dimen.popup_row_height);
815 
816         final int[] anchorOnScreenXY = new int[2];
817         anchor.getLocationOnScreen(anchorOnScreenXY);
818 
819         // Compute the start coordinates of a downward swipe and the amount of swipe. We'll
820         // be swiping by twice the row height. That, combined with the swipe originating in the
821         // center of the anchor should result in clicking the second row in the popup.
822         int emulatedX = anchorOnScreenXY[0] + anchor.getWidth() / 2;
823         int emulatedStartY = anchorOnScreenXY[1] + anchor.getHeight() / 2;
824         int swipeAmount = 2 * popupRowHeight;
825 
826         // Emulate drag-down gesture with a sequence of motion events
827         CtsTouchUtils.emulateDragGesture(mInstrumentation, mActivityRule, emulatedX, emulatedStartY,
828                 0, swipeAmount);
829 
830         // We expect the swipe / drag gesture to result in clicking the second item in our list.
831         verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
832                 any(AdapterView.class), any(View.class), eq(1), eq(1L));
833         // Since our item click listener calls dismiss() on the popup, we expect the popup to not
834         // be showing
835         assertFalse(mPopupWindow.isShowing());
836         // At this point our popup should have notified its dismiss listener
837         verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
838     }
839 
840     /**
841      * Inner helper class to configure an instance of <code>ListPopupWindow</code> for the
842      * specific test. The main reason for its existence is that once a popup window is shown
843      * with the show() method, most of its configuration APIs are no-ops. This means that
844      * we can't add logic that is specific to a certain test (such as dismissing a non-modal
845      * popup window) once it's shown and we have a reference to a displayed ListPopupWindow.
846      */
847     private class Builder {
848         private boolean mIsModal;
849         private boolean mHasDismissListener;
850         private boolean mHasItemClickListener;
851         private boolean mHasItemSelectedListener;
852         private boolean mIgnoreContentWidth;
853         private int mHorizontalOffset;
854         private int mVerticalOffset;
855         private int mDropDownGravity;
856         private int mAnchorId = R.id.anchor_upper;
857         private int mContentRowLayoutId = android.R.layout.simple_list_item_1;
858 
859         private boolean mHasWindowLayoutType;
860         private int mWindowLayoutType;
861 
862         private boolean mUseCustomPopupStyle;
863         private int mPopupStyleAttr;
864 
865         private View mPromptView;
866         private int mPromptPosition;
867 
868         private AdapterView.OnItemClickListener mOnItemClickListener;
869         private AdapterView.OnItemSelectedListener mOnItemSelectedListener;
870         private PopupWindow.OnDismissListener mOnDismissListener;
871 
withAnchor(int anchorId)872         Builder withAnchor(int anchorId) {
873             mAnchorId = anchorId;
874             return this;
875         }
876 
withContentRowLayoutId(int contentRowLayoutId)877         Builder withContentRowLayoutId(int contentRowLayoutId) {
878             mContentRowLayoutId = contentRowLayoutId;
879             return this;
880         }
881 
withPopupStyleAttr(int popupStyleAttr)882         Builder withPopupStyleAttr(int popupStyleAttr) {
883             mUseCustomPopupStyle = true;
884             mPopupStyleAttr = popupStyleAttr;
885             return this;
886         }
887 
ignoreContentWidth()888         Builder ignoreContentWidth() {
889             mIgnoreContentWidth = true;
890             return this;
891         }
892 
setModal(boolean isModal)893         Builder setModal(boolean isModal) {
894             mIsModal = isModal;
895             return this;
896         }
897 
withItemClickListener()898         Builder withItemClickListener() {
899             mHasItemClickListener = true;
900             return this;
901         }
902 
withItemSelectedListener()903         Builder withItemSelectedListener() {
904             mHasItemSelectedListener = true;
905             return this;
906         }
907 
withDismissListener()908         Builder withDismissListener() {
909             mHasDismissListener = true;
910             return this;
911         }
912 
withWindowLayoutType(int windowLayoutType)913         Builder withWindowLayoutType(int windowLayoutType) {
914             mHasWindowLayoutType = true;
915             mWindowLayoutType = windowLayoutType;
916             return this;
917         }
918 
withHorizontalOffset(int horizontalOffset)919         Builder withHorizontalOffset(int horizontalOffset) {
920             mHorizontalOffset = horizontalOffset;
921             return this;
922         }
923 
withVerticalOffset(int verticalOffset)924         Builder withVerticalOffset(int verticalOffset) {
925             mVerticalOffset = verticalOffset;
926             return this;
927         }
928 
withDropDownGravity(int dropDownGravity)929         Builder withDropDownGravity(int dropDownGravity) {
930             mDropDownGravity = dropDownGravity;
931             return this;
932         }
933 
withPrompt(View promptView, int promptPosition)934         Builder withPrompt(View promptView, int promptPosition) {
935             mPromptView = promptView;
936             mPromptPosition = promptPosition;
937             return this;
938         }
939 
getContentWidth(ListAdapter listAdapter, Drawable background)940         private int getContentWidth(ListAdapter listAdapter, Drawable background) {
941             if (listAdapter == null) {
942                 return 0;
943             }
944 
945             int width = 0;
946             View itemView = null;
947             int itemType = 0;
948 
949             for (int i = 0; i < listAdapter.getCount(); i++) {
950                 final int positionType = listAdapter.getItemViewType(i);
951                 if (positionType != itemType) {
952                     itemType = positionType;
953                     itemView = null;
954                 }
955                 itemView = listAdapter.getView(i, itemView, null);
956                 if (itemView.getLayoutParams() == null) {
957                     itemView.setLayoutParams(new ViewGroup.LayoutParams(
958                             ViewGroup.LayoutParams.WRAP_CONTENT,
959                             ViewGroup.LayoutParams.WRAP_CONTENT));
960                 }
961                 itemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
962                 width = Math.max(width, itemView.getMeasuredWidth());
963             }
964 
965             // Add background padding to measured width
966             if (background != null) {
967                 final Rect rect = new Rect();
968                 background.getPadding(rect);
969                 width += rect.left + rect.right;
970             }
971 
972             return width;
973         }
974 
configure()975         private void configure() {
976             if (mUseCustomPopupStyle) {
977                 mPopupWindow = new ListPopupWindow(mActivity, null, mPopupStyleAttr, 0);
978             } else {
979                 mPopupWindow = new ListPopupWindow(mActivity);
980             }
981             final String[] POPUP_CONTENT =
982                     new String[]{"Alice", "Bob", "Charlie", "Deirdre", "El"};
983             final BaseAdapter listPopupAdapter = new BaseAdapter() {
984                 class ViewHolder {
985                     private TextView title;
986                 }
987 
988                 @Override
989                 public int getCount() {
990                     return POPUP_CONTENT.length;
991                 }
992 
993                 @Override
994                 public Object getItem(int position) {
995                     return POPUP_CONTENT[position];
996                 }
997 
998                 @Override
999                 public long getItemId(int position) {
1000                     return position;
1001                 }
1002 
1003                 @Override
1004                 public View getView(int position, View convertView, ViewGroup parent) {
1005                     if (convertView == null) {
1006                         convertView = LayoutInflater.from(mActivity).inflate(
1007                                 mContentRowLayoutId, parent, false);
1008                         ViewHolder viewHolder = new ViewHolder();
1009                         viewHolder.title = convertView.findViewById(android.R.id.text1);
1010                         convertView.setTag(viewHolder);
1011                     }
1012 
1013                     ViewHolder viewHolder = (ViewHolder) convertView.getTag();
1014                     viewHolder.title.setText(POPUP_CONTENT[position]);
1015                     return convertView;
1016                 }
1017             };
1018 
1019             mPopupWindow.setAdapter(listPopupAdapter);
1020             mPopupWindow.setAnchorView(mActivity.findViewById(mAnchorId));
1021 
1022             // The following mock listeners have to be set before the call to show() as
1023             // they are set on the internally constructed drop down.
1024             if (mHasItemClickListener) {
1025                 // Wrap our item click listener with a Mockito spy
1026                 mOnItemClickListener = spy(mItemClickListener);
1027                 // Register that spy as the item click listener on the ListPopupWindow
1028                 mPopupWindow.setOnItemClickListener(mOnItemClickListener);
1029                 // And configure Mockito to call our original listener with onItemClick.
1030                 // This way we can have both our item click listener running to dismiss the popup
1031                 // window, and track the invocations of onItemClick with Mockito APIs.
1032                 doCallRealMethod().when(mOnItemClickListener).onItemClick(
1033                         any(AdapterView.class), any(View.class), any(int.class), any(int.class));
1034             }
1035 
1036             if (mHasItemSelectedListener) {
1037                 mOnItemSelectedListener = mock(AdapterView.OnItemSelectedListener.class);
1038                 mPopupWindow.setOnItemSelectedListener(mOnItemSelectedListener);
1039                 mPopupWindow.setListSelector(
1040                         mActivity.getDrawable(R.drawable.red_translucent_fill));
1041             }
1042 
1043             if (mHasDismissListener) {
1044                 mOnDismissListener = mock(PopupWindow.OnDismissListener.class);
1045                 mPopupWindow.setOnDismissListener(mOnDismissListener);
1046             }
1047 
1048             mPopupWindow.setModal(mIsModal);
1049             if (mHasWindowLayoutType) {
1050                 mPopupWindow.setWindowLayoutType(mWindowLayoutType);
1051             }
1052 
1053             if (!mIgnoreContentWidth) {
1054                 mPopupWindow.setContentWidth(
1055                         getContentWidth(listPopupAdapter, mPopupWindow.getBackground()));
1056             }
1057 
1058             if (mHorizontalOffset != 0) {
1059                 mPopupWindow.setHorizontalOffset(mHorizontalOffset);
1060             }
1061 
1062             if (mVerticalOffset != 0) {
1063                 mPopupWindow.setVerticalOffset(mVerticalOffset);
1064             }
1065 
1066             if (mDropDownGravity != Gravity.NO_GRAVITY) {
1067                 mPopupWindow.setDropDownGravity(mDropDownGravity);
1068             }
1069 
1070             if (mPromptView != null) {
1071                 mPopupWindow.setPromptPosition(mPromptPosition);
1072                 mPopupWindow.setPromptView(mPromptView);
1073             }
1074         }
1075 
show()1076         private void show() {
1077             configure();
1078             mPopupWindow.show();
1079             assertTrue(mPopupWindow.isShowing());
1080         }
1081 
showAgain()1082         private void showAgain() {
1083             if (mPopupWindow == null || mPopupWindow.isShowing()) {
1084                 return;
1085             }
1086             mPopupWindow.show();
1087             assertTrue(mPopupWindow.isShowing());
1088         }
1089 
dismiss()1090         private void dismiss() {
1091             if (mPopupWindow == null || !mPopupWindow.isShowing())
1092                 return;
1093             mPopupWindow.dismiss();
1094         }
1095     }
1096 }
1097