Loading core/java/android/widget/SelectionActionModeHelper.java +11 −12 Original line number Diff line number Diff line Loading @@ -68,9 +68,7 @@ final class SelectionActionModeHelper { public void startActionModeAsync(boolean adjustSelection) { cancelAsyncTask(); if (isNoOpTextClassifier() || !hasSelection()) { // No need to make an async call for a no-op TextClassifier. // Do not call the TextClassifier if there is no selection. if (skipTextClassification()) { startActionMode(null); } else { resetTextClassificationHelper(true /* resetSelectionTag */); Loading @@ -88,9 +86,7 @@ final class SelectionActionModeHelper { public void invalidateActionModeAsync() { cancelAsyncTask(); if (isNoOpTextClassifier() || !hasSelection()) { // No need to make an async call for a no-op TextClassifier. // Do not call the TextClassifier if there is no selection. if (skipTextClassification()) { invalidateActionMode(null); } else { resetTextClassificationHelper(false /* resetSelectionTag */); Loading Loading @@ -132,13 +128,16 @@ final class SelectionActionModeHelper { mTextClassification = null; } private boolean isNoOpTextClassifier() { return mEditor.getTextView().getTextClassifier() == TextClassifier.NO_OP; } private boolean hasSelection() { private boolean skipTextClassification() { final TextView textView = mEditor.getTextView(); return textView.getSelectionEnd() > textView.getSelectionStart(); // No need to make an async call for a no-op TextClassifier. final boolean noOpTextClassifier = textView.getTextClassifier() == TextClassifier.NO_OP; // Do not call the TextClassifier if there is no selection. final boolean noSelection = textView.getSelectionEnd() == textView.getSelectionStart(); // Do not call the TextClassifier if this is a password field. final boolean password = textView.hasPasswordTransformationMethod() || TextView.isPasswordInputType(textView.getInputType()); return noOpTextClassifier || noSelection || password; } private void startActionMode(@Nullable SelectionResult result) { Loading core/java/android/widget/TextView.java +1 −1 Original line number Diff line number Diff line Loading @@ -5676,7 +5676,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTransformation instanceof PasswordTransformationMethod; } private static boolean isPasswordInputType(int inputType) { static boolean isPasswordInputType(int inputType) { final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION); return variation Loading core/tests/coretests/src/android/widget/TextViewActivityTest.java +22 −1 Original line number Diff line number Diff line Loading @@ -786,7 +786,7 @@ public class TextViewActivityTest { @Test public void testAssistItemIsAtIndexZero() throws Throwable { mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null); useSystemDefaultTextClassifier(); final TextView textView = mActivity.findViewById(R.id.textview); mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback( new ActionMode.Callback() { Loading Loading @@ -821,6 +821,23 @@ public class TextViewActivityTest { assertFloatingToolbarItemIndex(android.R.id.textAssist, 0); } @Test public void testNoAssistItemForPasswordField() throws Throwable { useSystemDefaultTextClassifier(); final TextView textView = mActivity.findViewById(R.id.textview); mActivityRule.runOnUiThread(() -> { textView.setInputType( InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); }); mInstrumentation.waitForIdleSync(); final String password = "afigbo@android.com"; onView(withId(R.id.textview)).perform(replaceText(password)); onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@'))); sleepForFloatingToolbarPopup(); assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist); } @Test public void testPastePlainText_menuAction() { initializeClipboardWithText(TextStyle.STYLED); Loading Loading @@ -848,6 +865,10 @@ public class TextViewActivityTest { mActivity.getString(com.android.internal.R.string.paste_as_plain_text)); } private void useSystemDefaultTextClassifier() { mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null); } private void initializeClipboardWithText(TextStyle textStyle) { final ClipData clip; switch (textStyle) { Loading core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +44 −9 Original line number Diff line number Diff line Loading @@ -23,29 +23,31 @@ import static android.support.test.espresso.matcher.RootMatchers.withDecorView; import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.isRoot; import static android.support.test.espresso.matcher.ViewMatchers.withTagValue; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withTagValue; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; import android.view.MenuItem; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import android.support.test.espresso.NoMatchingRootException; import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.support.test.espresso.ViewInteraction; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import com.android.internal.widget.FloatingToolbar; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import java.util.ArrayList; import java.util.List; /** * Espresso utility methods for the floating toolbar. */ Loading Loading @@ -176,6 +178,39 @@ public class FloatingToolbarEspressoUtils { throw new AssertionError("Floating toolbar contains " + itemLabel); } /** * 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) { onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() { @Override public boolean matchesSafely(View view) { return !hasMenuItemWithSpecifiedId(view); } @Override public void describeTo(Description description) {} private boolean hasMenuItemWithSpecifiedId(View view) { if (view.getTag() instanceof MenuItem && ((MenuItem) view.getTag()).getItemId() == menuItemId) { return true; } else if (view instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) view; for (int i = 0; i < viewGroup.getChildCount(); i++) { if (hasMenuItemWithSpecifiedId(viewGroup.getChildAt(i))) { return true; } } } return false; } })); } /** * Click specified item on the floating tool bar. * Loading Loading
core/java/android/widget/SelectionActionModeHelper.java +11 −12 Original line number Diff line number Diff line Loading @@ -68,9 +68,7 @@ final class SelectionActionModeHelper { public void startActionModeAsync(boolean adjustSelection) { cancelAsyncTask(); if (isNoOpTextClassifier() || !hasSelection()) { // No need to make an async call for a no-op TextClassifier. // Do not call the TextClassifier if there is no selection. if (skipTextClassification()) { startActionMode(null); } else { resetTextClassificationHelper(true /* resetSelectionTag */); Loading @@ -88,9 +86,7 @@ final class SelectionActionModeHelper { public void invalidateActionModeAsync() { cancelAsyncTask(); if (isNoOpTextClassifier() || !hasSelection()) { // No need to make an async call for a no-op TextClassifier. // Do not call the TextClassifier if there is no selection. if (skipTextClassification()) { invalidateActionMode(null); } else { resetTextClassificationHelper(false /* resetSelectionTag */); Loading Loading @@ -132,13 +128,16 @@ final class SelectionActionModeHelper { mTextClassification = null; } private boolean isNoOpTextClassifier() { return mEditor.getTextView().getTextClassifier() == TextClassifier.NO_OP; } private boolean hasSelection() { private boolean skipTextClassification() { final TextView textView = mEditor.getTextView(); return textView.getSelectionEnd() > textView.getSelectionStart(); // No need to make an async call for a no-op TextClassifier. final boolean noOpTextClassifier = textView.getTextClassifier() == TextClassifier.NO_OP; // Do not call the TextClassifier if there is no selection. final boolean noSelection = textView.getSelectionEnd() == textView.getSelectionStart(); // Do not call the TextClassifier if this is a password field. final boolean password = textView.hasPasswordTransformationMethod() || TextView.isPasswordInputType(textView.getInputType()); return noOpTextClassifier || noSelection || password; } private void startActionMode(@Nullable SelectionResult result) { Loading
core/java/android/widget/TextView.java +1 −1 Original line number Diff line number Diff line Loading @@ -5676,7 +5676,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTransformation instanceof PasswordTransformationMethod; } private static boolean isPasswordInputType(int inputType) { static boolean isPasswordInputType(int inputType) { final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION); return variation Loading
core/tests/coretests/src/android/widget/TextViewActivityTest.java +22 −1 Original line number Diff line number Diff line Loading @@ -786,7 +786,7 @@ public class TextViewActivityTest { @Test public void testAssistItemIsAtIndexZero() throws Throwable { mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null); useSystemDefaultTextClassifier(); final TextView textView = mActivity.findViewById(R.id.textview); mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback( new ActionMode.Callback() { Loading Loading @@ -821,6 +821,23 @@ public class TextViewActivityTest { assertFloatingToolbarItemIndex(android.R.id.textAssist, 0); } @Test public void testNoAssistItemForPasswordField() throws Throwable { useSystemDefaultTextClassifier(); final TextView textView = mActivity.findViewById(R.id.textview); mActivityRule.runOnUiThread(() -> { textView.setInputType( InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); }); mInstrumentation.waitForIdleSync(); final String password = "afigbo@android.com"; onView(withId(R.id.textview)).perform(replaceText(password)); onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@'))); sleepForFloatingToolbarPopup(); assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist); } @Test public void testPastePlainText_menuAction() { initializeClipboardWithText(TextStyle.STYLED); Loading Loading @@ -848,6 +865,10 @@ public class TextViewActivityTest { mActivity.getString(com.android.internal.R.string.paste_as_plain_text)); } private void useSystemDefaultTextClassifier() { mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null); } private void initializeClipboardWithText(TextStyle textStyle) { final ClipData clip; switch (textStyle) { Loading
core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +44 −9 Original line number Diff line number Diff line Loading @@ -23,29 +23,31 @@ import static android.support.test.espresso.matcher.RootMatchers.withDecorView; import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.isRoot; import static android.support.test.espresso.matcher.ViewMatchers.withTagValue; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withTagValue; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; import android.view.MenuItem; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import android.support.test.espresso.NoMatchingRootException; import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.support.test.espresso.ViewInteraction; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import com.android.internal.widget.FloatingToolbar; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import java.util.ArrayList; import java.util.List; /** * Espresso utility methods for the floating toolbar. */ Loading Loading @@ -176,6 +178,39 @@ public class FloatingToolbarEspressoUtils { throw new AssertionError("Floating toolbar contains " + itemLabel); } /** * 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) { onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() { @Override public boolean matchesSafely(View view) { return !hasMenuItemWithSpecifiedId(view); } @Override public void describeTo(Description description) {} private boolean hasMenuItemWithSpecifiedId(View view) { if (view.getTag() instanceof MenuItem && ((MenuItem) view.getTag()).getItemId() == menuItemId) { return true; } else if (view instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) view; for (int i = 0; i < viewGroup.getChildCount(); i++) { if (hasMenuItemWithSpecifiedId(viewGroup.getChildAt(i))) { return true; } } } return false; } })); } /** * Click specified item on the floating tool bar. * Loading