From 02416de354ed00ec8f70d35ff217223453065bde Mon Sep 17 00:00:00 2001 From: Abodunrinwa Toki Date: Thu, 3 Feb 2022 12:23:23 +0000 Subject: [PATCH] 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 --- .../RemoteSelectionToolbar.java | 1 + .../LocalFloatingToolbarPopup.java | 1 + .../android/widget/FloatingToolbarUtils.java | 102 +++++ .../widget/SuggestionsPopupWindowTest.java | 18 +- .../android/widget/TextViewActivityTest.java | 379 +++++++++--------- .../FloatingToolbarEspressoUtils.java | 263 ------------ 6 files changed, 298 insertions(+), 466 deletions(-) create mode 100644 core/tests/coretests/src/android/widget/FloatingToolbarUtils.java delete mode 100644 core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java index 179d39de2cfb6..9a1192326f4fd 100644 --- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java +++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java @@ -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; } diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java index 80d8bd78e746c..8c61a12b47e60 100644 --- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java +++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java @@ -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; } diff --git a/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java new file mode 100644 index 0000000000000..c6f592447c22d --- /dev/null +++ b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java @@ -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); + } +} diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java index 28f9ccc101350..90844ea4c76f4 100644 --- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java +++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java @@ -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 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(); diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 40ef04a5e3695..3d927bfb12111 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -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 mSelectionEvents = new ArrayList<>(); final List 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) {} + } } diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java deleted file mode 100644 index 4f95cb88e217c..0000000000000 --- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +++ /dev/null @@ -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 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() { - private List 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 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 hasMenuItemId = view -> - view.getTag() instanceof MenuItemRepr - && ((MenuItemRepr) view.getTag()).itemId == menuItemId; - assertFloatingToolbarMenuItem(hasMenuItemId, false); - } - - private static void assertFloatingToolbarMenuItem( - final Predicate predicate, final boolean positiveAssertion) { - onFloatingToolBar().check(matches(new TypeSafeMatcher() { - @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 getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return "Sleep " + SLEEP_DURATION + " ms."; - } - - @Override - public void perform(UiController uiController, View view) { - uiController.loopMainThreadForAtLeast(SLEEP_DURATION); - } - }; -}