Merge "[DO NOT MERGE] Fix context menu position for RTL"
This commit is contained in:
@@ -256,7 +256,7 @@ public class MenuPopupHelper implements MenuHelper {
|
|||||||
final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
|
final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
|
||||||
mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
|
mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||||
if (hgrav == Gravity.RIGHT) {
|
if (hgrav == Gravity.RIGHT) {
|
||||||
xOffset += mAnchorView.getWidth();
|
xOffset -= mAnchorView.getWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
popup.setHorizontalOffset(xOffset);
|
popup.setHorizontalOffset(xOffset);
|
||||||
|
|||||||
@@ -263,7 +263,6 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
|
|||||||
mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
|
mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
|
||||||
subPopup.setPresenterCallback(mPresenterCallback);
|
subPopup.setPresenterCallback(mPresenterCallback);
|
||||||
subPopup.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(subMenu));
|
subPopup.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(subMenu));
|
||||||
subPopup.setGravity(mDropDownGravity);
|
|
||||||
|
|
||||||
// Pass responsibility for handling onDismiss to the submenu.
|
// Pass responsibility for handling onDismiss to the submenu.
|
||||||
subPopup.setOnDismissListener(mOnDismissListener);
|
subPopup.setOnDismissListener(mOnDismissListener);
|
||||||
@@ -273,8 +272,17 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
|
|||||||
mMenu.close(false /* closeAllMenus */);
|
mMenu.close(false /* closeAllMenus */);
|
||||||
|
|
||||||
// Show the new sub-menu popup at the same location as this popup.
|
// Show the new sub-menu popup at the same location as this popup.
|
||||||
final int horizontalOffset = mPopup.getHorizontalOffset();
|
int horizontalOffset = mPopup.getHorizontalOffset();
|
||||||
final int verticalOffset = mPopup.getVerticalOffset();
|
final int verticalOffset = mPopup.getVerticalOffset();
|
||||||
|
|
||||||
|
// As xOffset of parent menu popup is subtracted with Anchor width for Gravity.RIGHT,
|
||||||
|
// So, again to display sub-menu popup in same xOffset, add the Anchor width.
|
||||||
|
final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
|
||||||
|
mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||||
|
if (hgrav == Gravity.RIGHT) {
|
||||||
|
horizontalOffset += mAnchorView.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
if (subPopup.tryShow(horizontalOffset, verticalOffset)) {
|
if (subPopup.tryShow(horizontalOffset, verticalOffset)) {
|
||||||
if (mPresenterCallback != null) {
|
if (mPresenterCallback != null) {
|
||||||
mPresenterCallback.onOpenSubMenu(subMenu);
|
mPresenterCallback.onOpenSubMenu(subMenu);
|
||||||
|
|||||||
@@ -1041,6 +1041,13 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name="android.view.menu.ContextMenuActivity" android:label="ContextMenu" android:theme="@android:style/Theme.Material">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<activity android:name="android.view.menu.MenuWith1Item" android:label="MenuWith1Item">
|
<activity android:name="android.view.menu.MenuWith1Item" android:label="MenuWith1Item">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|||||||
54
core/tests/coretests/res/layout/context_menu.xml
Normal file
54
core/tests/coretests/res/layout/context_menu.xml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
/*
|
||||||
|
**
|
||||||
|
** Copyright 2018, The Android Open Source Project
|
||||||
|
**
|
||||||
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
** you may not use this file except in compliance with the License.
|
||||||
|
** You may obtain a copy of the License at
|
||||||
|
**
|
||||||
|
** http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
**
|
||||||
|
** Unless required by applicable law or agreed to in writing, software
|
||||||
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
** See the License for the specific language governing permissions and
|
||||||
|
** limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/context_menu_target_ltr"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layoutDirection="ltr"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="50px"
|
||||||
|
android:layout_marginEnd="50px">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="LTR"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/context_menu_target_rtl"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layoutDirection="rtl"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="50px"
|
||||||
|
android:layout_marginEnd="50px">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="RTL"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
104
core/tests/coretests/src/android/util/PollingCheck.java
Normal file
104
core/tests/coretests/src/android/util/PollingCheck.java
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.util;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility used for testing that allows to poll for a certain condition to happen within a timeout.
|
||||||
|
*
|
||||||
|
* Code copied from com.android.compatibility.common.util.PollingCheck
|
||||||
|
*/
|
||||||
|
public abstract class PollingCheck {
|
||||||
|
|
||||||
|
private static final long DEFAULT_TIMEOUT = 3000;
|
||||||
|
private static final long TIME_SLICE = 50;
|
||||||
|
private final long mTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The condition that the PollingCheck should use to proceed successfully.
|
||||||
|
*/
|
||||||
|
public interface PollingCheckCondition {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether the polling condition has been met.
|
||||||
|
*/
|
||||||
|
boolean canProceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PollingCheck(long timeout) {
|
||||||
|
mTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean check();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start running the polling check.
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
if (check()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long timeout = mTimeout;
|
||||||
|
while (timeout > 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(TIME_SLICE);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Assert.fail("unexpected InterruptedException");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout -= TIME_SLICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.fail("unexpected timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate and start polling for a given condition with a default 3000ms timeout.
|
||||||
|
*
|
||||||
|
* @param condition The condition to check for success.
|
||||||
|
*/
|
||||||
|
public static void waitFor(final PollingCheckCondition condition) {
|
||||||
|
new PollingCheck(DEFAULT_TIMEOUT) {
|
||||||
|
@Override
|
||||||
|
protected boolean check() {
|
||||||
|
return condition.canProceed();
|
||||||
|
}
|
||||||
|
}.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate and start polling for a given condition.
|
||||||
|
*
|
||||||
|
* @param timeout Time out in ms
|
||||||
|
* @param condition The condition to check for success.
|
||||||
|
*/
|
||||||
|
public static void waitFor(long timeout, final PollingCheckCondition condition) {
|
||||||
|
new PollingCheck(timeout) {
|
||||||
|
@Override
|
||||||
|
protected boolean check() {
|
||||||
|
return condition.canProceed();
|
||||||
|
}
|
||||||
|
}.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.view.menu;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.android.frameworks.coretests.R;
|
||||||
|
|
||||||
|
public class ContextMenuActivity extends Activity {
|
||||||
|
|
||||||
|
static final String LABEL_ITEM = "Item";
|
||||||
|
static final String LABEL_SUBMENU = "Submenu";
|
||||||
|
static final String LABEL_SUBITEM = "Subitem";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.context_menu);
|
||||||
|
registerForContextMenu(getTargetLtr());
|
||||||
|
registerForContextMenu(getTargetRtl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||||
|
menu.add(LABEL_ITEM);
|
||||||
|
menu.addSubMenu(LABEL_SUBMENU).add(LABEL_SUBITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
View getTargetLtr() {
|
||||||
|
return findViewById(R.id.context_menu_target_ltr);
|
||||||
|
}
|
||||||
|
|
||||||
|
View getTargetRtl() {
|
||||||
|
return findViewById(R.id.context_menu_target_rtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.view.menu;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.support.test.filters.MediumTest;
|
||||||
|
import android.test.ActivityInstrumentationTestCase;
|
||||||
|
import android.util.PollingCheck;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.espresso.ContextMenuUtils;
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public class ContextMenuTest extends ActivityInstrumentationTestCase<ContextMenuActivity> {
|
||||||
|
|
||||||
|
public ContextMenuTest() {
|
||||||
|
super("com.android.frameworks.coretests", ContextMenuActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testContextMenuPositionLtr() throws InterruptedException {
|
||||||
|
testMenuPosition(getActivity().getTargetLtr());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testContextMenuPositionRtl() throws InterruptedException {
|
||||||
|
testMenuPosition(getActivity().getTargetRtl());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testMenuPosition(View target) throws InterruptedException {
|
||||||
|
final int minScreenDimension = getMinScreenDimension();
|
||||||
|
if (minScreenDimension < 320) {
|
||||||
|
// Assume there is insufficient room for the context menu to be aligned properly.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offsetX = target.getWidth() / 2;
|
||||||
|
int offsetY = target.getHeight() / 2;
|
||||||
|
|
||||||
|
getInstrumentation().runOnMainSync(() -> target.performLongClick(offsetX, offsetY));
|
||||||
|
|
||||||
|
PollingCheck.waitFor(
|
||||||
|
() -> ContextMenuUtils.isMenuItemClickable(ContextMenuActivity.LABEL_SUBMENU));
|
||||||
|
|
||||||
|
ContextMenuUtils.assertContextMenuAlignment(target, offsetX, offsetY);
|
||||||
|
|
||||||
|
ContextMenuUtils.clickMenuItem(ContextMenuActivity.LABEL_SUBMENU);
|
||||||
|
|
||||||
|
PollingCheck.waitFor(
|
||||||
|
() -> ContextMenuUtils.isMenuItemClickable(ContextMenuActivity.LABEL_SUBITEM));
|
||||||
|
|
||||||
|
if (minScreenDimension < getCascadingMenuTreshold()) {
|
||||||
|
// A non-cascading submenu should be displayed at the same location as its parent.
|
||||||
|
// Not testing cascading submenu position, as it is positioned differently.
|
||||||
|
ContextMenuUtils.assertContextMenuAlignment(target, offsetX, offsetY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum of the default display's width and height.
|
||||||
|
*/
|
||||||
|
private int getMinScreenDimension() {
|
||||||
|
final WindowManager windowManager = (WindowManager) getActivity().getSystemService(
|
||||||
|
Context.WINDOW_SERVICE);
|
||||||
|
final Display display = windowManager.getDefaultDisplay();
|
||||||
|
final Point displaySize = new Point();
|
||||||
|
display.getRealSize(displaySize);
|
||||||
|
return Math.min(displaySize.x, displaySize.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum display size where cascading submenus are supported.
|
||||||
|
*/
|
||||||
|
private int getCascadingMenuTreshold() {
|
||||||
|
// Use the same dimension resource as in MenuPopupHelper.createPopup().
|
||||||
|
return getActivity().getResources().getDimensionPixelSize(
|
||||||
|
com.android.internal.R.dimen.cascading_menus_min_smallest_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,25 +17,32 @@
|
|||||||
package android.widget.espresso;
|
package android.widget.espresso;
|
||||||
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
import static android.support.test.espresso.Espresso.onView;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.click;
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||||
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
|
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
|
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
|
import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
|
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
|
import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
|
||||||
import com.android.internal.view.menu.ListMenuItemView;
|
|
||||||
|
|
||||||
import android.support.test.espresso.NoMatchingRootException;
|
import android.support.test.espresso.NoMatchingRootException;
|
||||||
import android.support.test.espresso.NoMatchingViewException;
|
import android.support.test.espresso.NoMatchingViewException;
|
||||||
import android.support.test.espresso.ViewInteraction;
|
import android.support.test.espresso.ViewInteraction;
|
||||||
import android.support.test.espresso.matcher.ViewMatchers;
|
import android.support.test.espresso.matcher.ViewMatchers;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.MenuPopupWindow.MenuDropDownListView;
|
import android.widget.MenuPopupWindow.MenuDropDownListView;
|
||||||
|
|
||||||
|
import com.android.internal.view.menu.ListMenuItemView;
|
||||||
|
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Espresso utility methods for the context menu.
|
* Espresso utility methods for the context menu.
|
||||||
*/
|
*/
|
||||||
@@ -82,10 +89,15 @@ public final class ContextMenuUtils {
|
|||||||
private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel,
|
private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel,
|
||||||
boolean enabled) {
|
boolean enabled) {
|
||||||
onContextMenu().check(matches(
|
onContextMenu().check(matches(
|
||||||
hasDescendant(allOf(
|
hasDescendant(getVisibleMenuItemMatcher(itemLabel, enabled))));
|
||||||
isAssignableFrom(ListMenuItemView.class),
|
}
|
||||||
enabled ? isEnabled() : not(isEnabled()),
|
|
||||||
hasDescendant(withText(itemLabel))))));
|
private static Matcher<View> getVisibleMenuItemMatcher(String itemLabel, boolean enabled) {
|
||||||
|
return allOf(
|
||||||
|
isAssignableFrom(ListMenuItemView.class),
|
||||||
|
hasDescendant(withText(itemLabel)),
|
||||||
|
enabled ? isEnabled() : not(isEnabled()),
|
||||||
|
isDisplayingAtLeast(90));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,4 +119,70 @@ public final class ContextMenuUtils {
|
|||||||
public static void assertContextMenuContainsItemDisabled(String itemLabel) {
|
public static void assertContextMenuContainsItemDisabled(String itemLabel) {
|
||||||
asssertContextMenuContainsItemWithEnabledState(itemLabel, false);
|
asssertContextMenuContainsItemWithEnabledState(itemLabel, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the context menu window is aligned to a given view with a given offset.
|
||||||
|
*
|
||||||
|
* @param anchor Anchor view.
|
||||||
|
* @param offsetX x offset
|
||||||
|
* @param offsetY y offset.
|
||||||
|
* @throws AssertionError if the assertion fails
|
||||||
|
*/
|
||||||
|
public static void assertContextMenuAlignment(View anchor, int offsetX, int offsetY) {
|
||||||
|
int [] expectedLocation = new int[2];
|
||||||
|
anchor.getLocationOnScreen(expectedLocation);
|
||||||
|
expectedLocation[0] += offsetX;
|
||||||
|
expectedLocation[1] += offsetY;
|
||||||
|
|
||||||
|
final boolean rtl = anchor.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||||
|
|
||||||
|
onContextMenu().check(matches(new TypeSafeMatcher<View>() {
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("root view ");
|
||||||
|
description.appendText(rtl ? "right" : "left");
|
||||||
|
description.appendText("=");
|
||||||
|
description.appendText(Integer.toString(offsetX));
|
||||||
|
description.appendText(", top=");
|
||||||
|
description.appendText(Integer.toString(offsetY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matchesSafely(View view) {
|
||||||
|
View rootView = view.getRootView();
|
||||||
|
int [] actualLocation = new int[2];
|
||||||
|
rootView.getLocationOnScreen(actualLocation);
|
||||||
|
if (rtl) {
|
||||||
|
actualLocation[0] += rootView.getWidth();
|
||||||
|
}
|
||||||
|
return expectedLocation[0] == actualLocation[0]
|
||||||
|
&& expectedLocation[1] == actualLocation[1];
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check is the menu item is clickable (i.e. visible and enabled).
|
||||||
|
*
|
||||||
|
* @param itemLabel Label of the item.
|
||||||
|
* @return True if the menu item is clickable.
|
||||||
|
*/
|
||||||
|
public static boolean isMenuItemClickable(String itemLabel) {
|
||||||
|
try {
|
||||||
|
onContextMenu().check(matches(
|
||||||
|
hasDescendant(getVisibleMenuItemMatcher(itemLabel, true))));
|
||||||
|
return true;
|
||||||
|
} catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click on a menu item with the specified label
|
||||||
|
* @param itemLabel Label of the item.
|
||||||
|
*/
|
||||||
|
public static void clickMenuItem(String itemLabel) {
|
||||||
|
onView(getVisibleMenuItemMatcher(itemLabel, true))
|
||||||
|
.inRoot(withDecorView(hasFocus())).perform(click());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user