1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.widget; 18 19import static android.support.test.espresso.Espresso.onView; 20import static android.support.test.espresso.Espresso.pressBack; 21import static android.support.test.espresso.action.ViewActions.clearText; 22import static android.support.test.espresso.action.ViewActions.click; 23import static android.support.test.espresso.action.ViewActions.replaceText; 24import static android.support.test.espresso.assertion.ViewAssertions.matches; 25import static android.support.test.espresso.matcher.RootMatchers.withDecorView; 26import static android.support.test.espresso.matcher.ViewMatchers.withId; 27import static android.support.test.espresso.matcher.ViewMatchers.withText; 28import static android.widget.espresso.DragHandleUtils.onHandleView; 29import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem; 30import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem; 31import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup; 32import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupContainsItem; 33import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsDisplayed; 34import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsNotDisplayed; 35import static android.widget.espresso.SuggestionsPopupwindowUtils.clickSuggestionsPopupItem; 36import static android.widget.espresso.SuggestionsPopupwindowUtils.onSuggestionsPopup; 37import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; 38import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; 39import static org.hamcrest.Matchers.is; 40import android.content.res.TypedArray; 41import android.support.test.espresso.NoMatchingViewException; 42import android.support.test.espresso.ViewAssertion; 43import android.test.ActivityInstrumentationTestCase2; 44import android.test.suitebuilder.annotation.SmallTest; 45import android.test.suitebuilder.annotation.Suppress; 46import android.text.Selection; 47import android.text.Spannable; 48import android.text.Spanned; 49import android.text.TextPaint; 50import android.text.style.SuggestionSpan; 51import android.text.style.TextAppearanceSpan; 52import android.view.View; 53 54import com.android.frameworks.coretests.R; 55 56/** 57 * SuggestionsPopupWindowTest tests. 58 * 59 * TODO: Add tests for when there are no suggestions 60 */ 61public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2<TextViewActivity> { 62 63 public SuggestionsPopupWindowTest() { 64 super(TextViewActivity.class); 65 } 66 67 @Override 68 protected void setUp() throws Exception { 69 super.setUp(); 70 getActivity(); 71 } 72 73 private void setSuggestionSpan(SuggestionSpan span, int start, int end) { 74 final TextView textView = (TextView) getActivity().findViewById(R.id.textview); 75 textView.post( 76 () -> { 77 final Spannable text = (Spannable) textView.getText(); 78 text.setSpan(span, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 79 Selection.setSelection(text, (start + end) / 2); 80 }); 81 getInstrumentation().waitForIdleSync(); 82 } 83 84 @SmallTest 85 public void testOnTextContextMenuItem() { 86 final String text = "abc def ghi"; 87 88 onView(withId(R.id.textview)).perform(click()); 89 onView(withId(R.id.textview)).perform(replaceText(text)); 90 91 final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), 92 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION); 93 setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); 94 95 final TextView textView = (TextView) getActivity().findViewById(R.id.textview); 96 textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE)); 97 getInstrumentation().waitForIdleSync(); 98 99 assertSuggestionsPopupIsDisplayed(); 100 } 101 102 @SmallTest 103 public void testSelectionActionMode() { 104 final String text = "abc def ghi"; 105 106 onView(withId(R.id.textview)).perform(click()); 107 onView(withId(R.id.textview)).perform(replaceText(text)); 108 109 final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), 110 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION); 111 setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); 112 113 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e'))); 114 sleepForFloatingToolbarPopup(); 115 assertFloatingToolbarContainsItem( 116 getActivity().getString(com.android.internal.R.string.replace)); 117 sleepForFloatingToolbarPopup(); 118 clickFloatingToolbarItem( 119 getActivity().getString(com.android.internal.R.string.replace)); 120 121 assertSuggestionsPopupIsDisplayed(); 122 } 123 124 @SmallTest 125 public void testInsertionActionMode() { 126 final String text = "abc def ghi"; 127 128 onView(withId(R.id.textview)).perform(click()); 129 onView(withId(R.id.textview)).perform(replaceText(text)); 130 131 final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), 132 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION); 133 setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); 134 135 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e'))); 136 onHandleView(com.android.internal.R.id.insertion_handle).perform(click()); 137 sleepForFloatingToolbarPopup(); 138 assertFloatingToolbarContainsItem( 139 getActivity().getString(com.android.internal.R.string.replace)); 140 clickFloatingToolbarItem( 141 getActivity().getString(com.android.internal.R.string.replace)); 142 143 assertSuggestionsPopupIsDisplayed(); 144 } 145 146 private void showSuggestionsPopup() { 147 final TextView textView = (TextView) getActivity().findViewById(R.id.textview); 148 textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE)); 149 getInstrumentation().waitForIdleSync(); 150 assertSuggestionsPopupIsDisplayed(); 151 } 152 153 @SmallTest 154 public void testSuggestionItems() { 155 final String text = "abc def ghi"; 156 157 onView(withId(R.id.textview)).perform(click()); 158 onView(withId(R.id.textview)).perform(replaceText(text)); 159 160 final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), 161 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION); 162 setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); 163 164 showSuggestionsPopup(); 165 166 assertSuggestionsPopupIsDisplayed(); 167 assertSuggestionsPopupContainsItem("DEF"); 168 assertSuggestionsPopupContainsItem("Def"); 169 assertSuggestionsPopupContainsItem( 170 getActivity().getString(com.android.internal.R.string.delete)); 171 172 // Select an item. 173 clickSuggestionsPopupItem("DEF"); 174 assertSuggestionsPopupIsNotDisplayed(); 175 onView(withId(R.id.textview)).check(matches(withText("abc DEF ghi"))); 176 177 showSuggestionsPopup(); 178 assertSuggestionsPopupIsDisplayed(); 179 assertSuggestionsPopupContainsItem("def"); 180 assertSuggestionsPopupContainsItem("Def"); 181 assertSuggestionsPopupContainsItem( 182 getActivity().getString(com.android.internal.R.string.delete)); 183 184 // Delete 185 clickSuggestionsPopupItem( 186 getActivity().getString(com.android.internal.R.string.delete)); 187 assertSuggestionsPopupIsNotDisplayed(); 188 onView(withId(R.id.textview)).check(matches(withText("abc ghi"))); 189 } 190 191 @SmallTest 192 public void testMisspelled() { 193 final String text = "abc def ghi"; 194 195 onView(withId(R.id.textview)).perform(click()); 196 onView(withId(R.id.textview)).perform(replaceText(text)); 197 198 final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), 199 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_MISSPELLED); 200 setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); 201 202 showSuggestionsPopup(); 203 204 assertSuggestionsPopupIsDisplayed(); 205 assertSuggestionsPopupContainsItem("DEF"); 206 assertSuggestionsPopupContainsItem("Def"); 207 assertSuggestionsPopupContainsItem( 208 getActivity().getString(com.android.internal.R.string.addToDictionary)); 209 assertSuggestionsPopupContainsItem( 210 getActivity().getString(com.android.internal.R.string.delete)); 211 212 // Click "Add to dictionary". 213 clickSuggestionsPopupItem( 214 getActivity().getString(com.android.internal.R.string.addToDictionary)); 215 // TODO: Check if add to dictionary dialog is displayed. 216 } 217 218 @SmallTest 219 public void testEasyCorrect() { 220 final String text = "abc def ghi"; 221 222 onView(withId(R.id.textview)).perform(click()); 223 onView(withId(R.id.textview)).perform(replaceText(text)); 224 225 final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), 226 new String[]{"DEF", "Def"}, 227 SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED); 228 setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); 229 230 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e'))); 231 232 assertSuggestionsPopupIsDisplayed(); 233 assertSuggestionsPopupContainsItem("DEF"); 234 assertSuggestionsPopupContainsItem("Def"); 235 assertSuggestionsPopupContainsItem( 236 getActivity().getString(com.android.internal.R.string.delete)); 237 238 // Select an item. 239 clickSuggestionsPopupItem("DEF"); 240 assertSuggestionsPopupIsNotDisplayed(); 241 onView(withId(R.id.textview)).check(matches(withText("abc DEF ghi"))); 242 243 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e'))); 244 assertSuggestionsPopupIsNotDisplayed(); 245 246 showSuggestionsPopup(); 247 assertSuggestionsPopupIsDisplayed(); 248 assertSuggestionsPopupContainsItem("def"); 249 assertSuggestionsPopupContainsItem("Def"); 250 assertSuggestionsPopupContainsItem( 251 getActivity().getString(com.android.internal.R.string.delete)); 252 } 253 254 @SmallTest 255 public void testTextAppearanceInSuggestionsPopup() { 256 final String text = "abc def ghi"; 257 258 final String[] singleWordCandidates = {"DEF", "Def"}; 259 final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), 260 singleWordCandidates, SuggestionSpan.FLAG_MISSPELLED); 261 final String[] multiWordCandidates = {"ABC DEF GHI", "Abc Def Ghi"}; 262 final SuggestionSpan multiWordSuggestionSpan = new SuggestionSpan(getActivity(), 263 multiWordCandidates, SuggestionSpan.FLAG_MISSPELLED); 264 265 final TypedArray array = 266 getActivity().obtainStyledAttributes(com.android.internal.R.styleable.Theme); 267 final int id = array.getResourceId( 268 com.android.internal.R.styleable.Theme_textEditSuggestionHighlightStyle, 0); 269 array.recycle(); 270 final TextAppearanceSpan expectedSpan = new TextAppearanceSpan(getActivity(), id); 271 final TextPaint tmpTp = new TextPaint(); 272 expectedSpan.updateDrawState(tmpTp); 273 final int expectedHighlightTextColor = tmpTp.getColor(); 274 final float expectedHighlightTextSize = tmpTp.getTextSize(); 275 final TextView textView = (TextView) getActivity().findViewById(R.id.textview); 276 277 // In this test, the SuggestionsPopupWindow looks like 278 // abc def ghi 279 // ----------------- 280 // | abc *DEF* ghi | 281 // | abc *Def* ghi | 282 // | *ABC DEF GHI* | 283 // | *Abc Def Ghi* | 284 // ----------------- 285 // | DELETE | 286 // ----------------- 287 // *XX* means that XX is highlighted. 288 for (int i = 0; i < 2; i++) { 289 onView(withId(R.id.textview)).perform(click()); 290 onView(withId(R.id.textview)).perform(replaceText(text)); 291 setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); 292 setSuggestionSpan(multiWordSuggestionSpan, 0, text.length()); 293 294 showSuggestionsPopup(); 295 assertSuggestionsPopupIsDisplayed(); 296 assertSuggestionsPopupContainsItem("abc DEF ghi"); 297 assertSuggestionsPopupContainsItem("abc Def ghi"); 298 assertSuggestionsPopupContainsItem("ABC DEF GHI"); 299 assertSuggestionsPopupContainsItem("Abc Def Ghi"); 300 assertSuggestionsPopupContainsItem( 301 getActivity().getString(com.android.internal.R.string.delete)); 302 303 onSuggestionsPopup().check(new ViewAssertion() { 304 @Override 305 public void check(View view, NoMatchingViewException e) { 306 final ListView listView = (ListView) view.findViewById( 307 com.android.internal.R.id.suggestionContainer); 308 assertNotNull(listView); 309 final int childNum = listView.getChildCount(); 310 assertEquals(singleWordCandidates.length + multiWordCandidates.length, 311 childNum); 312 313 for (int j = 0; j < childNum; j++) { 314 final TextView suggestion = (TextView) listView.getChildAt(j); 315 assertNotNull(suggestion); 316 final Spanned spanned = (Spanned) suggestion.getText(); 317 assertNotNull(spanned); 318 319 // Check that the suggestion item order is kept. 320 final String expectedText; 321 if (j < singleWordCandidates.length) { 322 expectedText = "abc " + singleWordCandidates[j] + " ghi"; 323 } else { 324 expectedText = multiWordCandidates[j - singleWordCandidates.length]; 325 } 326 assertEquals(expectedText, spanned.toString()); 327 328 // Check that the text is highlighted with correct color and text size. 329 final TextAppearanceSpan[] taSpan = spanned.getSpans( 330 text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class); 331 assertEquals(1, taSpan.length); 332 TextPaint tp = new TextPaint(); 333 taSpan[0].updateDrawState(tp); 334 assertEquals(expectedHighlightTextColor, tp.getColor()); 335 assertEquals(expectedHighlightTextSize, tp.getTextSize()); 336 337 // Check the correct part of the text is highlighted. 338 final int expectedStart; 339 final int expectedEnd; 340 if (j < singleWordCandidates.length) { 341 expectedStart = text.indexOf('d'); 342 expectedEnd = text.indexOf('f') + 1; 343 } else { 344 expectedStart = 0; 345 expectedEnd = text.length(); 346 } 347 assertEquals(expectedStart, spanned.getSpanStart(taSpan[0])); 348 assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0])); 349 } 350 } 351 }); 352 pressBack(); 353 onView(withId(R.id.textview)) 354 .inRoot(withDecorView(is(getActivity().getWindow().getDecorView()))) 355 .perform(clearText()); 356 } 357 } 358} 359