Ensure that TextViewActivityTest still pass for the new system toolbar.

The tests pass for both the old (local) and system toolbars.

Bug: 190031097
Test: atest android.widget.TextViewActivityTes
Change-Id: I1542d966d363c4a8bd2ac9465c982b82019472d7
This commit is contained in:
Abodunrinwa Toki
2022-02-03 12:23:23 +00:00
parent 2ccaf53cd7
commit 02416de354
6 changed files with 298 additions and 466 deletions

View File

@@ -1318,6 +1318,7 @@ final class RemoteSelectionToolbar {
contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
contentContainer.setClipToOutline(true);
return contentContainer;
}

View File

@@ -1475,6 +1475,7 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup {
contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
contentContainer.setClipToOutline(true);
return contentContainer;
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2022 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.widget;
import static com.android.internal.widget.floatingtoolbar.FloatingToolbar.FLOATING_TOOLBAR_TAG;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.res.Resources;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
final class FloatingToolbarUtils {
private final UiDevice mDevice;
FloatingToolbarUtils() {
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
}
void waitForFloatingToolbarPopup() {
mDevice.wait(Until.findObject(By.desc(FLOATING_TOOLBAR_TAG)), 500);
}
void assertFloatingToolbarIsDisplayed() {
waitForFloatingToolbarPopup();
assertThat(mDevice.hasObject(By.desc(FLOATING_TOOLBAR_TAG))).isTrue();
}
void assertFloatingToolbarContainsItem(String itemLabel) {
waitForFloatingToolbarPopup();
assertWithMessage("Expected to find item labelled [" + itemLabel + "]")
.that(mDevice.hasObject(
By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
.isTrue();
}
void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
waitForFloatingToolbarPopup();
assertWithMessage("Expected to not find item labelled [" + itemLabel + "]")
.that(mDevice.hasObject(
By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
.isFalse();
}
void assertFloatingToolbarContainsItemAtIndex(String itemLabel, int index) {
waitForFloatingToolbarPopup();
assertWithMessage("Expected to find item labelled [" + itemLabel + "] at index " + index)
.that(mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
.findObjects(By.clickable(true))
.get(index)
.getChildren()
.get(1)
.getText())
.isEqualTo(itemLabel);
}
void clickFloatingToolbarItem(String label) {
waitForFloatingToolbarPopup();
mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
.findObject(By.text(label))
.click();
}
void clickFloatingToolbarOverflowItem(String label) {
// TODO: There might be a benefit to combining this with "clickFloatingToolbarItem" method.
waitForFloatingToolbarPopup();
mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
.findObject(By.desc(str(R.string.floating_toolbar_open_overflow_description)))
.click();
mDevice.wait(
Until.findObject(By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(label))),
1000);
mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
.findObject(By.text(label))
.click();
}
private static String str(int id) {
return Resources.getSystem().getString(id);
}
}

View File

@@ -17,9 +17,6 @@
package android.widget;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupContainsItem;
import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsDisplayed;
import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsNotDisplayed;
@@ -72,6 +69,7 @@ public class SuggestionsPopupWindowTest {
@Rule
public final ActivityTestRule<TextViewActivity> mActivityRule =
new ActivityTestRule<>(TextViewActivity.class);
private final FloatingToolbarUtils mToolbar = new FloatingToolbarUtils();
private TextViewActivity getActivity() {
return mActivityRule.getActivity();
@@ -118,22 +116,19 @@ public class SuggestionsPopupWindowTest {
setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
sleepForFloatingToolbarPopup();
assertFloatingToolbarContainsItem(
getActivity().getString(com.android.internal.R.string.replace));
sleepForFloatingToolbarPopup();
clickFloatingToolbarItem(
mToolbar.clickFloatingToolbarOverflowItem(
getActivity().getString(com.android.internal.R.string.replace));
assertSuggestionsPopupIsDisplayed();
}
@Test
public void testInsertionActionMode() {
public void testInsertionActionMode() throws Throwable {
final String text = "abc def ghi";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(replaceText(text));
Thread.sleep(500);
final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
@@ -141,10 +136,7 @@ public class SuggestionsPopupWindowTest {
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
sleepForFloatingToolbarPopup();
assertFloatingToolbarContainsItem(
getActivity().getString(com.android.internal.R.string.replace));
clickFloatingToolbarItem(
mToolbar.clickFloatingToolbarItem(
getActivity().getString(com.android.internal.R.string.replace));
assertSuggestionsPopupIsDisplayed();

View File

@@ -16,14 +16,9 @@
package android.widget;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
import static android.widget.espresso.TextViewActions.Handle;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -63,10 +58,17 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.Instrumentation;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
import android.text.InputType;
import android.text.Selection;
import android.text.Spannable;
@@ -78,6 +80,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
@@ -101,6 +104,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Tests the TextView widget from an Activity
@@ -115,11 +119,16 @@ public class TextViewActivityTest {
private Activity mActivity;
private Instrumentation mInstrumentation;
private UiDevice mDevice;
private FloatingToolbarUtils mToolbar;
@Before
public void setUp() {
public void setUp() throws Exception {
mActivity = mActivityRule.getActivity();
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mDevice = UiDevice.getInstance(mInstrumentation);
mDevice.wakeUp();
mToolbar = new FloatingToolbarUtils();
TextClassificationManager tcm = mActivity.getSystemService(
TextClassificationManager.class);
tcm.setTextClassifier(TextClassifier.NO_OP);
@@ -131,14 +140,14 @@ public class TextViewActivityTest {
final String helloWorld = "Hello world!";
// We use replaceText instead of typeTextIntoFocusedView to input text to avoid
// unintentional interactions with software keyboard.
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
}
@Test
public void testPositionCursorAtTextAtIndex() {
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
// Delete text at specified index and see if we got the right one.
@@ -151,7 +160,7 @@ public class TextViewActivityTest {
// Arabic text. The expected cursorable boundary is
// | \u0623 \u064F | \u067A | \u0633 \u0652 |
final String text = "\u0623\u064F\u067A\u0633\u0652";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
@@ -171,7 +180,7 @@ public class TextViewActivityTest {
public void testPositionCursorAtTextAtIndex_devanagari() {
// Devanagari text. The expected cursorable boundary is | \u0915 \u093E |
final String text = "\u0915\u093E";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
@@ -185,7 +194,7 @@ public class TextViewActivityTest {
public void testLongPressToSelect() {
final String helloWorld = "Hello Kirk!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).perform(
longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
@@ -195,7 +204,7 @@ public class TextViewActivityTest {
@Test
public void testLongPressEmptySpace() {
final String helloWorld = "Hello big round sun!";
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
// Move cursor somewhere else
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
// Long-press at end of line.
@@ -209,7 +218,7 @@ public class TextViewActivityTest {
@Test
public void testLongPressAndDragToSelect() {
final String helloWorld = "Hello little handsome boy!";
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).perform(
longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
@@ -219,7 +228,7 @@ public class TextViewActivityTest {
@Test
public void testLongPressAndDragToSelect_emoji() {
final String text = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressAndDragOnText(4, 6));
onView(withId(R.id.textview)).check(hasSelection("\uD83D\uDE02"));
@@ -233,7 +242,7 @@ public class TextViewActivityTest {
@Test
public void testDragAndDrop() {
final String text = "abc def ghi.";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("e")));
onView(withId(R.id.textview)).perform(
@@ -253,7 +262,7 @@ public class TextViewActivityTest {
@Test
public void testDoubleTapToSelect() {
final String helloWorld = "Hello SuetYi!";
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).perform(
doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
@@ -264,7 +273,7 @@ public class TextViewActivityTest {
@Test
public void testDoubleTapAndDragToSelect() {
final String helloWorld = "Hello young beautiful person!";
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).perform(doubleTapAndDragOnText(helloWorld.indexOf("young"),
helloWorld.indexOf(" person!")));
@@ -274,7 +283,7 @@ public class TextViewActivityTest {
@Test
public void testDoubleTapAndDragToSelect_multiLine() {
final String helloWorld = "abcd\n" + "efg\n" + "hijklm\n" + "nop";
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).perform(
doubleTapAndDragOnText(helloWorld.indexOf("m"), helloWorld.indexOf("a")));
onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijklm"));
@@ -283,7 +292,7 @@ public class TextViewActivityTest {
@Test
public void testSelectBackwordsByTouch() {
final String helloWorld = "Hello king of the Jungle!";
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
setText(helloWorld);
onView(withId(R.id.textview)).perform(
doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
@@ -293,12 +302,11 @@ public class TextViewActivityTest {
@Test
public void testToolbarAppearsAfterSelection() {
final String text = "Toolbar appears after selection.";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(
longPressOnTextAtIndex(text.indexOf("appears")));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
}
@Test
@@ -316,13 +324,12 @@ public class TextViewActivityTest {
});
mInstrumentation.waitForIdleSync();
onView(withId(R.id.textview)).perform(replaceText("test"));
setText("test");
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.cut));
mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.cut));
onView(withId(R.id.textview)).perform(longClick());
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
}
@Test
@@ -330,8 +337,7 @@ public class TextViewActivityTest {
TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
int position = (textLink.getStart() + textLink.getEnd()) / 2;
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
}
@Test
@@ -341,23 +347,20 @@ public class TextViewActivityTest {
final int position = (textLink.getStart() + textLink.getEnd()) / 2;
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
assertTrue(textView.hasSelection());
// toggle
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
mToolbar.waitForFloatingToolbarPopup();
assertFalse(textView.hasSelection());
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
assertTrue(textView.hasSelection());
// click outside
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(0));
sleepForFloatingToolbarPopup();
assertFalse(textView.hasSelection());
}
@@ -371,8 +374,7 @@ public class TextViewActivityTest {
});
mInstrumentation.waitForIdleSync();
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
}
@Test
@@ -384,7 +386,7 @@ public class TextViewActivityTest {
final TextView textView = mActivity.findViewById(R.id.textview);
textView.setText(text);
textView.setCustomSelectionActionModeCallback(
new ActionMode.Callback() {
new ActionModeCallbackAdapter() {
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
menu.clear();
@@ -397,29 +399,19 @@ public class TextViewActivityTest {
clickedItem[0] = item;
return true;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {}
});
});
mInstrumentation.waitForIdleSync();
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
sleepForFloatingToolbarPopup();
// Change the selection so that the menu items are refreshed.
final TextView textView = mActivity.findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, 0));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
clickFloatingToolbarItem("Item");
mToolbar.clickFloatingToolbarItem("Item");
mInstrumentation.waitForIdleSync();
assertEquals(latestItem[0], clickedItem[0]);
@@ -433,13 +425,11 @@ public class TextViewActivityTest {
mActivityRule.runOnUiThread(() -> textView.setFocusableInTouchMode(true));
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
assertTrue(textView.hasSelection());
mActivityRule.runOnUiThread(() -> textView.clearFocus());
mInstrumentation.waitForIdleSync();
sleepForFloatingToolbarPopup();
assertFalse(textView.hasSelection());
}
@@ -452,14 +442,12 @@ public class TextViewActivityTest {
onView(withId(R.id.nonselectable_textview))
.perform(clickOnTextAtIndex(nonselectablePosition));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
assertTrue(nonselectableTextView.hasSelection());
UiDevice device = UiDevice.getInstance(mInstrumentation);
device.openNotification();
mDevice.openNotification();
Thread.sleep(2000);
device.pressBack();
mDevice.pressBack();
Thread.sleep(2000);
assertFalse(nonselectableTextView.hasSelection());
@@ -492,62 +480,58 @@ public class TextViewActivityTest {
}
@Test
public void testToolbarAndInsertionHandle() {
public void testToolbarAndInsertionHandle() throws Throwable {
final String text = "text";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
Thread.sleep(500);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.selectAll));
assertFloatingToolbarDoesNotContainItem(
mToolbar.assertFloatingToolbarDoesNotContainItem(
mActivity.getString(com.android.internal.R.string.copy));
assertFloatingToolbarDoesNotContainItem(
mToolbar.assertFloatingToolbarDoesNotContainItem(
mActivity.getString(com.android.internal.R.string.cut));
}
@Test
public void testToolbarAndSelectionHandle() {
final String text = "abcd efg hijk";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.selectAll));
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.copy));
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.cut));
final TextView textView = mActivity.findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
onHandleView(com.android.internal.R.id.selection_end_handle)
.perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
assertFloatingToolbarDoesNotContainItem(
mToolbar.assertFloatingToolbarDoesNotContainItem(
mActivity.getString(com.android.internal.R.string.selectAll));
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.copy));
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.cut));
}
@Test
public void testInsertionHandle() {
final String text = "abcd efg hijk ";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -566,7 +550,7 @@ public class TextViewActivityTest {
@Test
public void testInsertionHandle_multiLine() {
final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -604,7 +588,7 @@ public class TextViewActivityTest {
final TextView textView = mActivity.findViewById(R.id.textview);
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -618,7 +602,7 @@ public class TextViewActivityTest {
enableFlagsForInsertionHandleGestures();
final TextView textView = mActivity.findViewById(R.id.textview);
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -634,7 +618,7 @@ public class TextViewActivityTest {
final TextView textView = mActivity.findViewById(R.id.textview);
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -649,7 +633,7 @@ public class TextViewActivityTest {
final TextView textView = mActivity.findViewById(R.id.textview);
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -662,7 +646,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles() {
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
@@ -684,7 +668,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles_bidi() {
final String text = "abc \u0621\u0622\u0623 def";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0622')));
@@ -726,7 +710,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles_multiLine() {
final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
final TextView textView = mActivity.findViewById(R.id.textview);
@@ -754,7 +738,7 @@ public class TextViewActivityTest {
final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
+ "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n"
+ "\u0639\u063A\u063B";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0634')));
final TextView textView = mActivity.findViewById(R.id.textview);
@@ -781,7 +765,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles_doesNotPassAnotherHandle() {
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
final TextView textView = mActivity.findViewById(R.id.textview);
@@ -798,7 +782,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles_doesNotPassAnotherHandle_multiLine() {
final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
final TextView textView = mActivity.findViewById(R.id.textview);
@@ -815,7 +799,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles_snapToWordBoundary() {
final String text = "abcd efg hijk lmn opqr";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
final TextView textView = mActivity.findViewById(R.id.textview);
@@ -868,7 +852,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles_snapToWordBoundary_multiLine() {
final String text = "abcd efg\n" + "hijk lmn\n" + "opqr stu";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('m')));
final TextView textView = mActivity.findViewById(R.id.textview);
@@ -903,7 +887,7 @@ public class TextViewActivityTest {
@Test
public void testSelectionHandles_visibleEvenWithEmptyMenu() {
((TextView) mActivity.findViewById(R.id.textview)).setCustomSelectionActionModeCallback(
new ActionMode.Callback() {
new ActionModeCallbackAdapter() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.clear();
@@ -915,17 +899,9 @@ public class TextViewActivityTest {
menu.clear();
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {}
});
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
@@ -946,7 +922,7 @@ public class TextViewActivityTest {
textView.setCustomSelectionActionModeCallback(amCallback);
final String text = "abc def";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
mActivityRule.runOnUiThread(
() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
mInstrumentation.waitForIdleSync();
@@ -955,15 +931,13 @@ public class TextViewActivityTest {
// Make sure that "Select All" is included in the selection action mode when the entire text
// is not selected.
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
// Changing the selection range by API should not interrupt the selection action mode.
mActivityRule.runOnUiThread(
() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
mInstrumentation.waitForIdleSync();
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.selectAll));
// Make sure that "Select All" is no longer included when the entire text is selected by
// API.
@@ -971,9 +945,8 @@ public class TextViewActivityTest {
() -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
mInstrumentation.waitForIdleSync();
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertFloatingToolbarDoesNotContainItem(
mToolbar.assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarDoesNotContainItem(
mActivity.getString(com.android.internal.R.string.selectAll));
// Make sure that shrinking the selection range to cursor (an empty range) by API
// terminates selection action mode and does not trigger the insertion action mode.
@@ -984,17 +957,15 @@ public class TextViewActivityTest {
// Make sure that user click can trigger the insertion action mode.
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarIsDisplayed();
// Make sure that an existing insertion action mode keeps alive after the insertion point is
// moved by API.
mActivityRule.runOnUiThread(
() -> Selection.setSelection((Spannable) textView.getText(), 0));
mInstrumentation.waitForIdleSync();
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertFloatingToolbarDoesNotContainItem(
mToolbar.assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarDoesNotContainItem(
mActivity.getString(com.android.internal.R.string.copy));
// Make sure that selection action mode is started after selection is created by API when
// insertion action mode is active.
@@ -1002,16 +973,15 @@ public class TextViewActivityTest {
() -> Selection.setSelection((Spannable) textView.getText(), 1, text.length()));
mInstrumentation.waitForIdleSync();
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertFloatingToolbarContainsItem(
mToolbar.assertFloatingToolbarIsDisplayed();
mToolbar.assertFloatingToolbarContainsItem(
mActivity.getString(com.android.internal.R.string.copy));
}
@Test
public void testTransientState() throws Throwable {
final String text = "abc def";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
final TextView textView = mActivity.findViewById(R.id.textview);
assertFalse(textView.hasTransientState());
@@ -1032,58 +1002,43 @@ public class TextViewActivityTest {
@Test
public void testResetMenuItemTitle() throws Throwable {
mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
mActivity.getSystemService(TextClassificationManager.class)
.setTextClassifier(TextClassifier.NO_OP);
final TextView textView = mActivity.findViewById(R.id.textview);
final int itemId = 1;
final String title1 = " AFIGBO";
final int index = title1.indexOf('I');
final String title2 = title1.substring(index);
final String title1 = "@AFIGBO";
final int index = 3;
final String title2 = "IGBO";
final String[] title = new String[]{title1};
mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
return true;
}
new ActionModeCallbackAdapter() {
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
menu.removeItem(itemId);
menu.clear();
menu.add(Menu.NONE /* group */, itemId, 0 /* order */, title[0]);
return true;
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode actionMode) {
}
}));
mInstrumentation.waitForIdleSync();
onView(withId(R.id.textview)).perform(replaceText(title1));
setText(title1);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(index));
sleepForFloatingToolbarPopup();
assertFloatingToolbarContainsItem(title1);
mToolbar.assertFloatingToolbarContainsItem(title1);
// Change the menu item title.
title[0] = title2;
// Change the selection to invalidate the action mode without restarting it.
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, index));
sleepForFloatingToolbarPopup();
assertFloatingToolbarContainsItem(title2);
mToolbar.assertFloatingToolbarContainsItem(title2);
}
@Test
public void testAssistItemIsAtIndexZero() throws Throwable {
useSystemDefaultTextClassifier();
final SingleActionTextClassifier tc = useSingleActionTextClassifier();
final TextView textView = mActivity.findViewById(R.id.textview);
mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
new ActionMode.Callback() {
new ActionModeCallbackAdapter() {
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
// Create another item at order position 0 to confirm that it will never be
@@ -1091,33 +1046,19 @@ public class TextViewActivityTest {
menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test");
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return true;
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode actionMode) {
}
}));
mInstrumentation.waitForIdleSync();
final String text = "droid@android.com";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
sleepForFloatingToolbarPopup();
assertFloatingToolbarItemIndex(android.R.id.textAssist, 0);
mToolbar.assertFloatingToolbarContainsItemAtIndex(tc.getActionLabel(), 0);
}
@Test
public void testNoAssistItemForPasswordField() throws Throwable {
useSystemDefaultTextClassifier();
final SingleActionTextClassifier tc = useSingleActionTextClassifier();
final TextView textView = mActivity.findViewById(R.id.textview);
mActivityRule.runOnUiThread(() -> {
textView.setInputType(
@@ -1126,23 +1067,22 @@ public class TextViewActivityTest {
mInstrumentation.waitForIdleSync();
final String password = "afigbo@android.com";
onView(withId(R.id.textview)).perform(replaceText(password));
setText(password);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@')));
sleepForFloatingToolbarPopup();
assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
mToolbar.assertFloatingToolbarDoesNotContainItem(tc.getActionLabel());
}
@Test
public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
useSystemDefaultTextClassifier();
// NOTE: This test addresses a security bug.
final SingleActionTextClassifier tc = useSingleActionTextClassifier();
final String text = "\u202Emoc.diordna.com";
final TextView textView = mActivity.findViewById(R.id.textview);
mActivityRule.runOnUiThread(() -> textView.setText(text));
mInstrumentation.waitForIdleSync();
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
sleepForFloatingToolbarPopup();
assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
mToolbar.assertFloatingToolbarDoesNotContainItem(tc.getActionLabel());
}
@Test
@@ -1159,10 +1099,9 @@ public class TextViewActivityTest {
mInstrumentation.waitForIdleSync();
final String text = "andyroid@android.com";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
sleepForFloatingToolbarPopup();
clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
mInstrumentation.waitForIdleSync();
final SelectionEvent lastEvent = selectionEvents.get(selectionEvents.size() - 1);
@@ -1178,9 +1117,8 @@ public class TextViewActivityTest {
final String text = "My number is 987654321";
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('9')));
sleepForFloatingToolbarPopup();
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
mInstrumentation.waitForIdleSync();
@@ -1211,9 +1149,8 @@ public class TextViewActivityTest {
final String text = "My number is 987654321";
// Long press to trigger selection
onView(withId(R.id.textview)).perform(replaceText(text));
setText(text);
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('9')));
sleepForFloatingToolbarPopup();
// Type over the selection
onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_A));
@@ -1255,16 +1192,14 @@ public class TextViewActivityTest {
});
// Long press to trigger selection
onView(withId(R.id.textview)).perform(replaceText("android.com"));
setText("android.com");
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0));
sleepForFloatingToolbarPopup();
// Click "Copy" to dismiss the selection.
clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
// Long press to trigger another selection
onView(withId(R.id.textview)).perform(replaceText("android@android.com"));
setText("android@android.com");
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0));
sleepForFloatingToolbarPopup();
// suggestSelection should be called in two different TextClassifier sessions.
assertEquals(2, testableTextClassifiers.size());
@@ -1276,10 +1211,9 @@ public class TextViewActivityTest {
public void testPastePlainText_menuAction() {
initializeClipboardWithText(TextStyle.STYLED);
onView(withId(R.id.textview)).perform(replaceText(""));
setText("");
onView(withId(R.id.textview)).perform(longClick());
sleepForFloatingToolbarPopup();
clickFloatingToolbarItem(
mToolbar.clickFloatingToolbarItem(
mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
mInstrumentation.waitForIdleSync();
@@ -1291,18 +1225,33 @@ public class TextViewActivityTest {
public void testPastePlainText_noMenuItemForPlainText() {
initializeClipboardWithText(TextStyle.PLAIN);
onView(withId(R.id.textview)).perform(replaceText(""));
setText("");
onView(withId(R.id.textview)).perform(longClick());
sleepForFloatingToolbarPopup();
assertFloatingToolbarDoesNotContainItem(
mToolbar.assertFloatingToolbarDoesNotContainItem(
mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
}
private void setText(String text) {
onView(withId(R.id.textview)).perform(replaceText(text));
mDevice.wait(Until.findObject(By.text(text)), 1000);
mInstrumentation.waitForIdleSync();
}
private void useSystemDefaultTextClassifier() {
mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
}
private SingleActionTextClassifier useSingleActionTextClassifier() {
useSystemDefaultTextClassifier();
final TextClassificationManager tcm =
mActivity.getSystemService(TextClassificationManager.class);
final SingleActionTextClassifier oneActionTC =
new SingleActionTextClassifier(mActivity, tcm.getTextClassifier());
tcm.setTextClassifier(oneActionTC);
return oneActionTC;
}
private void initializeClipboardWithText(TextStyle textStyle) {
final ClipData clip;
switch (textStyle) {
@@ -1324,7 +1273,7 @@ public class TextViewActivityTest {
PLAIN, STYLED
}
private final class TestableTextClassifier implements TextClassifier {
private static final class TestableTextClassifier implements TextClassifier {
final List<SelectionEvent> mSelectionEvents = new ArrayList<>();
final List<TextSelection.Request> mTextSelectionRequests = new ArrayList<>();
@@ -1349,4 +1298,54 @@ public class TextViewActivityTest {
return mTextSelectionRequests;
}
}
private static final class SingleActionTextClassifier implements TextClassifier {
private final RemoteAction mAction;
private final TextClassifier mOriginal;
private final TextClassification mClassificationResult;
SingleActionTextClassifier(Context context, TextClassifier original) {
mAction = new RemoteAction(
Icon.createWithResource(context, android.R.drawable.btn_star),
"assist",
"assist",
PendingIntent.getActivity(context, 0, new Intent(), FLAG_IMMUTABLE));
mClassificationResult = new TextClassification.Builder().addAction(mAction).build();
mOriginal = Objects.requireNonNull(original);
}
public String getActionLabel() {
return mAction.getTitle().toString();
}
@Override
public TextSelection suggestSelection(TextSelection.Request request) {
final TextSelection sel = mOriginal.suggestSelection(request);
return new TextSelection.Builder(
sel.getSelectionStartIndex(), sel.getSelectionEndIndex())
.setTextClassification(mClassificationResult)
.build();
}
}
private static class ActionModeCallbackAdapter implements ActionMode.Callback {
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return true;
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
return true;
}
@Override
public void onDestroyActionMode(ActionMode actionMode) {}
}
}

View File

@@ -1,263 +0,0 @@
/*
* Copyright (C) 2015 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.widget.espresso;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.internal.widget.floatingtoolbar.LocalFloatingToolbarPopup.MenuItemRepr;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
import android.view.View;
import android.view.ViewGroup;
import androidx.test.espresso.NoMatchingRootException;
import androidx.test.espresso.NoMatchingViewException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.ViewInteraction;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* Espresso utility methods for the floating toolbar.
*/
public class FloatingToolbarEspressoUtils {
private final static Object TAG = FloatingToolbar.FLOATING_TOOLBAR_TAG;
private FloatingToolbarEspressoUtils() {}
private static ViewInteraction onFloatingToolBar() {
return onView(withTagValue(is(TAG)))
.inRoot(allOf(
isPlatformPopup(),
withDecorView(hasDescendant(withTagValue(is(TAG))))));
}
/**
* Creates a {@link ViewInteraction} for the floating bar menu item with the given matcher.
*
* @param matcher The matcher for the menu item.
*/
public static ViewInteraction onFloatingToolBarItem(Matcher<View> matcher) {
return onView(matcher)
.inRoot(withDecorView(hasDescendant(withTagValue(is(TAG)))));
}
/**
* Asserts that the floating toolbar is displayed on screen.
*
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarIsDisplayed() {
onFloatingToolBar().check(matches(isDisplayed()));
}
/**
* Asserts that the floating toolbar is not displayed on screen.
*
* @throws AssertionError if the assertion fails
* @deprecated Negative assertions are taking too long to timeout in Espresso.
*/
@Deprecated
public static void assertFloatingToolbarIsNotDisplayed() {
try {
onFloatingToolBar().check(matches(isDisplayed()));
} catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
return;
}
throw new AssertionError("Floating toolbar is displayed");
}
private static void toggleOverflow() {
final int id = com.android.internal.R.id.overflow;
onView(allOf(withId(id), isDisplayed()))
.inRoot(withDecorView(hasDescendant(withId(id))))
.perform(click());
onView(isRoot()).perform(SLEEP);
}
public static void sleepForFloatingToolbarPopup() {
onView(isRoot()).perform(SLEEP);
}
/**
* Asserts that the floating toolbar contains the specified item.
*
* @param itemLabel label of the item.
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarContainsItem(String itemLabel) {
try{
onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
} catch (AssertionError e) {
try{
toggleOverflow();
} catch (NoMatchingViewException | NoMatchingRootException e2) {
// No overflow items.
throw e;
}
try{
onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
} finally {
toggleOverflow();
}
}
}
/**
* Asserts that the floating toolbar contains a specified item at a specified index.
*
* @param menuItemId id of the menu item
* @param index expected index of the menu item in the floating toolbar
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarItemIndex(final int menuItemId, final int index) {
onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
private List<Integer> menuItemIds = new ArrayList<>();
@Override
public boolean matchesSafely(View view) {
collectMenuItemIds(view);
return menuItemIds.size() > index && menuItemIds.get(index) == menuItemId;
}
@Override
public void describeTo(Description description) {}
private void collectMenuItemIds(View view) {
if (view.getTag() instanceof MenuItemRepr) {
menuItemIds.add(((MenuItemRepr) view.getTag()).itemId);
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
collectMenuItemIds(viewGroup.getChildAt(i));
}
}
}
}));
}
/**
* Asserts that the floating toolbar doesn't contain the specified item.
*
* @param itemLabel label of the item.
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
final Predicate<View> hasMenuItemLabel = view ->
view.getTag() instanceof MenuItemRepr
&& itemLabel.equals(((MenuItemRepr) view.getTag()).title);
assertFloatingToolbarMenuItem(hasMenuItemLabel, false);
}
/**
* Asserts that the floating toolbar does not contain a menu item with the specified id.
*
* @param menuItemId id of the menu item
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) {
final Predicate<View> hasMenuItemId = view ->
view.getTag() instanceof MenuItemRepr
&& ((MenuItemRepr) view.getTag()).itemId == menuItemId;
assertFloatingToolbarMenuItem(hasMenuItemId, false);
}
private static void assertFloatingToolbarMenuItem(
final Predicate<View> predicate, final boolean positiveAssertion) {
onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
@Override
public boolean matchesSafely(View view) {
return positiveAssertion == containsItem(view);
}
@Override
public void describeTo(Description description) {}
private boolean containsItem(View view) {
if (predicate.test(view)) {
return true;
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
if (containsItem(viewGroup.getChildAt(i))) {
return true;
}
}
}
return false;
}
}));
}
/**
* Click specified item on the floating tool bar.
*
* @param itemLabel label of the item.
*/
public static void clickFloatingToolbarItem(String itemLabel) {
try{
onFloatingToolBarItem(withText(itemLabel)).check(matches(isDisplayed()));
} catch (AssertionError e) {
// Try to find the item in the overflow menu.
toggleOverflow();
}
onFloatingToolBarItem(withText(itemLabel)).perform(click());
}
/**
* ViewAction to sleep to wait floating toolbar's animation.
*/
private static final ViewAction SLEEP = new ViewAction() {
private static final long SLEEP_DURATION = 400;
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return "Sleep " + SLEEP_DURATION + " ms.";
}
@Override
public void perform(UiController uiController, View view) {
uiController.loopMainThreadForAtLeast(SLEEP_DURATION);
}
};
}