12da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov/*
22da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Copyright (C) 2016 The Android Open Source Project
32da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov *
42da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Licensed under the Apache License, Version 2.0 (the "License");
52da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * you may not use this file except in compliance with the License.
62da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * You may obtain a copy of the License at
72da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov *
82da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov *      http://www.apache.org/licenses/LICENSE-2.0
92da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov *
102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * Unless required by applicable law or agreed to in writing, software
112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * distributed under the License is distributed on an "AS IS" BASIS,
122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * See the License for the specific language governing permissions and
142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov * limitations under the License.
152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov */
162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovpackage android.support.v7.widget;
172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.Espresso.onData;
1970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.Espresso.onView;
2070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.action.ViewActions.click;
2170058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
2270058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.assertion.ViewAssertions.matches;
2370058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
2470058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.RootMatchers.withDecorView;
2570058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
2670058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.withClassName;
2770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.withId;
2870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static android.support.test.espresso.matcher.ViewMatchers.withText;
2970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas
3070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.Matchers.allOf;
3170058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.Matchers.anything;
3270058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.core.Is.is;
3370058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.hamcrest.core.IsNot.not;
3470058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.junit.Assert.assertEquals;
3570058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.junit.Assert.assertNotNull;
3670058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.any;
3770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.mock;
3870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.never;
3970058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.times;
4070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport static org.mockito.Mockito.verify;
410f3f71c172e87b05b0a6b766cf129f4be56179d5Chris Banes
422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.app.Instrumentation;
432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.content.res.Resources;
442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.graphics.Rect;
452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.graphics.drawable.Drawable;
462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.os.SystemClock;
4770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport android.support.test.InstrumentationRegistry;
482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.test.espresso.Root;
492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.test.espresso.UiController;
502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.test.espresso.ViewAction;
512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.v7.app.BaseInstrumentationTestCase;
522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.support.v7.appcompat.test.R;
532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.test.suitebuilder.annotation.SmallTest;
542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.MenuInflater;
552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.MenuItem;
562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.MotionEvent;
572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.View;
582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.view.ViewParent;
592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.widget.Button;
602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovimport android.widget.FrameLayout;
612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
6270058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.Description;
6370058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.Matcher;
6470058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.Matchers;
6570058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.hamcrest.TypeSafeMatcher;
6670058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.junit.Before;
6770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikasimport org.junit.Test;
682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikovpublic class PopupMenuTest extends BaseInstrumentationTestCase<PopupTestActivity> {
702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    // Since PopupMenu doesn't expose any access to the underlying
712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    // implementation (like ListPopupWindow.getListView), we're relying on the
7294940fe6cff4af4f084b8cb5692e0501e3fe7912Kirill Grouchnikov    // class name of the list view from MenuPopupWindow that is being used
732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    // in PopupMenu. This is not the cleanest, but it's not making any assumptions
742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    // on the platform-specific details of the popup windows.
752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private static final String DROP_DOWN_CLASS_NAME =
7694940fe6cff4af4f084b8cb5692e0501e3fe7912Kirill Grouchnikov            "android.support.v7.widget.MenuPopupWindow$MenuDropDownListView";
772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private FrameLayout mContainer;
782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
792da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private Button mButton;
802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
812da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private PopupMenu mPopupMenu;
822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
83eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov    private Resources mResources;
84eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov
85eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov    private View mMainDecorView;
86eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov
872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public PopupMenuTest() {
882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        super(PopupTestActivity.class);
892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
910f3f71c172e87b05b0a6b766cf129f4be56179d5Chris Banes    @Before
922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public void setUp() throws Exception {
93eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        final PopupTestActivity activity = mActivityTestRule.getActivity();
942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        mContainer = (FrameLayout) activity.findViewById(R.id.container);
952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        mButton = (Button) mContainer.findViewById(R.id.test_button);
96eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        mResources = mActivityTestRule.getActivity().getResources();
97eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        mMainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView();
982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
1012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
1022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public void testBasicContent() {
1032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final Builder menuBuilder = new Builder();
1042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
1052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
1072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        assertNotNull("Popup menu created", mPopupMenu);
1082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing.
1092da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Use a custom matcher to check the visibility of the drop down list view instead.
1102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME)))
1112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .inRoot(isPlatformPopup()).check(matches(isDisplayed()));
1122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Note that MenuItem.isVisible() refers to the current "data" visibility state
1142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // and not the "on screen" visibility state. This is why we're testing the display
1152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // visibility of our main and sub menu items.
11670058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        // onData(anything()).atPosition(x) operates on the content of the popup. It is used to
11770058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        // scroll the popup if necessary to make sure the menu item views are visible.
11870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        onData(anything()).atPosition(0).check(matches(isDisplayed()));
119eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_highlight)))
120eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
12270058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        onData(anything()).atPosition(1).check(matches(isDisplayed()));
123eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_edit)))
124eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
12670058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        onData(anything()).atPosition(2).check(matches(isDisplayed()));
127eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_delete)))
128eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
13070058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        onData(anything()).atPosition(3).check(matches(isDisplayed()));
131eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_ignore)))
132eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
13470058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        onData(anything()).atPosition(4).check(matches(isDisplayed()));
135eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share)))
136eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1372da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
13870058e4bc399c316f1c7197b694ee54d0001b985Aurimas Liutikas        onData(anything()).atPosition(5).check(matches(isDisplayed()));
139eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_print)))
140eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
1422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Share submenu items should not be visible
144eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share_email)))
145eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(doesNotExist());
147eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share_circles)))
148eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
1492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(doesNotExist());
1502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
1512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    /**
1532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * Returns the location of our popup menu in its window.
1542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     */
1552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private int[] getPopupLocationInWindow() {
1562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final int[] location = new int[2];
1572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME)))
1582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .inRoot(isPlatformPopup()).perform(new ViewAction() {
1592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
1602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public Matcher<View> getConstraints() {
1612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                return isDisplayed();
1622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
1632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
1652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public String getDescription() {
1662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                return "Popup matcher";
1672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
1682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
1702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void perform(UiController uiController, View view) {
1712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                view.getLocationInWindow(location);
1722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
1732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        });
1742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        return location;
1752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
1762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    /**
1782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * Returns the location of our popup menu on the screen.
1792da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     */
1802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private int[] getPopupLocationOnScreen() {
1812da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final int[] location = new int[2];
1822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME)))
1832da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .inRoot(isPlatformPopup()).perform(new ViewAction() {
1842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
1852da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public Matcher<View> getConstraints() {
1862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                return isDisplayed();
1872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
1882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
1902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public String getDescription() {
1912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                return "Popup matcher";
1922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
1932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
1942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
1952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void perform(UiController uiController, View view) {
1962da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                view.getLocationOnScreen(location);
1972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
1982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        });
1992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        return location;
2002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
2012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    /**
2032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * Returns the combined padding around the content of our popup menu.
2042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     */
2052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private Rect getPopupPadding() {
2062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final Rect result = new Rect();
2072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME)))
2082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .inRoot(isPlatformPopup()).perform(new ViewAction() {
2092da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
2102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public Matcher<View> getConstraints() {
2112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                return isDisplayed();
2122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
2132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
2152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public String getDescription() {
2162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                return "Popup matcher";
2172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
2182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
2202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void perform(UiController uiController, View view) {
2212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                // Traverse the parent hierarchy and combine all their paddings
2222da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                result.setEmpty();
2232da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                final Rect current = new Rect();
2242da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                while (true) {
2252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    ViewParent parent = view.getParent();
2262da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    if (parent == null || !(parent instanceof View)) {
2272da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                        return;
2282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    }
2292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2302da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    view = (View) parent;
2312da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    Drawable currentBackground = view.getBackground();
2322da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    if (currentBackground != null) {
2332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                        currentBackground.getPadding(current);
2342da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                        result.left += current.left;
2352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                        result.right += current.right;
2362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                        result.top += current.top;
2372da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                        result.bottom += current.bottom;
2382da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    }
2392da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                }
2402da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
2412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        });
2422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        return result;
2432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
2442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    /**
2462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * Returns a root matcher that matches roots that have window focus on their decor view.
2472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     */
2482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    private static Matcher<Root> hasWindowFocus() {
2492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        return new TypeSafeMatcher<Root>() {
2502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
2512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void describeTo(Description description) {
2522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                description.appendText("has window focus");
2532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
2542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
2562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public boolean matchesSafely(Root root) {
2572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                View rootView = root.getDecorView();
2582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                return rootView.hasWindowFocus();
2592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
2602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        };
2612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
2622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
2642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
2652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public void testAnchoring() {
2662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Builder menuBuilder = new Builder();
2672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
2682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
2702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final int[] anchorOnScreenXY = new int[2];
2722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final int[] popupOnScreenXY = getPopupLocationOnScreen();
2732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final int[] popupInWindowXY = getPopupLocationInWindow();
2742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final Rect popupPadding = getPopupPadding();
2752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        mButton.getLocationOnScreen(anchorOnScreenXY);
2772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Allow for off-by-one mismatch in anchoring
2792da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        assertEquals("Anchoring X", anchorOnScreenXY[0] + popupInWindowXY[0],
2802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                popupOnScreenXY[0], 1);
281d7d01383deb3aac00ea54aab340cb43ff1ba3984Kirill Grouchnikov        assertEquals("Anchoring Y",
282d7d01383deb3aac00ea54aab340cb43ff1ba3984Kirill Grouchnikov                anchorOnScreenXY[1] + popupInWindowXY[1] + mButton.getHeight() - popupPadding.top,
28394940fe6cff4af4f084b8cb5692e0501e3fe7912Kirill Grouchnikov                popupOnScreenXY[1], 1);
2842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
2852da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
2872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
288eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov    public void testDismissalViaAPI() {
2892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Builder menuBuilder = new Builder().withDismissListener();
2902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
2912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
2932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
2942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Since PopupMenu is not a View, we can't use Espresso's view actions to invoke
2952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // the dismiss() API
296eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
2972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
2982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void run() {
2992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                mPopupMenu.dismiss();
3002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
3012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        });
3022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
30331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
30431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov
3052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing.
3062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Use a custom matcher to check the visibility of the drop down list view instead.
3072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist());
3082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
3092da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
3112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
3122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public void testDismissalViaTouch() throws Throwable {
3132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Builder menuBuilder = new Builder().withDismissListener();
3142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
3152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
3172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Determine the location of the popup on the screen so that we can emulate
3192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // a tap outside of its bounds to dismiss it
3202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final int[] popupOnScreenXY = getPopupLocationOnScreen();
3212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        final Rect popupPadding = getPopupPadding();
3222da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3232da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3242da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        int emulatedTapX = popupOnScreenXY[0] - popupPadding.left - 20;
3252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        int emulatedTapY = popupOnScreenXY[1] + popupPadding.top + 20;
3262da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3272da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // The logic below uses Instrumentation to emulate a tap outside the bounds of the
3282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // displayed popup menu. This tap is then treated by the framework to be "split" as
3292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // the ACTION_OUTSIDE for the popup itself, as well as DOWN / MOVE / UP for the underlying
3302da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // view root if the popup is not modal.
3312da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // It is not correct to emulate these two sequences separately in the test, as it
3322da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // wouldn't emulate the user-facing interaction for this test. Note that usage
3332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // of Instrumentation is necessary here since Espresso's actions operate at the level
3342da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // of view or data. Also, we don't want to use View.dispatchTouchEvent directly as
3352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // that would require emulation of two separate sequences as well.
3362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
337eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
3382da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3392da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Inject DOWN event
3402da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        long downTime = SystemClock.uptimeMillis();
3412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        MotionEvent eventDown = MotionEvent.obtain(
3422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                downTime, downTime, MotionEvent.ACTION_DOWN, emulatedTapX, emulatedTapY, 1);
3432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        instrumentation.sendPointerSync(eventDown);
3442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Inject MOVE event
3462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        long moveTime = SystemClock.uptimeMillis();
3472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        MotionEvent eventMove = MotionEvent.obtain(
3482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                moveTime, moveTime, MotionEvent.ACTION_MOVE, emulatedTapX, emulatedTapY, 1);
3492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        instrumentation.sendPointerSync(eventMove);
3502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Inject UP event
3522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        long upTime = SystemClock.uptimeMillis();
3532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        MotionEvent eventUp = MotionEvent.obtain(
3542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                upTime, upTime, MotionEvent.ACTION_UP, emulatedTapX, emulatedTapY, 1);
3552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        instrumentation.sendPointerSync(eventUp);
3562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3572da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Wait for the system to process all events in the queue
3582da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        instrumentation.waitForIdleSync();
3592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // At this point our popup should not be showing and should have notified its
3612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // dismiss listener
36231c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
3632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist());
3642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
3652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
3672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
3682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public void testSimpleMenuItemClickViaEvent() {
3692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Builder menuBuilder = new Builder().withMenuItemClickListener();
3702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
3712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
3732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
37431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that our menu item click listener hasn't been called yet
37531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
3762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
377eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_delete)))
378eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
3792da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .perform(click());
38031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov
38131c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that out menu item click listener has been called with the expected menu item
38231c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick(
38331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.getMenu().findItem(R.id.action_delete));
3842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3852da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Popup menu should be automatically dismissed on selecting an item
3862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist());
3872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
3882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
3902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
391eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov    public void testSimpleMenuItemClickViaAPI() {
3922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Builder menuBuilder = new Builder().withMenuItemClickListener();
3932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
3942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
3952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
3962da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
39731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that our menu item click listener hasn't been called yet
39831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
39931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov
400eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
4012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
4022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void run() {
4032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                mPopupMenu.getMenu().performIdentifierAction(R.id.action_highlight, 0);
4042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
4052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        });
4062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
40731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that out menu item click listener has been called with the expected menu item
40831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick(
40931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.getMenu().findItem(R.id.action_highlight));
4102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Popup menu should be automatically dismissed on selecting an item
4122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist());
4132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
4142da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
4162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
4172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public void testSubMenuClicksViaEvent() throws Throwable {
4182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Builder menuBuilder = new Builder().withMenuItemClickListener();
4192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
4202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
4222da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
42331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that our menu item click listener hasn't been called yet
42431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
4252da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
426eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share)))
427eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(withDecorView(not(is(mMainDecorView))))
4282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .perform(click());
42931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov
43031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that out menu item click listener has been called with the expected menu item
43131c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick(
43231c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.getMenu().findItem(R.id.action_share));
4332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4342da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Sleep for a bit to allow the menu -> submenu transition to complete
4352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Thread.sleep(1000);
4362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4372da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // At this point we should now have our sub-menu displayed. At this point on newer
4382da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // platform versions (L+) we have two view roots on the screen - one for the main popup
4392da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // menu and one for the submenu that has just been activated. If we only use the
4402da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // logic based on decor view, Espresso will time out on waiting for the first root
4412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // to acquire window focus. This is why from this point on in this test we are using
4422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // two root conditions to detect the submenu - one with decor view not being the same
4432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // as the decor view of our main activity window, and the other that checks for window
4442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // focus.
4452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing.
4472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Use a custom matcher to check the visibility of the drop down list view instead.
4482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME)))
449eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus()))
4502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
4512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Note that MenuItem.isVisible() refers to the current "data" visibility state
4532da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // and not the "on screen" visibility state. This is why we're testing the display
4542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // visibility of our main and sub menu items.
4552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Share submenu items should now be visible
457eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share_email)))
458eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus()))
4592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
460eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share_circles)))
461eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus()))
4622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
4632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Now click an item in the sub-menu
465eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share_circles)))
466eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus()))
4672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .perform(click());
46831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov
46931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that out menu item click listener has been called with the expected menu item
47031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick(
47131c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.getMenu().findItem(R.id.action_share_circles));
4722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Popup menu should be automatically dismissed on selecting an item in the submenu
4742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist());
4752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
4762da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @Test
4782da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    @SmallTest
4792da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    public void testSubMenuClicksViaAPI() throws Throwable {
4802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Builder menuBuilder = new Builder().withMenuItemClickListener();
4812da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        menuBuilder.wireToActionButton();
4822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4832da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withId(R.id.test_button)).perform(click());
4842da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
48531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that our menu item click listener hasn't been called yet
48631c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
48731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov
488eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
4892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
4902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void run() {
4912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                mPopupMenu.getMenu().performIdentifierAction(R.id.action_share, 0);
4922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
4932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        });
4942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
49531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that out menu item click listener has been called with the expected menu item
49631c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick(
49731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.getMenu().findItem(R.id.action_share));
4982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
4992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Sleep for a bit to allow the menu -> submenu transition to complete
5002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        Thread.sleep(1000);
5012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5022da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // At this point we should now have our sub-menu displayed. At this point on newer
5032da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // platform versions (L+) we have two view roots on the screen - one for the main popup
5042da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // menu and one for the submenu that has just been activated. If we only use the
5052da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // logic based on decor view, Espresso will time out on waiting for the first root
5062da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // to acquire window focus. This is why from this point on in this test we are using
5072da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // two root conditions to detect the submenu - one with decor view not being the same
5082da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // as the decor view of our main activity window, and the other that checks for window
5092da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // focus.
5102da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5112da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing.
5122da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Use a custom matcher to check the visibility of the drop down list view instead.
5132da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME)))
514eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus()))
5152da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
5162da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5172da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Note that MenuItem.isVisible() refers to the current "data" visibility state
5182da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // and not the "on screen" visibility state. This is why we're testing the display
5192da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // visibility of our main and sub menu items.
5202da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5212da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Share submenu items should now be visible
522eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share_email)))
523eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus()))
5242da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
525eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        onView(withText(mResources.getString(R.string.popup_menu_share_circles)))
526eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov                .inRoot(allOf(withDecorView(not(is(mMainDecorView))), hasWindowFocus()))
5272da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                .check(matches(isDisplayed()));
5282da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5292da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Now ask the share submenu to perform an action on its specific menu item
530eb673c3e06976775075d489cdd53f65208e5b551Kirill Grouchnikov        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
5312da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            @Override
5322da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            public void run() {
5332da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                mPopupMenu.getMenu().findItem(R.id.action_share).getSubMenu().
5342da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                        performIdentifierAction(R.id.action_share_email, 0);
5352da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
5362da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        });
53731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov
53831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        // Verify that out menu item click listener has been called with the expected menu item
53931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        verify(menuBuilder.mOnMenuItemClickListener, times(1)).onMenuItemClick(
54031c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.getMenu().findItem(R.id.action_share_email));
5412da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5422da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        // Popup menu should be automatically dismissed on selecting an item in the submenu
5432da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME))).check(doesNotExist());
5442da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
5452da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5462da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    /**
5472da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * Inner helper class to configure an instance of <code>PopupMenu</code> for the
5482da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * specific test. The main reason for its existence is that once a popup menu is shown
5492da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * with the show() method, most of its configuration APIs are no-ops. This means that
5502da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * we can't add logic that is specific to a certain test once it's shown and we have a
5512da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     * reference to a displayed PopupMenu.
5522da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov     */
55331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov    public class Builder {
5542da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        private boolean mHasDismissListener;
5552da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        private boolean mHasMenuItemClickListener;
5562da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
55731c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        private PopupMenu.OnMenuItemClickListener mOnMenuItemClickListener;
55831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov        private PopupMenu.OnDismissListener mOnDismissListener;
5592da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5602da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        public Builder withMenuItemClickListener() {
5612da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            mHasMenuItemClickListener = true;
5622da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            return this;
5632da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        }
5642da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5652da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        public Builder withDismissListener() {
5662da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            mHasDismissListener = true;
5672da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            return this;
5682da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        }
5692da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5702da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        private void show() {
5712da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            mPopupMenu = new PopupMenu(mContainer.getContext(), mButton);
5722da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            final MenuInflater menuInflater = mPopupMenu.getMenuInflater();
5732da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            menuInflater.inflate(R.menu.popup_menu, mPopupMenu.getMenu());
5742da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5752da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            if (mHasMenuItemClickListener) {
57631c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                // Register a mock listener to be notified when a menu item in our popup menu has
5772da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                // been clicked.
57831c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mOnMenuItemClickListener = mock(PopupMenu.OnMenuItemClickListener.class);
57931c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.setOnMenuItemClickListener(mOnMenuItemClickListener);
5802da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
5812da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5822da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            if (mHasDismissListener) {
58331c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                // Register a mock listener to be notified when our popup menu is dismissed.
58431c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mOnDismissListener = mock(PopupMenu.OnDismissListener.class);
58531c1690d6ed7ebec75bd9f6c0c74ac29528bd2f8Kirill Grouchnikov                mPopupMenu.setOnDismissListener(mOnDismissListener);
5862da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            }
5872da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5882da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            // Show the popup menu
5892da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            mPopupMenu.show();
5902da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        }
5912da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov
5922da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        public void wireToActionButton() {
5932da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            mButton.setOnClickListener(new View.OnClickListener() {
5942da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                @Override
5952da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                public void onClick(View v) {
5962da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                    show();
5972da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov                }
5982da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov            });
5992da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov        }
6002da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov    }
6012da48305c96b4cd11dda7f480003b1559c774ed7Kirill Grouchnikov}
602