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