Home | History | Annotate | Download | only in cts
      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
     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
    110     public void setup() {
    111         mInstrumentation = InstrumentationRegistry.getInstrumentation();
    112         mActivity = mActivityRule.getActivity();
    113         mItemClickListener = new PopupItemClickListener();
    114     }
    115 
    116     @After
    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
    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
    152     public void testNoDefaultVisibility() {
    153         mPopupWindow = new ListPopupWindow(mActivity);
    154         assertFalse(mPopupWindow.isShowing());
    155     }
    156 
    157     @Test
    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
    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
    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      */
    223     private Display getDisplay() {
    224         WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
    225         return wm.getDefaultDisplay();
    226     }
    227 
    228     @Test
    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 
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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
    513     public void testDismissalOutsideNonModal() {
    514         verifyDismissalViaTouch(false);
    515     }
    516 
    517     @Test
    518     public void testDismissalOutsideModal() {
    519         verifyDismissalViaTouch(true);
    520     }
    521 
    522     @Test
    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
    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
    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
    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
    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
    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
    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
    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 
    872         Builder withAnchor(int anchorId) {
    873             mAnchorId = anchorId;
    874             return this;
    875         }
    876 
    877         Builder withContentRowLayoutId(int contentRowLayoutId) {
    878             mContentRowLayoutId = contentRowLayoutId;
    879             return this;
    880         }
    881 
    882         Builder withPopupStyleAttr(int popupStyleAttr) {
    883             mUseCustomPopupStyle = true;
    884             mPopupStyleAttr = popupStyleAttr;
    885             return this;
    886         }
    887 
    888         Builder ignoreContentWidth() {
    889             mIgnoreContentWidth = true;
    890             return this;
    891         }
    892 
    893         Builder setModal(boolean isModal) {
    894             mIsModal = isModal;
    895             return this;
    896         }
    897 
    898         Builder withItemClickListener() {
    899             mHasItemClickListener = true;
    900             return this;
    901         }
    902 
    903         Builder withItemSelectedListener() {
    904             mHasItemSelectedListener = true;
    905             return this;
    906         }
    907 
    908         Builder withDismissListener() {
    909             mHasDismissListener = true;
    910             return this;
    911         }
    912 
    913         Builder withWindowLayoutType(int windowLayoutType) {
    914             mHasWindowLayoutType = true;
    915             mWindowLayoutType = windowLayoutType;
    916             return this;
    917         }
    918 
    919         Builder withHorizontalOffset(int horizontalOffset) {
    920             mHorizontalOffset = horizontalOffset;
    921             return this;
    922         }
    923 
    924         Builder withVerticalOffset(int verticalOffset) {
    925             mVerticalOffset = verticalOffset;
    926             return this;
    927         }
    928 
    929         Builder withDropDownGravity(int dropDownGravity) {
    930             mDropDownGravity = dropDownGravity;
    931             return this;
    932         }
    933 
    934         Builder withPrompt(View promptView, int promptPosition) {
    935             mPromptView = promptView;
    936             mPromptPosition = promptPosition;
    937             return this;
    938         }
    939 
    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 
    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 
   1076         private void show() {
   1077             configure();
   1078             mPopupWindow.show();
   1079             assertTrue(mPopupWindow.isShowing());
   1080         }
   1081 
   1082         private void showAgain() {
   1083             if (mPopupWindow == null || mPopupWindow.isShowing()) {
   1084                 return;
   1085             }
   1086             mPopupWindow.show();
   1087             assertTrue(mPopupWindow.isShowing());
   1088         }
   1089 
   1090         private void dismiss() {
   1091             if (mPopupWindow == null || !mPopupWindow.isShowing())
   1092                 return;
   1093             mPopupWindow.dismiss();
   1094         }
   1095     }
   1096 }
   1097