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