1 /* 2 * Copyright (C) 2008 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 17 package android.widget.cts; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNotEquals; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertNull; 25 import static org.junit.Assert.assertSame; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 import static org.mockito.Matchers.any; 29 import static org.mockito.Matchers.eq; 30 import static org.mockito.Matchers.refEq; 31 import static org.mockito.Mockito.doAnswer; 32 import static org.mockito.Mockito.doCallRealMethod; 33 import static org.mockito.Mockito.doNothing; 34 import static org.mockito.Mockito.mock; 35 import static org.mockito.Mockito.never; 36 import static org.mockito.Mockito.reset; 37 import static org.mockito.Mockito.spy; 38 import static org.mockito.Mockito.times; 39 import static org.mockito.Mockito.verify; 40 import static org.mockito.Mockito.verifyNoMoreInteractions; 41 import static org.mockito.Mockito.verifyZeroInteractions; 42 import static org.mockito.Mockito.when; 43 44 import static java.lang.annotation.RetentionPolicy.SOURCE; 45 46 import android.annotation.IntDef; 47 import android.annotation.Nullable; 48 import android.app.Activity; 49 import android.app.Instrumentation; 50 import android.app.Instrumentation.ActivityMonitor; 51 import android.content.Context; 52 import android.content.Intent; 53 import android.content.pm.PackageManager; 54 import android.content.res.ColorStateList; 55 import android.content.res.Configuration; 56 import android.content.res.Resources; 57 import android.content.res.Resources.NotFoundException; 58 import android.graphics.Color; 59 import android.graphics.Paint; 60 import android.graphics.Path; 61 import android.graphics.Point; 62 import android.graphics.PorterDuff; 63 import android.graphics.Rect; 64 import android.graphics.RectF; 65 import android.graphics.Typeface; 66 import android.graphics.drawable.BitmapDrawable; 67 import android.graphics.drawable.ColorDrawable; 68 import android.graphics.drawable.Drawable; 69 import android.icu.lang.UCharacter; 70 import android.net.Uri; 71 import android.os.Bundle; 72 import android.os.Handler; 73 import android.os.LocaleList; 74 import android.os.Looper; 75 import android.os.Parcelable; 76 import android.os.ResultReceiver; 77 import android.os.SystemClock; 78 import android.support.test.InstrumentationRegistry; 79 import android.support.test.annotation.UiThreadTest; 80 import android.support.test.filters.MediumTest; 81 import android.support.test.filters.SmallTest; 82 import android.support.test.rule.ActivityTestRule; 83 import android.support.test.runner.AndroidJUnit4; 84 import android.text.Editable; 85 import android.text.InputFilter; 86 import android.text.InputType; 87 import android.text.Layout; 88 import android.text.Selection; 89 import android.text.Spannable; 90 import android.text.SpannableString; 91 import android.text.Spanned; 92 import android.text.TextPaint; 93 import android.text.TextUtils; 94 import android.text.TextUtils.TruncateAt; 95 import android.text.TextWatcher; 96 import android.text.method.ArrowKeyMovementMethod; 97 import android.text.method.DateKeyListener; 98 import android.text.method.DateTimeKeyListener; 99 import android.text.method.DialerKeyListener; 100 import android.text.method.DigitsKeyListener; 101 import android.text.method.KeyListener; 102 import android.text.method.LinkMovementMethod; 103 import android.text.method.MovementMethod; 104 import android.text.method.PasswordTransformationMethod; 105 import android.text.method.QwertyKeyListener; 106 import android.text.method.SingleLineTransformationMethod; 107 import android.text.method.TextKeyListener; 108 import android.text.method.TextKeyListener.Capitalize; 109 import android.text.method.TimeKeyListener; 110 import android.text.method.TransformationMethod; 111 import android.text.style.ClickableSpan; 112 import android.text.style.ImageSpan; 113 import android.text.style.URLSpan; 114 import android.text.style.UnderlineSpan; 115 import android.text.util.Linkify; 116 import android.util.AttributeSet; 117 import android.util.DisplayMetrics; 118 import android.util.SparseArray; 119 import android.util.TypedValue; 120 import android.view.ActionMode; 121 import android.view.ContextMenu; 122 import android.view.Gravity; 123 import android.view.InputDevice; 124 import android.view.KeyEvent; 125 import android.view.LayoutInflater; 126 import android.view.Menu; 127 import android.view.MotionEvent; 128 import android.view.PointerIcon; 129 import android.view.View; 130 import android.view.ViewConfiguration; 131 import android.view.ViewGroup; 132 import android.view.ViewGroup.LayoutParams; 133 import android.view.accessibility.AccessibilityNodeInfo; 134 import android.view.inputmethod.BaseInputConnection; 135 import android.view.inputmethod.CompletionInfo; 136 import android.view.inputmethod.CorrectionInfo; 137 import android.view.inputmethod.EditorInfo; 138 import android.view.inputmethod.ExtractedText; 139 import android.view.inputmethod.ExtractedTextRequest; 140 import android.view.inputmethod.InputConnection; 141 import android.view.inputmethod.InputMethodManager; 142 import android.view.textclassifier.TextClassification; 143 import android.view.textclassifier.TextClassifier; 144 import android.view.textclassifier.TextSelection; 145 import android.widget.EditText; 146 import android.widget.FrameLayout; 147 import android.widget.LinearLayout; 148 import android.widget.Scroller; 149 import android.widget.TextView; 150 import android.widget.TextView.BufferType; 151 import android.widget.cts.util.TestUtils; 152 153 import com.android.compatibility.common.util.CtsKeyEventUtil; 154 import com.android.compatibility.common.util.CtsTouchUtils; 155 import com.android.compatibility.common.util.PollingCheck; 156 import com.android.compatibility.common.util.WidgetTestUtils; 157 158 import org.junit.Before; 159 import org.junit.Rule; 160 import org.junit.Test; 161 import org.junit.runner.RunWith; 162 import org.mockito.invocation.InvocationOnMock; 163 import org.xmlpull.v1.XmlPullParserException; 164 165 import java.io.IOException; 166 import java.lang.annotation.Retention; 167 import java.util.Arrays; 168 import java.util.Locale; 169 170 /** 171 * Test {@link TextView}. 172 */ 173 @MediumTest 174 @RunWith(AndroidJUnit4.class) 175 public class TextViewTest { 176 private Instrumentation mInstrumentation; 177 private Activity mActivity; 178 private TextView mTextView; 179 private TextView mSecondTextView; 180 181 private static final String LONG_TEXT = "This is a really long string which exceeds " 182 + "the width of the view. New devices have a much larger screen which " 183 + "actually enables long strings to be displayed with no fading. " 184 + "I have made this string longer to fix this case. If you are correcting " 185 + "this text, I would love to see the kind of devices you guys now use!"; 186 private static final long TIMEOUT = 5000; 187 188 private static final int SMARTSELECT_START = 0; 189 private static final int SMARTSELECT_END = 40; 190 private static final TextClassifier FAKE_TEXT_CLASSIFIER = new TextClassifier() { 191 @Override 192 public TextSelection suggestSelection( 193 CharSequence text, int start, int end, LocaleList locales) { 194 return new TextSelection.Builder(SMARTSELECT_START, SMARTSELECT_END).build(); 195 } 196 197 @Override 198 public TextClassification classifyText( 199 CharSequence text, int start, int end, LocaleList locales) { 200 return new TextClassification.Builder().build(); 201 } 202 }; 203 private static final int CLICK_TIMEOUT = ViewConfiguration.getDoubleTapTimeout() + 50; 204 205 private CharSequence mTransformedText; 206 private Handler mHandler = new Handler(Looper.getMainLooper()); 207 208 @Rule 209 public ActivityTestRule<TextViewCtsActivity> mActivityRule = 210 new ActivityTestRule<>(TextViewCtsActivity.class); 211 212 @Before 213 public void setup() { 214 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 215 mActivity = mActivityRule.getActivity(); 216 PollingCheck.waitFor(mActivity::hasWindowFocus); 217 } 218 219 /** 220 * Promotes the TextView to editable and places focus in it to allow simulated typing. Used in 221 * test methods annotated with {@link UiThreadTest}. 222 */ 223 private void initTextViewForTyping() { 224 mTextView = findTextView(R.id.textview_text); 225 mTextView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE)); 226 mTextView.setText("", BufferType.EDITABLE); 227 mTextView.requestFocus(); 228 // Disable smart selection 229 mTextView.setTextClassifier(TextClassifier.NO_OP); 230 } 231 232 /** 233 * Used in test methods that can not entirely be run on the UiThread (e.g: tests that need to 234 * emulate touches and/or key presses). 235 */ 236 private void initTextViewForTypingOnUiThread() throws Throwable { 237 mActivityRule.runOnUiThread(this::initTextViewForTyping); 238 mInstrumentation.waitForIdleSync(); 239 } 240 241 @UiThreadTest 242 @Test 243 public void testConstructorOnUiThread() { 244 verifyConstructor(); 245 } 246 247 @Test 248 public void testConstructorOffUiThread() { 249 verifyConstructor(); 250 } 251 252 private void verifyConstructor() { 253 new TextView(mActivity); 254 new TextView(mActivity, null); 255 new TextView(mActivity, null, android.R.attr.textViewStyle); 256 new TextView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_TextView); 257 new TextView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_TextView); 258 new TextView(mActivity, null, 0, android.R.style.Widget_Material_TextView); 259 new TextView(mActivity, null, 0, android.R.style.Widget_Material_Light_TextView); 260 } 261 262 @UiThreadTest 263 @Test 264 public void testAccessText() { 265 TextView tv = findTextView(R.id.textview_text); 266 267 String expected = mActivity.getResources().getString(R.string.text_view_hello); 268 tv.setText(expected); 269 assertEquals(expected, tv.getText().toString()); 270 271 tv.setText(null); 272 assertEquals("", tv.getText().toString()); 273 } 274 275 @UiThreadTest 276 @Test 277 public void testGetLineHeight() { 278 mTextView = new TextView(mActivity); 279 assertTrue(mTextView.getLineHeight() > 0); 280 281 mTextView.setLineSpacing(1.2f, 1.5f); 282 assertTrue(mTextView.getLineHeight() > 0); 283 } 284 285 @Test 286 public void testGetLayout() throws Throwable { 287 mActivityRule.runOnUiThread(() -> { 288 mTextView = findTextView(R.id.textview_text); 289 mTextView.setGravity(Gravity.CENTER); 290 }); 291 mInstrumentation.waitForIdleSync(); 292 assertNotNull(mTextView.getLayout()); 293 294 TestLayoutRunnable runnable = new TestLayoutRunnable(mTextView) { 295 public void run() { 296 // change the text of TextView. 297 mTextView.setText("Hello, Android!"); 298 saveLayout(); 299 } 300 }; 301 mActivityRule.runOnUiThread(runnable); 302 mInstrumentation.waitForIdleSync(); 303 assertNull(runnable.getLayout()); 304 assertNotNull(mTextView.getLayout()); 305 } 306 307 @Test 308 public void testAccessKeyListener() throws Throwable { 309 mActivityRule.runOnUiThread(() -> mTextView = findTextView(R.id.textview_text)); 310 mInstrumentation.waitForIdleSync(); 311 312 assertNull(mTextView.getKeyListener()); 313 314 final KeyListener digitsKeyListener = DigitsKeyListener.getInstance(); 315 316 mActivityRule.runOnUiThread(() -> mTextView.setKeyListener(digitsKeyListener)); 317 mInstrumentation.waitForIdleSync(); 318 assertSame(digitsKeyListener, mTextView.getKeyListener()); 319 320 final QwertyKeyListener qwertyKeyListener 321 = QwertyKeyListener.getInstance(false, Capitalize.NONE); 322 mActivityRule.runOnUiThread(() -> mTextView.setKeyListener(qwertyKeyListener)); 323 mInstrumentation.waitForIdleSync(); 324 assertSame(qwertyKeyListener, mTextView.getKeyListener()); 325 } 326 327 @Test 328 public void testAccessMovementMethod() throws Throwable { 329 final CharSequence LONG_TEXT = "Scrolls the specified widget to the specified " 330 + "coordinates, except constrains the X scrolling position to the horizontal " 331 + "regions of the text that will be visible after scrolling to " 332 + "the specified Y position."; 333 final int selectionStart = 10; 334 final int selectionEnd = LONG_TEXT.length(); 335 final MovementMethod movementMethod = ArrowKeyMovementMethod.getInstance(); 336 mActivityRule.runOnUiThread(() -> { 337 mTextView = findTextView(R.id.textview_text); 338 mTextView.setMovementMethod(movementMethod); 339 mTextView.setText(LONG_TEXT, BufferType.EDITABLE); 340 Selection.setSelection((Editable) mTextView.getText(), 341 selectionStart, selectionEnd); 342 mTextView.requestFocus(); 343 }); 344 mInstrumentation.waitForIdleSync(); 345 346 assertSame(movementMethod, mTextView.getMovementMethod()); 347 assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText())); 348 assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText())); 349 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_SHIFT_LEFT, 350 KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_DPAD_UP); 351 // the selection has been removed. 352 assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText())); 353 assertEquals(selectionStart, Selection.getSelectionEnd(mTextView.getText())); 354 355 mActivityRule.runOnUiThread(() -> { 356 mTextView.setMovementMethod(null); 357 Selection.setSelection((Editable) mTextView.getText(), 358 selectionStart, selectionEnd); 359 mTextView.requestFocus(); 360 }); 361 mInstrumentation.waitForIdleSync(); 362 363 assertNull(mTextView.getMovementMethod()); 364 assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText())); 365 assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText())); 366 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_SHIFT_LEFT, 367 KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_DPAD_UP); 368 // the selection will not be changed. 369 assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText())); 370 assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText())); 371 } 372 373 @UiThreadTest 374 @Test 375 public void testLength() { 376 mTextView = findTextView(R.id.textview_text); 377 378 String content = "This is content"; 379 mTextView.setText(content); 380 assertEquals(content.length(), mTextView.length()); 381 382 mTextView.setText(""); 383 assertEquals(0, mTextView.length()); 384 385 mTextView.setText(null); 386 assertEquals(0, mTextView.length()); 387 } 388 389 @UiThreadTest 390 @Test 391 public void testAccessGravity() { 392 mActivity.setContentView(R.layout.textview_gravity); 393 394 mTextView = findTextView(R.id.gravity_default); 395 assertEquals(Gravity.TOP | Gravity.START, mTextView.getGravity()); 396 397 mTextView = findTextView(R.id.gravity_bottom); 398 assertEquals(Gravity.BOTTOM | Gravity.START, mTextView.getGravity()); 399 400 mTextView = findTextView(R.id.gravity_right); 401 assertEquals(Gravity.TOP | Gravity.RIGHT, mTextView.getGravity()); 402 403 mTextView = findTextView(R.id.gravity_center); 404 assertEquals(Gravity.CENTER, mTextView.getGravity()); 405 406 mTextView = findTextView(R.id.gravity_fill); 407 assertEquals(Gravity.FILL, mTextView.getGravity()); 408 409 mTextView = findTextView(R.id.gravity_center_vertical_right); 410 assertEquals(Gravity.CENTER_VERTICAL | Gravity.RIGHT, mTextView.getGravity()); 411 412 mTextView.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); 413 assertEquals(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, mTextView.getGravity()); 414 mTextView.setGravity(Gravity.FILL); 415 assertEquals(Gravity.FILL, mTextView.getGravity()); 416 mTextView.setGravity(Gravity.CENTER); 417 assertEquals(Gravity.CENTER, mTextView.getGravity()); 418 419 mTextView.setGravity(Gravity.NO_GRAVITY); 420 assertEquals(Gravity.TOP | Gravity.START, mTextView.getGravity()); 421 422 mTextView.setGravity(Gravity.RIGHT); 423 assertEquals(Gravity.TOP | Gravity.RIGHT, mTextView.getGravity()); 424 425 mTextView.setGravity(Gravity.FILL_VERTICAL); 426 assertEquals(Gravity.FILL_VERTICAL | Gravity.START, mTextView.getGravity()); 427 428 //test negative input value. 429 mTextView.setGravity(-1); 430 assertEquals(-1, mTextView.getGravity()); 431 } 432 433 @Retention(SOURCE) 434 @IntDef({EditorInfo.IME_ACTION_UNSPECIFIED, EditorInfo.IME_ACTION_NONE, 435 EditorInfo.IME_ACTION_GO, EditorInfo.IME_ACTION_SEARCH, EditorInfo.IME_ACTION_SEND, 436 EditorInfo.IME_ACTION_NEXT, EditorInfo.IME_ACTION_DONE, EditorInfo.IME_ACTION_PREVIOUS}) 437 private @interface ImeOptionAction {} 438 439 @Retention(SOURCE) 440 @IntDef(flag = true, 441 value = {EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING, 442 EditorInfo.IME_FLAG_NO_FULLSCREEN, EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, 443 EditorInfo.IME_FLAG_NAVIGATE_NEXT, EditorInfo.IME_FLAG_NO_EXTRACT_UI, 444 EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION, EditorInfo.IME_FLAG_NO_ENTER_ACTION, 445 EditorInfo.IME_FLAG_FORCE_ASCII}) 446 private @interface ImeOptionFlags {} 447 448 private static void assertImeOptions(TextView textView, 449 @ImeOptionAction int expectedImeOptionAction, 450 @ImeOptionFlags int expectedImeOptionFlags) { 451 final int actualAction = textView.getImeOptions() & EditorInfo.IME_MASK_ACTION; 452 final int actualFlags = textView.getImeOptions() & ~EditorInfo.IME_MASK_ACTION; 453 assertEquals(expectedImeOptionAction, actualAction); 454 assertEquals(expectedImeOptionFlags, actualFlags); 455 } 456 457 @UiThreadTest 458 @Test 459 public void testImeOptions() { 460 mActivity.setContentView(R.layout.textview_imeoptions); 461 462 // Test "normal" to be a synonym EditorInfo.IME_NULL 463 assertEquals(EditorInfo.IME_NULL, 464 mActivity.<TextView>findViewById(R.id.textview_imeoption_normal).getImeOptions()); 465 466 // Test EditorInfo.IME_ACTION_* 467 assertImeOptions( 468 mActivity.findViewById(R.id.textview_imeoption_action_unspecified), 469 EditorInfo.IME_ACTION_UNSPECIFIED, 0); 470 assertImeOptions( 471 mActivity.findViewById(R.id.textview_imeoption_action_none), 472 EditorInfo.IME_ACTION_NONE, 0); 473 assertImeOptions( 474 mActivity.findViewById(R.id.textview_imeoption_action_go), 475 EditorInfo.IME_ACTION_GO, 0); 476 assertImeOptions( 477 mActivity.findViewById(R.id.textview_imeoption_action_search), 478 EditorInfo.IME_ACTION_SEARCH, 0); 479 assertImeOptions( 480 mActivity.findViewById(R.id.textview_imeoption_action_send), 481 EditorInfo.IME_ACTION_SEND, 0); 482 assertImeOptions( 483 mActivity.findViewById(R.id.textview_imeoption_action_next), 484 EditorInfo.IME_ACTION_NEXT, 0); 485 assertImeOptions( 486 mActivity.findViewById(R.id.textview_imeoption_action_done), 487 EditorInfo.IME_ACTION_DONE, 0); 488 assertImeOptions( 489 mActivity.findViewById(R.id.textview_imeoption_action_previous), 490 EditorInfo.IME_ACTION_PREVIOUS, 0); 491 492 // Test EditorInfo.IME_FLAG_* 493 assertImeOptions( 494 mActivity.findViewById(R.id.textview_imeoption_no_personalized_learning), 495 EditorInfo.IME_ACTION_UNSPECIFIED, 496 EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING); 497 assertImeOptions( 498 mActivity.findViewById(R.id.textview_imeoption_no_fullscreen), 499 EditorInfo.IME_ACTION_UNSPECIFIED, 500 EditorInfo.IME_FLAG_NO_FULLSCREEN); 501 assertImeOptions( 502 mActivity.findViewById(R.id.textview_imeoption_navigation_previous), 503 EditorInfo.IME_ACTION_UNSPECIFIED, 504 EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS); 505 assertImeOptions( 506 mActivity.findViewById(R.id.textview_imeoption_navigation_next), 507 EditorInfo.IME_ACTION_UNSPECIFIED, 508 EditorInfo.IME_FLAG_NAVIGATE_NEXT); 509 assertImeOptions( 510 mActivity.findViewById(R.id.textview_imeoption_no_extract_ui), 511 EditorInfo.IME_ACTION_UNSPECIFIED, 512 EditorInfo.IME_FLAG_NO_EXTRACT_UI); 513 assertImeOptions( 514 mActivity.findViewById(R.id.textview_imeoption_no_accessory_action), 515 EditorInfo.IME_ACTION_UNSPECIFIED, 516 EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION); 517 assertImeOptions( 518 mActivity.findViewById(R.id.textview_imeoption_no_enter_action), 519 EditorInfo.IME_ACTION_UNSPECIFIED, 520 EditorInfo.IME_FLAG_NO_ENTER_ACTION); 521 assertImeOptions( 522 mActivity.findViewById(R.id.textview_imeoption_force_ascii), 523 EditorInfo.IME_ACTION_UNSPECIFIED, 524 EditorInfo.IME_FLAG_FORCE_ASCII); 525 526 // test action + multiple flags 527 assertImeOptions( 528 mActivity.findViewById( 529 R.id.textview_imeoption_action_go_nagivate_next_no_extract_ui_force_ascii), 530 EditorInfo.IME_ACTION_GO, 531 EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI 532 | EditorInfo.IME_FLAG_FORCE_ASCII); 533 } 534 535 @Test 536 public void testAccessAutoLinkMask() throws Throwable { 537 mTextView = findTextView(R.id.textview_text); 538 final CharSequence text1 = 539 new SpannableString("URL: http://www.google.com. mailto: account (at) gmail.com"); 540 mActivityRule.runOnUiThread(() -> { 541 mTextView.setAutoLinkMask(Linkify.ALL); 542 mTextView.setText(text1, BufferType.EDITABLE); 543 }); 544 mInstrumentation.waitForIdleSync(); 545 assertEquals(Linkify.ALL, mTextView.getAutoLinkMask()); 546 547 Spannable spanString = (Spannable) mTextView.getText(); 548 URLSpan[] spans = spanString.getSpans(0, spanString.length(), URLSpan.class); 549 assertNotNull(spans); 550 assertEquals(2, spans.length); 551 assertEquals("http://www.google.com", spans[0].getURL()); 552 assertEquals("mailto:account (at) gmail.com", spans[1].getURL()); 553 554 final CharSequence text2 = 555 new SpannableString("name: Jack. tel: +41 44 800 8999"); 556 mActivityRule.runOnUiThread(() -> { 557 mTextView.setAutoLinkMask(Linkify.PHONE_NUMBERS); 558 mTextView.setText(text2, BufferType.EDITABLE); 559 }); 560 mInstrumentation.waitForIdleSync(); 561 assertEquals(Linkify.PHONE_NUMBERS, mTextView.getAutoLinkMask()); 562 563 spanString = (Spannable) mTextView.getText(); 564 spans = spanString.getSpans(0, spanString.length(), URLSpan.class); 565 assertNotNull(spans); 566 assertEquals(1, spans.length); 567 assertEquals("tel:+41448008999", spans[0].getURL()); 568 569 layout(R.layout.textview_autolink); 570 // 1 for web, 2 for email, 4 for phone, 7 for all(web|email|phone) 571 assertEquals(0, getAutoLinkMask(R.id.autolink_default)); 572 assertEquals(Linkify.WEB_URLS, getAutoLinkMask(R.id.autolink_web)); 573 assertEquals(Linkify.EMAIL_ADDRESSES, getAutoLinkMask(R.id.autolink_email)); 574 assertEquals(Linkify.PHONE_NUMBERS, getAutoLinkMask(R.id.autolink_phone)); 575 assertEquals(Linkify.ALL, getAutoLinkMask(R.id.autolink_all)); 576 assertEquals(Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES, 577 getAutoLinkMask(R.id.autolink_compound1)); 578 assertEquals(Linkify.WEB_URLS | Linkify.PHONE_NUMBERS, 579 getAutoLinkMask(R.id.autolink_compound2)); 580 assertEquals(Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS, 581 getAutoLinkMask(R.id.autolink_compound3)); 582 assertEquals(Linkify.PHONE_NUMBERS | Linkify.ALL, 583 getAutoLinkMask(R.id.autolink_compound4)); 584 } 585 586 @UiThreadTest 587 @Test 588 public void testAccessTextSize() { 589 DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics(); 590 591 mTextView = new TextView(mActivity); 592 mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 20f); 593 assertEquals(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 20f, metrics), 594 mTextView.getTextSize(), 0.01f); 595 596 mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20f); 597 assertEquals(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20f, metrics), 598 mTextView.getTextSize(), 0.01f); 599 600 mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f); 601 assertEquals(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20f, metrics), 602 mTextView.getTextSize(), 0.01f); 603 604 // setTextSize by default unit "sp" 605 mTextView.setTextSize(20f); 606 assertEquals(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20f, metrics), 607 mTextView.getTextSize(), 0.01f); 608 609 mTextView.setTextSize(200f); 610 assertEquals(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 200f, metrics), 611 mTextView.getTextSize(), 0.01f); 612 } 613 614 @UiThreadTest 615 @Test 616 public void testAccessTextColor() { 617 mTextView = new TextView(mActivity); 618 619 mTextView.setTextColor(Color.GREEN); 620 assertEquals(Color.GREEN, mTextView.getCurrentTextColor()); 621 assertSame(ColorStateList.valueOf(Color.GREEN), mTextView.getTextColors()); 622 623 mTextView.setTextColor(Color.BLACK); 624 assertEquals(Color.BLACK, mTextView.getCurrentTextColor()); 625 assertSame(ColorStateList.valueOf(Color.BLACK), mTextView.getTextColors()); 626 627 mTextView.setTextColor(Color.RED); 628 assertEquals(Color.RED, mTextView.getCurrentTextColor()); 629 assertSame(ColorStateList.valueOf(Color.RED), mTextView.getTextColors()); 630 631 // using ColorStateList 632 // normal 633 ColorStateList colors = new ColorStateList(new int[][] { 634 new int[] { android.R.attr.state_focused}, new int[0] }, 635 new int[] { Color.rgb(0, 255, 0), Color.BLACK }); 636 mTextView.setTextColor(colors); 637 assertSame(colors, mTextView.getTextColors()); 638 assertEquals(Color.BLACK, mTextView.getCurrentTextColor()); 639 640 // exceptional 641 try { 642 mTextView.setTextColor(null); 643 fail("Should thrown exception if the colors is null"); 644 } catch (NullPointerException e){ 645 } 646 } 647 648 @Test 649 public void testGetTextColor() { 650 // TODO: How to get a suitable TypedArray to test this method. 651 652 try { 653 TextView.getTextColor(mActivity, null, -1); 654 fail("There should be a NullPointerException thrown out."); 655 } catch (NullPointerException e) { 656 } 657 } 658 659 @Test 660 public void testAccessHighlightColor() throws Throwable { 661 final TextView textView = (TextView) mActivity.findViewById(R.id.textview_text); 662 663 mActivityRule.runOnUiThread(() -> { 664 textView.setTextIsSelectable(true); 665 textView.setText("abcd", BufferType.EDITABLE); 666 textView.setHighlightColor(Color.BLUE); 667 }); 668 mInstrumentation.waitForIdleSync(); 669 670 assertTrue(textView.isTextSelectable()); 671 assertEquals(Color.BLUE, textView.getHighlightColor()); 672 673 // Long click on the text selects all text and shows selection handlers. The view has an 674 // attribute layout_width="wrap_content", so clicked location (the center of the view) 675 // should be on the text. 676 CtsTouchUtils.emulateLongPressOnViewCenter(mInstrumentation, textView); 677 678 // At this point the entire content of our TextView should be selected and highlighted 679 // with blue. Now change the highlight to red while the selection is still on. 680 mActivityRule.runOnUiThread(() -> textView.setHighlightColor(Color.RED)); 681 mInstrumentation.waitForIdleSync(); 682 683 assertEquals(Color.RED, textView.getHighlightColor()); 684 assertTrue(TextUtils.equals("abcd", textView.getText())); 685 686 // Remove the selection 687 mActivityRule.runOnUiThread(() -> Selection.removeSelection((Spannable) textView.getText())); 688 mInstrumentation.waitForIdleSync(); 689 690 // And switch highlight to green after the selection has been removed 691 mActivityRule.runOnUiThread(() -> textView.setHighlightColor(Color.GREEN)); 692 mInstrumentation.waitForIdleSync(); 693 694 assertEquals(Color.GREEN, textView.getHighlightColor()); 695 assertTrue(TextUtils.equals("abcd", textView.getText())); 696 } 697 698 @UiThreadTest 699 @Test 700 public void testSetShadowLayer() { 701 // test values 702 final MockTextView mockTextView = new MockTextView(mActivity); 703 704 mockTextView.setShadowLayer(1.0f, 0.3f, 0.4f, Color.CYAN); 705 assertEquals(Color.CYAN, mockTextView.getShadowColor()); 706 assertEquals(0.3f, mockTextView.getShadowDx(), 0.0f); 707 assertEquals(0.4f, mockTextView.getShadowDy(), 0.0f); 708 assertEquals(1.0f, mockTextView.getShadowRadius(), 0.0f); 709 710 // shadow is placed to the left and below the text 711 mockTextView.setShadowLayer(1.0f, 0.3f, 0.3f, Color.CYAN); 712 assertTrue(mockTextView.isPaddingOffsetRequired()); 713 assertEquals(0, mockTextView.getLeftPaddingOffset()); 714 assertEquals(0, mockTextView.getTopPaddingOffset()); 715 assertEquals(1, mockTextView.getRightPaddingOffset()); 716 assertEquals(1, mockTextView.getBottomPaddingOffset()); 717 718 // shadow is placed to the right and above the text 719 mockTextView.setShadowLayer(1.0f, -0.8f, -0.8f, Color.CYAN); 720 assertTrue(mockTextView.isPaddingOffsetRequired()); 721 assertEquals(-1, mockTextView.getLeftPaddingOffset()); 722 assertEquals(-1, mockTextView.getTopPaddingOffset()); 723 assertEquals(0, mockTextView.getRightPaddingOffset()); 724 assertEquals(0, mockTextView.getBottomPaddingOffset()); 725 726 // no shadow 727 mockTextView.setShadowLayer(0.0f, 0.0f, 0.0f, Color.CYAN); 728 assertFalse(mockTextView.isPaddingOffsetRequired()); 729 assertEquals(0, mockTextView.getLeftPaddingOffset()); 730 assertEquals(0, mockTextView.getTopPaddingOffset()); 731 assertEquals(0, mockTextView.getRightPaddingOffset()); 732 assertEquals(0, mockTextView.getBottomPaddingOffset()); 733 } 734 735 @UiThreadTest 736 @Test 737 public void testSetSelectAllOnFocus() { 738 mActivity.setContentView(R.layout.textview_selectallonfocus); 739 String content = "This is the content"; 740 String blank = ""; 741 mTextView = findTextView(R.id.selectAllOnFocus_default); 742 mTextView.setText(blank, BufferType.SPANNABLE); 743 // change the focus 744 findTextView(R.id.selectAllOnFocus_dummy).requestFocus(); 745 assertFalse(mTextView.isFocused()); 746 mTextView.requestFocus(); 747 assertTrue(mTextView.isFocused()); 748 749 assertEquals(-1, mTextView.getSelectionStart()); 750 assertEquals(-1, mTextView.getSelectionEnd()); 751 752 mTextView.setText(content, BufferType.SPANNABLE); 753 mTextView.setSelectAllOnFocus(true); 754 // change the focus 755 findTextView(R.id.selectAllOnFocus_dummy).requestFocus(); 756 assertFalse(mTextView.isFocused()); 757 mTextView.requestFocus(); 758 assertTrue(mTextView.isFocused()); 759 760 assertEquals(0, mTextView.getSelectionStart()); 761 assertEquals(content.length(), mTextView.getSelectionEnd()); 762 763 Selection.setSelection((Spannable) mTextView.getText(), 0); 764 mTextView.setSelectAllOnFocus(false); 765 // change the focus 766 findTextView(R.id.selectAllOnFocus_dummy).requestFocus(); 767 assertFalse(mTextView.isFocused()); 768 mTextView.requestFocus(); 769 assertTrue(mTextView.isFocused()); 770 771 assertEquals(0, mTextView.getSelectionStart()); 772 assertEquals(0, mTextView.getSelectionEnd()); 773 774 mTextView.setText(blank, BufferType.SPANNABLE); 775 mTextView.setSelectAllOnFocus(true); 776 // change the focus 777 findTextView(R.id.selectAllOnFocus_dummy).requestFocus(); 778 assertFalse(mTextView.isFocused()); 779 mTextView.requestFocus(); 780 assertTrue(mTextView.isFocused()); 781 782 assertEquals(0, mTextView.getSelectionStart()); 783 assertEquals(blank.length(), mTextView.getSelectionEnd()); 784 785 Selection.setSelection((Spannable) mTextView.getText(), 0); 786 mTextView.setSelectAllOnFocus(false); 787 // change the focus 788 findTextView(R.id.selectAllOnFocus_dummy).requestFocus(); 789 assertFalse(mTextView.isFocused()); 790 mTextView.requestFocus(); 791 assertTrue(mTextView.isFocused()); 792 793 assertEquals(0, mTextView.getSelectionStart()); 794 assertEquals(0, mTextView.getSelectionEnd()); 795 } 796 797 @UiThreadTest 798 @Test 799 public void testGetPaint() { 800 mTextView = new TextView(mActivity); 801 TextPaint tp = mTextView.getPaint(); 802 assertNotNull(tp); 803 804 assertEquals(mTextView.getPaintFlags(), tp.getFlags()); 805 } 806 807 @UiThreadTest 808 @Test 809 public void testAccessLinksClickable() { 810 mActivity.setContentView(R.layout.textview_hint_linksclickable_freezestext); 811 812 mTextView = findTextView(R.id.hint_linksClickable_freezesText_default); 813 assertTrue(mTextView.getLinksClickable()); 814 815 mTextView = findTextView(R.id.linksClickable_true); 816 assertTrue(mTextView.getLinksClickable()); 817 818 mTextView = findTextView(R.id.linksClickable_false); 819 assertFalse(mTextView.getLinksClickable()); 820 821 mTextView.setLinksClickable(false); 822 assertFalse(mTextView.getLinksClickable()); 823 824 mTextView.setLinksClickable(true); 825 assertTrue(mTextView.getLinksClickable()); 826 827 assertNull(mTextView.getMovementMethod()); 828 829 final CharSequence text = new SpannableString("name: Jack. tel: +41 44 800 8999"); 830 831 mTextView.setAutoLinkMask(Linkify.PHONE_NUMBERS); 832 mTextView.setText(text, BufferType.EDITABLE); 833 834 // Movement method will be automatically set to LinkMovementMethod 835 assertTrue(mTextView.getMovementMethod() instanceof LinkMovementMethod); 836 } 837 838 @UiThreadTest 839 @Test 840 public void testAccessHintTextColor() { 841 mTextView = new TextView(mActivity); 842 // using int values 843 // normal 844 mTextView.setHintTextColor(Color.GREEN); 845 assertEquals(Color.GREEN, mTextView.getCurrentHintTextColor()); 846 assertSame(ColorStateList.valueOf(Color.GREEN), mTextView.getHintTextColors()); 847 848 mTextView.setHintTextColor(Color.BLUE); 849 assertSame(ColorStateList.valueOf(Color.BLUE), mTextView.getHintTextColors()); 850 assertEquals(Color.BLUE, mTextView.getCurrentHintTextColor()); 851 852 mTextView.setHintTextColor(Color.RED); 853 assertSame(ColorStateList.valueOf(Color.RED), mTextView.getHintTextColors()); 854 assertEquals(Color.RED, mTextView.getCurrentHintTextColor()); 855 856 // using ColorStateList 857 // normal 858 ColorStateList colors = new ColorStateList(new int[][] { 859 new int[] { android.R.attr.state_focused}, new int[0] }, 860 new int[] { Color.rgb(0, 255, 0), Color.BLACK }); 861 mTextView.setHintTextColor(colors); 862 assertSame(colors, mTextView.getHintTextColors()); 863 assertEquals(Color.BLACK, mTextView.getCurrentHintTextColor()); 864 865 // exceptional 866 mTextView.setHintTextColor(null); 867 assertNull(mTextView.getHintTextColors()); 868 assertEquals(mTextView.getCurrentTextColor(), mTextView.getCurrentHintTextColor()); 869 } 870 871 @UiThreadTest 872 @Test 873 public void testAccessLinkTextColor() { 874 mTextView = new TextView(mActivity); 875 // normal 876 mTextView.setLinkTextColor(Color.GRAY); 877 assertSame(ColorStateList.valueOf(Color.GRAY), mTextView.getLinkTextColors()); 878 assertEquals(Color.GRAY, mTextView.getPaint().linkColor); 879 880 mTextView.setLinkTextColor(Color.YELLOW); 881 assertSame(ColorStateList.valueOf(Color.YELLOW), mTextView.getLinkTextColors()); 882 assertEquals(Color.YELLOW, mTextView.getPaint().linkColor); 883 884 mTextView.setLinkTextColor(Color.WHITE); 885 assertSame(ColorStateList.valueOf(Color.WHITE), mTextView.getLinkTextColors()); 886 assertEquals(Color.WHITE, mTextView.getPaint().linkColor); 887 888 ColorStateList colors = new ColorStateList(new int[][] { 889 new int[] { android.R.attr.state_expanded}, new int[0] }, 890 new int[] { Color.rgb(0, 255, 0), Color.BLACK }); 891 mTextView.setLinkTextColor(colors); 892 assertSame(colors, mTextView.getLinkTextColors()); 893 assertEquals(Color.BLACK, mTextView.getPaint().linkColor); 894 895 mTextView.setLinkTextColor(null); 896 assertNull(mTextView.getLinkTextColors()); 897 assertEquals(Color.BLACK, mTextView.getPaint().linkColor); 898 } 899 900 @UiThreadTest 901 @Test 902 public void testAccessPaintFlags() { 903 mTextView = new TextView(mActivity); 904 assertEquals(Paint.DEV_KERN_TEXT_FLAG | Paint.EMBEDDED_BITMAP_TEXT_FLAG 905 | Paint.ANTI_ALIAS_FLAG, mTextView.getPaintFlags()); 906 907 mTextView.setPaintFlags(Paint.UNDERLINE_TEXT_FLAG | Paint.FAKE_BOLD_TEXT_FLAG); 908 assertEquals(Paint.UNDERLINE_TEXT_FLAG | Paint.FAKE_BOLD_TEXT_FLAG, 909 mTextView.getPaintFlags()); 910 911 mTextView.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG | Paint.LINEAR_TEXT_FLAG); 912 assertEquals(Paint.STRIKE_THRU_TEXT_FLAG | Paint.LINEAR_TEXT_FLAG, 913 mTextView.getPaintFlags()); 914 } 915 916 @Test 917 public void testHeight() throws Throwable { 918 mTextView = findTextView(R.id.textview_text); 919 final int originalHeight = mTextView.getHeight(); 920 921 // test setMaxHeight 922 int newHeight = originalHeight + 1; 923 setMaxHeight(newHeight); 924 assertEquals(originalHeight, mTextView.getHeight()); 925 assertEquals(newHeight, mTextView.getMaxHeight()); 926 927 newHeight = originalHeight - 1; 928 setMaxHeight(newHeight); 929 assertEquals(newHeight, mTextView.getHeight()); 930 assertEquals(newHeight, mTextView.getMaxHeight()); 931 932 newHeight = -1; 933 setMaxHeight(newHeight); 934 assertEquals(0, mTextView.getHeight()); 935 assertEquals(newHeight, mTextView.getMaxHeight()); 936 937 newHeight = Integer.MAX_VALUE; 938 setMaxHeight(newHeight); 939 assertEquals(originalHeight, mTextView.getHeight()); 940 assertEquals(newHeight, mTextView.getMaxHeight()); 941 942 // test setMinHeight 943 newHeight = originalHeight + 1; 944 setMinHeight(newHeight); 945 assertEquals(newHeight, mTextView.getHeight()); 946 assertEquals(newHeight, mTextView.getMinHeight()); 947 948 newHeight = originalHeight - 1; 949 setMinHeight(newHeight); 950 assertEquals(originalHeight, mTextView.getHeight()); 951 assertEquals(newHeight, mTextView.getMinHeight()); 952 953 newHeight = -1; 954 setMinHeight(newHeight); 955 assertEquals(originalHeight, mTextView.getHeight()); 956 assertEquals(newHeight, mTextView.getMinHeight()); 957 958 // reset min and max height 959 setMinHeight(0); 960 setMaxHeight(Integer.MAX_VALUE); 961 962 // test setHeight 963 newHeight = originalHeight + 1; 964 setHeight(newHeight); 965 assertEquals(newHeight, mTextView.getHeight()); 966 assertEquals(newHeight, mTextView.getMaxHeight()); 967 assertEquals(newHeight, mTextView.getMinHeight()); 968 969 newHeight = originalHeight - 1; 970 setHeight(newHeight); 971 assertEquals(newHeight, mTextView.getHeight()); 972 assertEquals(newHeight, mTextView.getMaxHeight()); 973 assertEquals(newHeight, mTextView.getMinHeight()); 974 975 newHeight = -1; 976 setHeight(newHeight); 977 assertEquals(0, mTextView.getHeight()); 978 assertEquals(newHeight, mTextView.getMaxHeight()); 979 assertEquals(newHeight, mTextView.getMinHeight()); 980 981 setHeight(originalHeight); 982 assertEquals(originalHeight, mTextView.getHeight()); 983 assertEquals(originalHeight, mTextView.getMaxHeight()); 984 assertEquals(originalHeight, mTextView.getMinHeight()); 985 986 // setting max/min lines should cause getMaxHeight/getMinHeight to return -1 987 setMaxLines(2); 988 assertEquals("Setting maxLines should return -1 fir maxHeight", 989 -1, mTextView.getMaxHeight()); 990 991 setMinLines(1); 992 assertEquals("Setting minLines should return -1 for minHeight", 993 -1, mTextView.getMinHeight()); 994 } 995 996 @Test 997 public void testSetMaxLines_toZero_shouldNotDisplayAnyLines() throws Throwable { 998 mTextView = findTextView(R.id.textview_text); 999 mActivityRule.runOnUiThread(() -> { 1000 mTextView.setPadding(0, 0, 0, 0); 1001 mTextView.setText("Single"); 1002 mTextView.setMaxLines(0); 1003 }); 1004 mInstrumentation.waitForIdleSync(); 1005 1006 final int expectedHeight = mTextView.getTotalPaddingBottom() 1007 + mTextView.getTotalPaddingTop(); 1008 1009 assertEquals(expectedHeight, mTextView.getHeight()); 1010 1011 mActivityRule.runOnUiThread(() -> mTextView.setText("Two\nLines")); 1012 mInstrumentation.waitForIdleSync(); 1013 assertEquals(expectedHeight, mTextView.getHeight()); 1014 1015 mActivityRule.runOnUiThread(() -> mTextView.setTextIsSelectable(true)); 1016 mInstrumentation.waitForIdleSync(); 1017 assertEquals(expectedHeight, mTextView.getHeight()); 1018 } 1019 1020 @Test 1021 public void testWidth() throws Throwable { 1022 mTextView = findTextView(R.id.textview_text); 1023 int originalWidth = mTextView.getWidth(); 1024 1025 int newWidth = mTextView.getWidth() / 8; 1026 setWidth(newWidth); 1027 assertEquals(newWidth, mTextView.getWidth()); 1028 assertEquals(newWidth, mTextView.getMaxWidth()); 1029 assertEquals(newWidth, mTextView.getMinWidth()); 1030 1031 // Min Width 1032 newWidth = originalWidth + 1; 1033 setMinWidth(newWidth); 1034 assertEquals(1, mTextView.getLineCount()); 1035 assertEquals(newWidth, mTextView.getWidth()); 1036 assertEquals(newWidth, mTextView.getMinWidth()); 1037 1038 newWidth = originalWidth - 1; 1039 setMinWidth(originalWidth - 1); 1040 assertEquals(2, mTextView.getLineCount()); 1041 assertEquals(newWidth, mTextView.getWidth()); 1042 assertEquals(newWidth, mTextView.getMinWidth()); 1043 1044 // Width 1045 newWidth = originalWidth + 1; 1046 setWidth(newWidth); 1047 assertEquals(1, mTextView.getLineCount()); 1048 assertEquals(newWidth, mTextView.getWidth()); 1049 assertEquals(newWidth, mTextView.getMaxWidth()); 1050 assertEquals(newWidth, mTextView.getMinWidth()); 1051 1052 newWidth = originalWidth - 1; 1053 setWidth(newWidth); 1054 assertEquals(2, mTextView.getLineCount()); 1055 assertEquals(newWidth, mTextView.getWidth()); 1056 assertEquals(newWidth, mTextView.getMaxWidth()); 1057 assertEquals(newWidth, mTextView.getMinWidth()); 1058 1059 // setting ems should cause getMaxWidth/getMinWidth to return -1 1060 setEms(1); 1061 assertEquals("Setting ems should return -1 for maxWidth", -1, mTextView.getMaxWidth()); 1062 assertEquals("Setting ems should return -1 for maxWidth", -1, mTextView.getMinWidth()); 1063 } 1064 1065 @Test 1066 public void testSetMinEms() throws Throwable { 1067 mTextView = findTextView(R.id.textview_text); 1068 assertEquals(1, mTextView.getLineCount()); 1069 1070 final int originalWidth = mTextView.getWidth(); 1071 final int originalEms = originalWidth / mTextView.getLineHeight(); 1072 1073 setMinEms(originalEms + 1); 1074 assertEquals((originalEms + 1) * mTextView.getLineHeight(), mTextView.getWidth()); 1075 assertEquals(-1, mTextView.getMinWidth()); 1076 assertEquals(originalEms + 1, mTextView.getMinEms()); 1077 1078 setMinEms(originalEms - 1); 1079 assertEquals(originalWidth, mTextView.getWidth()); 1080 assertEquals(-1, mTextView.getMinWidth()); 1081 assertEquals(originalEms - 1, mTextView.getMinEms()); 1082 1083 setMinWidth(1); 1084 assertEquals(-1, mTextView.getMinEms()); 1085 } 1086 1087 @Test 1088 public void testSetMaxEms() throws Throwable { 1089 mTextView = findTextView(R.id.textview_text); 1090 assertEquals(1, mTextView.getLineCount()); 1091 1092 final int originalWidth = mTextView.getWidth(); 1093 final int originalEms = originalWidth / mTextView.getLineHeight(); 1094 1095 setMaxEms(originalEms + 1); 1096 assertEquals(1, mTextView.getLineCount()); 1097 assertEquals(originalWidth, mTextView.getWidth()); 1098 assertEquals(-1, mTextView.getMaxWidth()); 1099 assertEquals(originalEms + 1, mTextView.getMaxEms()); 1100 1101 setMaxEms(originalEms - 1); 1102 assertTrue(1 < mTextView.getLineCount()); 1103 assertEquals((originalEms - 1) * mTextView.getLineHeight(), mTextView.getWidth()); 1104 assertEquals(-1, mTextView.getMaxWidth()); 1105 assertEquals(originalEms - 1, mTextView.getMaxEms()); 1106 1107 setMaxWidth(originalWidth); 1108 assertEquals(-1, mTextView.getMaxEms()); 1109 } 1110 1111 @Test 1112 public void testSetEms() throws Throwable { 1113 mTextView = findTextView(R.id.textview_text); 1114 assertEquals("check height", 1, mTextView.getLineCount()); 1115 final int originalWidth = mTextView.getWidth(); 1116 final int originalEms = originalWidth / mTextView.getLineHeight(); 1117 1118 setEms(originalEms + 1); 1119 assertEquals(1, mTextView.getLineCount()); 1120 assertEquals((originalEms + 1) * mTextView.getLineHeight(), mTextView.getWidth()); 1121 assertEquals(-1, mTextView.getMinWidth()); 1122 assertEquals(-1, mTextView.getMaxWidth()); 1123 assertEquals(originalEms + 1, mTextView.getMinEms()); 1124 assertEquals(originalEms + 1, mTextView.getMaxEms()); 1125 1126 setEms(originalEms - 1); 1127 assertTrue((1 < mTextView.getLineCount())); 1128 assertEquals((originalEms - 1) * mTextView.getLineHeight(), mTextView.getWidth()); 1129 assertEquals(-1, mTextView.getMinWidth()); 1130 assertEquals(-1, mTextView.getMaxWidth()); 1131 assertEquals(originalEms - 1, mTextView.getMinEms()); 1132 assertEquals(originalEms - 1, mTextView.getMaxEms()); 1133 } 1134 1135 @Test 1136 public void testSetLineSpacing() throws Throwable { 1137 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 1138 mInstrumentation.waitForIdleSync(); 1139 int originalLineHeight = mTextView.getLineHeight(); 1140 1141 // normal 1142 float add = 1.2f; 1143 float mult = 1.4f; 1144 setLineSpacing(add, mult); 1145 assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight()); 1146 add = 0.0f; 1147 mult = 1.4f; 1148 setLineSpacing(add, mult); 1149 assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight()); 1150 1151 // abnormal 1152 add = -1.2f; 1153 mult = 1.4f; 1154 setLineSpacing(add, mult); 1155 assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight()); 1156 add = -1.2f; 1157 mult = -1.4f; 1158 setLineSpacing(add, mult); 1159 assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight()); 1160 add = 1.2f; 1161 mult = 0.0f; 1162 setLineSpacing(add, mult); 1163 assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight()); 1164 1165 // edge 1166 add = Float.MIN_VALUE; 1167 mult = Float.MIN_VALUE; 1168 setLineSpacing(add, mult); 1169 assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight()); 1170 1171 // edge case where the behavior of Math.round() deviates from 1172 // FastMath.round(), requiring us to use an explicit 0 value 1173 add = Float.MAX_VALUE; 1174 mult = Float.MAX_VALUE; 1175 setLineSpacing(add, mult); 1176 assertEquals(0, mTextView.getLineHeight()); 1177 } 1178 1179 @Test 1180 public void testSetElegantLineHeight() throws Throwable { 1181 mTextView = findTextView(R.id.textview_text); 1182 assertFalse(mTextView.getPaint().isElegantTextHeight()); 1183 mActivityRule.runOnUiThread(() -> { 1184 mTextView.setWidth(mTextView.getWidth() / 3); 1185 mTextView.setPadding(1, 2, 3, 4); 1186 mTextView.setGravity(Gravity.BOTTOM); 1187 }); 1188 mInstrumentation.waitForIdleSync(); 1189 1190 int oldHeight = mTextView.getHeight(); 1191 mActivityRule.runOnUiThread(() -> mTextView.setElegantTextHeight(true)); 1192 mInstrumentation.waitForIdleSync(); 1193 1194 assertTrue(mTextView.getPaint().isElegantTextHeight()); 1195 assertTrue(mTextView.getHeight() > oldHeight); 1196 1197 mActivityRule.runOnUiThread(() -> mTextView.setElegantTextHeight(false)); 1198 mInstrumentation.waitForIdleSync(); 1199 assertFalse(mTextView.getPaint().isElegantTextHeight()); 1200 assertTrue(mTextView.getHeight() == oldHeight); 1201 } 1202 1203 @Test 1204 public void testAccessFreezesText() throws Throwable { 1205 layout(R.layout.textview_hint_linksclickable_freezestext); 1206 1207 mTextView = findTextView(R.id.hint_linksClickable_freezesText_default); 1208 assertFalse(mTextView.getFreezesText()); 1209 1210 mTextView = findTextView(R.id.freezesText_true); 1211 assertTrue(mTextView.getFreezesText()); 1212 1213 mTextView = findTextView(R.id.freezesText_false); 1214 assertFalse(mTextView.getFreezesText()); 1215 1216 mTextView.setFreezesText(false); 1217 assertFalse(mTextView.getFreezesText()); 1218 1219 final CharSequence text = "Hello, TextView."; 1220 mActivityRule.runOnUiThread(() -> mTextView.setText(text)); 1221 mInstrumentation.waitForIdleSync(); 1222 1223 final URLSpan urlSpan = new URLSpan("ctstest://TextView/test"); 1224 // TODO: How to simulate the TextView in frozen icicles. 1225 ActivityMonitor am = mInstrumentation.addMonitor(MockURLSpanTestActivity.class.getName(), 1226 null, false); 1227 1228 mActivityRule.runOnUiThread(() -> { 1229 Uri uri = Uri.parse(urlSpan.getURL()); 1230 Intent intent = new Intent(Intent.ACTION_VIEW, uri); 1231 mActivity.startActivity(intent); 1232 }); 1233 1234 Activity newActivity = am.waitForActivityWithTimeout(TIMEOUT); 1235 assertNotNull(newActivity); 1236 newActivity.finish(); 1237 mInstrumentation.removeMonitor(am); 1238 // the text of TextView is removed. 1239 mTextView = findTextView(R.id.freezesText_false); 1240 1241 assertEquals(text.toString(), mTextView.getText().toString()); 1242 1243 mTextView.setFreezesText(true); 1244 assertTrue(mTextView.getFreezesText()); 1245 1246 mActivityRule.runOnUiThread(() -> mTextView.setText(text)); 1247 mInstrumentation.waitForIdleSync(); 1248 // TODO: How to simulate the TextView in frozen icicles. 1249 am = mInstrumentation.addMonitor(MockURLSpanTestActivity.class.getName(), 1250 null, false); 1251 1252 mActivityRule.runOnUiThread(() -> { 1253 Uri uri = Uri.parse(urlSpan.getURL()); 1254 Intent intent = new Intent(Intent.ACTION_VIEW, uri); 1255 mActivity.startActivity(intent); 1256 }); 1257 1258 Activity oldActivity = newActivity; 1259 while (true) { 1260 newActivity = am.waitForActivityWithTimeout(TIMEOUT); 1261 assertNotNull(newActivity); 1262 if (newActivity != oldActivity) { 1263 break; 1264 } 1265 } 1266 newActivity.finish(); 1267 mInstrumentation.removeMonitor(am); 1268 // the text of TextView is still there. 1269 mTextView = findTextView(R.id.freezesText_false); 1270 assertEquals(text.toString(), mTextView.getText().toString()); 1271 } 1272 1273 @UiThreadTest 1274 @Test 1275 public void testSetEditableFactory() { 1276 mTextView = new TextView(mActivity); 1277 String text = "sample"; 1278 1279 final Editable.Factory mockEditableFactory = spy(new Editable.Factory()); 1280 doCallRealMethod().when(mockEditableFactory).newEditable(any(CharSequence.class)); 1281 mTextView.setEditableFactory(mockEditableFactory); 1282 1283 mTextView.setText(text); 1284 verify(mockEditableFactory, never()).newEditable(any(CharSequence.class)); 1285 1286 reset(mockEditableFactory); 1287 mTextView.setText(text, BufferType.SPANNABLE); 1288 verify(mockEditableFactory, never()).newEditable(any(CharSequence.class)); 1289 1290 reset(mockEditableFactory); 1291 mTextView.setText(text, BufferType.NORMAL); 1292 verify(mockEditableFactory, never()).newEditable(any(CharSequence.class)); 1293 1294 reset(mockEditableFactory); 1295 mTextView.setText(text, BufferType.EDITABLE); 1296 verify(mockEditableFactory, times(1)).newEditable(text); 1297 1298 mTextView.setKeyListener(DigitsKeyListener.getInstance()); 1299 reset(mockEditableFactory); 1300 mTextView.setText(text, BufferType.EDITABLE); 1301 verify(mockEditableFactory, times(1)).newEditable(text); 1302 1303 try { 1304 mTextView.setEditableFactory(null); 1305 fail("The factory can not set to null!"); 1306 } catch (NullPointerException e) { 1307 } 1308 } 1309 1310 @UiThreadTest 1311 @Test 1312 public void testSetSpannableFactory() { 1313 mTextView = new TextView(mActivity); 1314 String text = "sample"; 1315 1316 final Spannable.Factory mockSpannableFactory = spy(new Spannable.Factory()); 1317 doCallRealMethod().when(mockSpannableFactory).newSpannable(any(CharSequence.class)); 1318 mTextView.setSpannableFactory(mockSpannableFactory); 1319 1320 mTextView.setText(text); 1321 verify(mockSpannableFactory, never()).newSpannable(any(CharSequence.class)); 1322 1323 reset(mockSpannableFactory); 1324 mTextView.setText(text, BufferType.EDITABLE); 1325 verify(mockSpannableFactory, never()).newSpannable(any(CharSequence.class)); 1326 1327 reset(mockSpannableFactory); 1328 mTextView.setText(text, BufferType.NORMAL); 1329 verify(mockSpannableFactory, never()).newSpannable(any(CharSequence.class)); 1330 1331 reset(mockSpannableFactory); 1332 mTextView.setText(text, BufferType.SPANNABLE); 1333 verify(mockSpannableFactory, times(1)).newSpannable(text); 1334 1335 mTextView.setMovementMethod(LinkMovementMethod.getInstance()); 1336 reset(mockSpannableFactory); 1337 mTextView.setText(text, BufferType.NORMAL); 1338 verify(mockSpannableFactory, times(1)).newSpannable(text); 1339 1340 try { 1341 mTextView.setSpannableFactory(null); 1342 fail("The factory can not set to null!"); 1343 } catch (NullPointerException e) { 1344 } 1345 } 1346 1347 @UiThreadTest 1348 @Test 1349 public void testTextChangedListener() { 1350 mTextView = new TextView(mActivity); 1351 MockTextWatcher watcher0 = new MockTextWatcher(); 1352 MockTextWatcher watcher1 = new MockTextWatcher(); 1353 1354 mTextView.addTextChangedListener(watcher0); 1355 mTextView.addTextChangedListener(watcher1); 1356 1357 watcher0.reset(); 1358 watcher1.reset(); 1359 mTextView.setText("Changed"); 1360 assertTrue(watcher0.hasCalledBeforeTextChanged()); 1361 assertTrue(watcher0.hasCalledOnTextChanged()); 1362 assertTrue(watcher0.hasCalledAfterTextChanged()); 1363 assertTrue(watcher1.hasCalledBeforeTextChanged()); 1364 assertTrue(watcher1.hasCalledOnTextChanged()); 1365 assertTrue(watcher1.hasCalledAfterTextChanged()); 1366 1367 watcher0.reset(); 1368 watcher1.reset(); 1369 // BeforeTextChanged and OnTextChanged are called though the strings are same 1370 mTextView.setText("Changed"); 1371 assertTrue(watcher0.hasCalledBeforeTextChanged()); 1372 assertTrue(watcher0.hasCalledOnTextChanged()); 1373 assertTrue(watcher0.hasCalledAfterTextChanged()); 1374 assertTrue(watcher1.hasCalledBeforeTextChanged()); 1375 assertTrue(watcher1.hasCalledOnTextChanged()); 1376 assertTrue(watcher1.hasCalledAfterTextChanged()); 1377 1378 watcher0.reset(); 1379 watcher1.reset(); 1380 // BeforeTextChanged and OnTextChanged are called twice (The text is not 1381 // Editable, so in Append() it calls setText() first) 1382 mTextView.append("and appended"); 1383 assertTrue(watcher0.hasCalledBeforeTextChanged()); 1384 assertTrue(watcher0.hasCalledOnTextChanged()); 1385 assertTrue(watcher0.hasCalledAfterTextChanged()); 1386 assertTrue(watcher1.hasCalledBeforeTextChanged()); 1387 assertTrue(watcher1.hasCalledOnTextChanged()); 1388 assertTrue(watcher1.hasCalledAfterTextChanged()); 1389 1390 watcher0.reset(); 1391 watcher1.reset(); 1392 // Methods are not called if the string does not change 1393 mTextView.append(""); 1394 assertFalse(watcher0.hasCalledBeforeTextChanged()); 1395 assertFalse(watcher0.hasCalledOnTextChanged()); 1396 assertFalse(watcher0.hasCalledAfterTextChanged()); 1397 assertFalse(watcher1.hasCalledBeforeTextChanged()); 1398 assertFalse(watcher1.hasCalledOnTextChanged()); 1399 assertFalse(watcher1.hasCalledAfterTextChanged()); 1400 1401 watcher0.reset(); 1402 watcher1.reset(); 1403 mTextView.removeTextChangedListener(watcher1); 1404 mTextView.setText(null); 1405 assertTrue(watcher0.hasCalledBeforeTextChanged()); 1406 assertTrue(watcher0.hasCalledOnTextChanged()); 1407 assertTrue(watcher0.hasCalledAfterTextChanged()); 1408 assertFalse(watcher1.hasCalledBeforeTextChanged()); 1409 assertFalse(watcher1.hasCalledOnTextChanged()); 1410 assertFalse(watcher1.hasCalledAfterTextChanged()); 1411 } 1412 1413 @UiThreadTest 1414 @Test 1415 public void testSetTextKeepState1() { 1416 mTextView = new TextView(mActivity); 1417 1418 String longString = "very long content"; 1419 String shortString = "short"; 1420 1421 // selection is at the exact place which is inside the short string 1422 mTextView.setText(longString, BufferType.SPANNABLE); 1423 Selection.setSelection((Spannable) mTextView.getText(), 3); 1424 mTextView.setTextKeepState(shortString); 1425 assertEquals(shortString, mTextView.getText().toString()); 1426 assertEquals(3, mTextView.getSelectionStart()); 1427 assertEquals(3, mTextView.getSelectionEnd()); 1428 1429 // selection is at the exact place which is outside the short string 1430 mTextView.setText(longString); 1431 Selection.setSelection((Spannable) mTextView.getText(), shortString.length() + 1); 1432 mTextView.setTextKeepState(shortString); 1433 assertEquals(shortString, mTextView.getText().toString()); 1434 assertEquals(shortString.length(), mTextView.getSelectionStart()); 1435 assertEquals(shortString.length(), mTextView.getSelectionEnd()); 1436 1437 // select the sub string which is inside the short string 1438 mTextView.setText(longString); 1439 Selection.setSelection((Spannable) mTextView.getText(), 1, 4); 1440 mTextView.setTextKeepState(shortString); 1441 assertEquals(shortString, mTextView.getText().toString()); 1442 assertEquals(1, mTextView.getSelectionStart()); 1443 assertEquals(4, mTextView.getSelectionEnd()); 1444 1445 // select the sub string which ends outside the short string 1446 mTextView.setText(longString); 1447 Selection.setSelection((Spannable) mTextView.getText(), 2, shortString.length() + 1); 1448 mTextView.setTextKeepState(shortString); 1449 assertEquals(shortString, mTextView.getText().toString()); 1450 assertEquals(2, mTextView.getSelectionStart()); 1451 assertEquals(shortString.length(), mTextView.getSelectionEnd()); 1452 1453 // select the sub string which is outside the short string 1454 mTextView.setText(longString); 1455 Selection.setSelection((Spannable) mTextView.getText(), 1456 shortString.length() + 1, shortString.length() + 3); 1457 mTextView.setTextKeepState(shortString); 1458 assertEquals(shortString, mTextView.getText().toString()); 1459 assertEquals(shortString.length(), mTextView.getSelectionStart()); 1460 assertEquals(shortString.length(), mTextView.getSelectionEnd()); 1461 } 1462 1463 @UiThreadTest 1464 @Test 1465 public void testGetEditableText() { 1466 TextView tv = findTextView(R.id.textview_text); 1467 1468 String text = "Hello"; 1469 tv.setText(text, BufferType.EDITABLE); 1470 assertEquals(text, tv.getText().toString()); 1471 assertTrue(tv.getText() instanceof Editable); 1472 assertEquals(text, tv.getEditableText().toString()); 1473 1474 tv.setText(text, BufferType.SPANNABLE); 1475 assertEquals(text, tv.getText().toString()); 1476 assertTrue(tv.getText() instanceof Spannable); 1477 assertNull(tv.getEditableText()); 1478 1479 tv.setText(null, BufferType.EDITABLE); 1480 assertEquals("", tv.getText().toString()); 1481 assertTrue(tv.getText() instanceof Editable); 1482 assertEquals("", tv.getEditableText().toString()); 1483 1484 tv.setText(null, BufferType.SPANNABLE); 1485 assertEquals("", tv.getText().toString()); 1486 assertTrue(tv.getText() instanceof Spannable); 1487 assertNull(tv.getEditableText()); 1488 } 1489 1490 @UiThreadTest 1491 @Test 1492 public void testSetText2() { 1493 String string = "This is a test for setting text content by char array"; 1494 char[] input = string.toCharArray(); 1495 TextView tv = findTextView(R.id.textview_text); 1496 1497 tv.setText(input, 0, input.length); 1498 assertEquals(string, tv.getText().toString()); 1499 1500 tv.setText(input, 0, 5); 1501 assertEquals(string.substring(0, 5), tv.getText().toString()); 1502 1503 try { 1504 tv.setText(input, -1, input.length); 1505 fail("Should throw exception if the start position is negative!"); 1506 } catch (IndexOutOfBoundsException exception) { 1507 } 1508 1509 try { 1510 tv.setText(input, 0, -1); 1511 fail("Should throw exception if the length is negative!"); 1512 } catch (IndexOutOfBoundsException exception) { 1513 } 1514 1515 try { 1516 tv.setText(input, 1, input.length); 1517 fail("Should throw exception if the end position is out of index!"); 1518 } catch (IndexOutOfBoundsException exception) { 1519 } 1520 1521 tv.setText(input, 1, 0); 1522 assertEquals("", tv.getText().toString()); 1523 } 1524 1525 @UiThreadTest 1526 @Test 1527 public void testSetText1() { 1528 mTextView = findTextView(R.id.textview_text); 1529 1530 String longString = "very long content"; 1531 String shortString = "short"; 1532 1533 // selection is at the exact place which is inside the short string 1534 mTextView.setText(longString, BufferType.SPANNABLE); 1535 Selection.setSelection((Spannable) mTextView.getText(), 3); 1536 mTextView.setTextKeepState(shortString, BufferType.EDITABLE); 1537 assertTrue(mTextView.getText() instanceof Editable); 1538 assertEquals(shortString, mTextView.getText().toString()); 1539 assertEquals(shortString, mTextView.getEditableText().toString()); 1540 assertEquals(3, mTextView.getSelectionStart()); 1541 assertEquals(3, mTextView.getSelectionEnd()); 1542 1543 mTextView.setText(shortString, BufferType.EDITABLE); 1544 assertTrue(mTextView.getText() instanceof Editable); 1545 assertEquals(shortString, mTextView.getText().toString()); 1546 assertEquals(shortString, mTextView.getEditableText().toString()); 1547 // there is no selection. 1548 assertEquals(-1, mTextView.getSelectionStart()); 1549 assertEquals(-1, mTextView.getSelectionEnd()); 1550 1551 // selection is at the exact place which is outside the short string 1552 mTextView.setText(longString); 1553 Selection.setSelection((Spannable) mTextView.getText(), longString.length()); 1554 mTextView.setTextKeepState(shortString, BufferType.EDITABLE); 1555 assertTrue(mTextView.getText() instanceof Editable); 1556 assertEquals(shortString, mTextView.getText().toString()); 1557 assertEquals(shortString, mTextView.getEditableText().toString()); 1558 assertEquals(shortString.length(), mTextView.getSelectionStart()); 1559 assertEquals(shortString.length(), mTextView.getSelectionEnd()); 1560 1561 mTextView.setText(shortString, BufferType.EDITABLE); 1562 assertTrue(mTextView.getText() instanceof Editable); 1563 assertEquals(shortString, mTextView.getText().toString()); 1564 assertEquals(shortString, mTextView.getEditableText().toString()); 1565 // there is no selection. 1566 assertEquals(-1, mTextView.getSelectionStart()); 1567 assertEquals(-1, mTextView.getSelectionEnd()); 1568 1569 // select the sub string which is inside the short string 1570 mTextView.setText(longString); 1571 Selection.setSelection((Spannable) mTextView.getText(), 1, shortString.length() - 1); 1572 mTextView.setTextKeepState(shortString, BufferType.EDITABLE); 1573 assertTrue(mTextView.getText() instanceof Editable); 1574 assertEquals(shortString, mTextView.getText().toString()); 1575 assertEquals(shortString, mTextView.getEditableText().toString()); 1576 assertEquals(1, mTextView.getSelectionStart()); 1577 assertEquals(shortString.length() - 1, mTextView.getSelectionEnd()); 1578 1579 mTextView.setText(shortString, BufferType.EDITABLE); 1580 assertTrue(mTextView.getText() instanceof Editable); 1581 assertEquals(shortString, mTextView.getText().toString()); 1582 assertEquals(shortString, mTextView.getEditableText().toString()); 1583 // there is no selection. 1584 assertEquals(-1, mTextView.getSelectionStart()); 1585 assertEquals(-1, mTextView.getSelectionEnd()); 1586 1587 // select the sub string which ends outside the short string 1588 mTextView.setText(longString); 1589 Selection.setSelection((Spannable) mTextView.getText(), 2, longString.length()); 1590 mTextView.setTextKeepState(shortString, BufferType.EDITABLE); 1591 assertTrue(mTextView.getText() instanceof Editable); 1592 assertEquals(shortString, mTextView.getText().toString()); 1593 assertEquals(shortString, mTextView.getEditableText().toString()); 1594 assertEquals(2, mTextView.getSelectionStart()); 1595 assertEquals(shortString.length(), mTextView.getSelectionEnd()); 1596 1597 mTextView.setText(shortString, BufferType.EDITABLE); 1598 assertTrue(mTextView.getText() instanceof Editable); 1599 assertEquals(shortString, mTextView.getText().toString()); 1600 assertEquals(shortString, mTextView.getEditableText().toString()); 1601 // there is no selection. 1602 assertEquals(-1, mTextView.getSelectionStart()); 1603 assertEquals(-1, mTextView.getSelectionEnd()); 1604 1605 // select the sub string which is outside the short string 1606 mTextView.setText(longString); 1607 Selection.setSelection((Spannable) mTextView.getText(), 1608 shortString.length() + 1, shortString.length() + 3); 1609 mTextView.setTextKeepState(shortString, BufferType.EDITABLE); 1610 assertTrue(mTextView.getText() instanceof Editable); 1611 assertEquals(shortString, mTextView.getText().toString()); 1612 assertEquals(shortString, mTextView.getEditableText().toString()); 1613 assertEquals(shortString.length(), mTextView.getSelectionStart()); 1614 assertEquals(shortString.length(), mTextView.getSelectionEnd()); 1615 1616 mTextView.setText(shortString, BufferType.EDITABLE); 1617 assertTrue(mTextView.getText() instanceof Editable); 1618 assertEquals(shortString, mTextView.getText().toString()); 1619 assertEquals(shortString, mTextView.getEditableText().toString()); 1620 // there is no selection. 1621 assertEquals(-1, mTextView.getSelectionStart()); 1622 assertEquals(-1, mTextView.getSelectionEnd()); 1623 } 1624 1625 @UiThreadTest 1626 @Test 1627 public void testSetText3() { 1628 TextView tv = findTextView(R.id.textview_text); 1629 1630 int resId = R.string.text_view_hint; 1631 String result = mActivity.getResources().getString(resId); 1632 1633 tv.setText(resId); 1634 assertEquals(result, tv.getText().toString()); 1635 1636 try { 1637 tv.setText(-1); 1638 fail("Should throw exception with illegal id"); 1639 } catch (NotFoundException e) { 1640 } 1641 } 1642 1643 @Test 1644 public void testSetTextUpdatesHeightAfterRemovingImageSpan() throws Throwable { 1645 // Height calculation had problems when TextView had width: match_parent 1646 final int textViewWidth = ViewGroup.LayoutParams.MATCH_PARENT; 1647 final Spannable text = new SpannableString("some text"); 1648 final int spanHeight = 100; 1649 1650 // prepare TextView, width: MATCH_PARENT 1651 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 1652 mInstrumentation.waitForIdleSync(); 1653 mTextView.setSingleLine(true); 1654 mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 2); 1655 mTextView.setPadding(0, 0, 0, 0); 1656 mTextView.setIncludeFontPadding(false); 1657 mTextView.setText(text); 1658 final FrameLayout layout = new FrameLayout(mActivity); 1659 final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(textViewWidth, 1660 ViewGroup.LayoutParams.WRAP_CONTENT); 1661 layout.addView(mTextView, layoutParams); 1662 layout.setLayoutParams(layoutParams); 1663 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout)); 1664 mInstrumentation.waitForIdleSync(); 1665 1666 // measure height of text with no span 1667 final int heightWithoutSpan = mTextView.getHeight(); 1668 assertTrue("Text height should be smaller than span height", 1669 heightWithoutSpan < spanHeight); 1670 1671 // add ImageSpan to text 1672 Drawable drawable = mInstrumentation.getContext().getDrawable(R.drawable.scenery); 1673 drawable.setBounds(0, 0, spanHeight, spanHeight); 1674 ImageSpan span = new ImageSpan(drawable); 1675 text.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 1676 mActivityRule.runOnUiThread(() -> mTextView.setText(text)); 1677 mInstrumentation.waitForIdleSync(); 1678 1679 // measure height with span 1680 final int heightWithSpan = mTextView.getHeight(); 1681 assertTrue("Text height should be greater or equal than span height", 1682 heightWithSpan >= spanHeight); 1683 1684 // remove the span 1685 text.removeSpan(span); 1686 mActivityRule.runOnUiThread(() -> mTextView.setText(text)); 1687 mInstrumentation.waitForIdleSync(); 1688 1689 final int heightAfterRemoveSpan = mTextView.getHeight(); 1690 assertEquals("Text height should be same after removing the span", 1691 heightWithoutSpan, heightAfterRemoveSpan); 1692 } 1693 1694 @Test 1695 public void testRemoveSelectionWithSelectionHandles() throws Throwable { 1696 initTextViewForTypingOnUiThread(); 1697 1698 assertFalse(mTextView.isTextSelectable()); 1699 mActivityRule.runOnUiThread(() -> { 1700 mTextView.setTextIsSelectable(true); 1701 mTextView.setText("abcd", BufferType.EDITABLE); 1702 }); 1703 mInstrumentation.waitForIdleSync(); 1704 assertTrue(mTextView.isTextSelectable()); 1705 1706 // Long click on the text selects all text and shows selection handlers. The view has an 1707 // attribute layout_width="wrap_content", so clicked location (the center of the view) 1708 // should be on the text. 1709 CtsTouchUtils.emulateLongPressOnViewCenter(mInstrumentation, mTextView); 1710 1711 mActivityRule.runOnUiThread(() -> Selection.removeSelection((Spannable) mTextView.getText())); 1712 mInstrumentation.waitForIdleSync(); 1713 1714 assertTrue(TextUtils.equals("abcd", mTextView.getText())); 1715 } 1716 1717 @Test 1718 public void testUndo_insert() throws Throwable { 1719 initTextViewForTypingOnUiThread(); 1720 1721 // Type some text. 1722 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 1723 mActivityRule.runOnUiThread(() -> { 1724 // Precondition: The cursor is at the end of the text. 1725 assertEquals(3, mTextView.getSelectionStart()); 1726 1727 // Undo removes the typed string in one step. 1728 mTextView.onTextContextMenuItem(android.R.id.undo); 1729 assertEquals("", mTextView.getText().toString()); 1730 assertEquals(0, mTextView.getSelectionStart()); 1731 1732 // Redo restores the text and cursor position. 1733 mTextView.onTextContextMenuItem(android.R.id.redo); 1734 assertEquals("abc", mTextView.getText().toString()); 1735 assertEquals(3, mTextView.getSelectionStart()); 1736 1737 // Undoing the redo clears the text again. 1738 mTextView.onTextContextMenuItem(android.R.id.undo); 1739 assertEquals("", mTextView.getText().toString()); 1740 1741 // Undo when the undo stack is empty does nothing. 1742 mTextView.onTextContextMenuItem(android.R.id.undo); 1743 assertEquals("", mTextView.getText().toString()); 1744 }); 1745 mInstrumentation.waitForIdleSync(); 1746 } 1747 1748 @Test 1749 public void testUndo_delete() throws Throwable { 1750 initTextViewForTypingOnUiThread(); 1751 1752 // Simulate deleting text and undoing it. 1753 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "xyz"); 1754 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL, 1755 KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL); 1756 mActivityRule.runOnUiThread(() -> { 1757 // Precondition: The text was actually deleted. 1758 assertEquals("", mTextView.getText().toString()); 1759 assertEquals(0, mTextView.getSelectionStart()); 1760 1761 // Undo restores the typed string and cursor position in one step. 1762 mTextView.onTextContextMenuItem(android.R.id.undo); 1763 assertEquals("xyz", mTextView.getText().toString()); 1764 assertEquals(3, mTextView.getSelectionStart()); 1765 1766 // Redo removes the text in one step. 1767 mTextView.onTextContextMenuItem(android.R.id.redo); 1768 assertEquals("", mTextView.getText().toString()); 1769 assertEquals(0, mTextView.getSelectionStart()); 1770 1771 // Undoing the redo restores the text again. 1772 mTextView.onTextContextMenuItem(android.R.id.undo); 1773 assertEquals("xyz", mTextView.getText().toString()); 1774 assertEquals(3, mTextView.getSelectionStart()); 1775 1776 // Undoing again undoes the original typing. 1777 mTextView.onTextContextMenuItem(android.R.id.undo); 1778 assertEquals("", mTextView.getText().toString()); 1779 assertEquals(0, mTextView.getSelectionStart()); 1780 }); 1781 mInstrumentation.waitForIdleSync(); 1782 } 1783 1784 // Initialize the text view for simulated IME typing. Must be called on UI thread. 1785 private InputConnection initTextViewForSimulatedIme() { 1786 mTextView = findTextView(R.id.textview_text); 1787 return initTextViewForSimulatedIme(mTextView); 1788 } 1789 1790 private InputConnection initTextViewForSimulatedIme(TextView textView) { 1791 textView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE)); 1792 textView.setText("", BufferType.EDITABLE); 1793 return textView.onCreateInputConnection(new EditorInfo()); 1794 } 1795 1796 // Simulates IME composing text behavior. 1797 private void setComposingTextInBatch(InputConnection input, CharSequence text) { 1798 input.beginBatchEdit(); 1799 input.setComposingText(text, 1); // Leave cursor at end. 1800 input.endBatchEdit(); 1801 } 1802 1803 @UiThreadTest 1804 @Test 1805 public void testUndo_imeInsertLatin() { 1806 InputConnection input = initTextViewForSimulatedIme(); 1807 1808 // Simulate IME text entry behavior. The Latin IME enters text by replacing partial words, 1809 // such as "c" -> "ca" -> "cat" -> "cat ". 1810 setComposingTextInBatch(input, "c"); 1811 setComposingTextInBatch(input, "ca"); 1812 1813 // The completion and space are added in the same batch. 1814 input.beginBatchEdit(); 1815 input.commitText("cat", 1); 1816 input.commitText(" ", 1); 1817 input.endBatchEdit(); 1818 1819 // The repeated replacements undo in a single step. 1820 mTextView.onTextContextMenuItem(android.R.id.undo); 1821 assertEquals("", mTextView.getText().toString()); 1822 } 1823 1824 @UiThreadTest 1825 @Test 1826 public void testUndo_imeInsertJapanese() { 1827 InputConnection input = initTextViewForSimulatedIme(); 1828 1829 // The Japanese IME does repeated replacements of Latin characters to hiragana to kanji. 1830 final String HA = "\u306F"; // HIRAGANA LETTER HA 1831 final String NA = "\u306A"; // HIRAGANA LETTER NA 1832 setComposingTextInBatch(input, "h"); 1833 setComposingTextInBatch(input, HA); 1834 setComposingTextInBatch(input, HA + "n"); 1835 setComposingTextInBatch(input, HA + NA); 1836 1837 // The result may be a surrogate pair. The composition ends in the same batch. 1838 input.beginBatchEdit(); 1839 input.commitText("\uD83C\uDF37", 1); // U+1F337 TULIP 1840 input.setComposingText("", 1); 1841 input.endBatchEdit(); 1842 1843 // The repeated replacements are a single undo step. 1844 mTextView.onTextContextMenuItem(android.R.id.undo); 1845 assertEquals("", mTextView.getText().toString()); 1846 } 1847 1848 @UiThreadTest 1849 @Test 1850 public void testUndo_imeInsertAndDeleteLatin() { 1851 InputConnection input = initTextViewForSimulatedIme(); 1852 1853 setComposingTextInBatch(input, "t"); 1854 setComposingTextInBatch(input, "te"); 1855 setComposingTextInBatch(input, "tes"); 1856 setComposingTextInBatch(input, "test"); 1857 setComposingTextInBatch(input, "tes"); 1858 setComposingTextInBatch(input, "te"); 1859 setComposingTextInBatch(input, "t"); 1860 1861 input.beginBatchEdit(); 1862 input.setComposingText("", 1); 1863 input.finishComposingText(); 1864 input.endBatchEdit(); 1865 1866 mTextView.onTextContextMenuItem(android.R.id.undo); 1867 assertEquals("test", mTextView.getText().toString()); 1868 mTextView.onTextContextMenuItem(android.R.id.undo); 1869 assertEquals("", mTextView.getText().toString()); 1870 } 1871 1872 @UiThreadTest 1873 @Test 1874 public void testUndo_imeAutoCorrection() { 1875 mTextView = findTextView(R.id.textview_text); 1876 TextView spiedTextView = spy(mTextView); 1877 InputConnection input = initTextViewForSimulatedIme(spiedTextView); 1878 1879 // Start typing a composition. 1880 setComposingTextInBatch(input, "t"); 1881 setComposingTextInBatch(input, "te"); 1882 setComposingTextInBatch(input, "teh"); 1883 1884 CorrectionInfo correctionInfo = new CorrectionInfo(0, "teh", "the"); 1885 reset(spiedTextView); 1886 input.beginBatchEdit(); 1887 // Auto correct "teh" to "the". 1888 assertTrue(input.commitCorrection(correctionInfo)); 1889 input.commitText("the", 1); 1890 input.endBatchEdit(); 1891 1892 verify(spiedTextView, times(1)).onCommitCorrection(refEq(correctionInfo)); 1893 1894 assertEquals("the", spiedTextView.getText().toString()); 1895 spiedTextView.onTextContextMenuItem(android.R.id.undo); 1896 assertEquals("teh", spiedTextView.getText().toString()); 1897 spiedTextView.onTextContextMenuItem(android.R.id.undo); 1898 assertEquals("", spiedTextView.getText().toString()); 1899 } 1900 1901 @UiThreadTest 1902 @Test 1903 public void testUndo_imeAutoCompletion() { 1904 mTextView = findTextView(R.id.textview_text); 1905 TextView spiedTextView = spy(mTextView); 1906 InputConnection input = initTextViewForSimulatedIme(spiedTextView); 1907 1908 // Start typing a composition. 1909 setComposingTextInBatch(input, "a"); 1910 setComposingTextInBatch(input, "an"); 1911 setComposingTextInBatch(input, "and"); 1912 1913 CompletionInfo completionInfo = new CompletionInfo(0, 0, "android"); 1914 reset(spiedTextView); 1915 input.beginBatchEdit(); 1916 // Auto complete "and" to "android". 1917 assertTrue(input.commitCompletion(completionInfo)); 1918 input.commitText("android", 1); 1919 input.endBatchEdit(); 1920 1921 verify(spiedTextView, times(1)).onCommitCompletion(refEq(completionInfo)); 1922 1923 assertEquals("android", spiedTextView.getText().toString()); 1924 spiedTextView.onTextContextMenuItem(android.R.id.undo); 1925 assertEquals("", spiedTextView.getText().toString()); 1926 } 1927 1928 @UiThreadTest 1929 @Test 1930 public void testUndo_imeCancel() { 1931 InputConnection input = initTextViewForSimulatedIme(); 1932 mTextView.setText("flower"); 1933 1934 // Start typing a composition. 1935 final String HA = "\u306F"; // HIRAGANA LETTER HA 1936 setComposingTextInBatch(input, "h"); 1937 setComposingTextInBatch(input, HA); 1938 setComposingTextInBatch(input, HA + "n"); 1939 1940 // Cancel the composition. 1941 setComposingTextInBatch(input, ""); 1942 1943 mTextView.onTextContextMenuItem(android.R.id.undo); 1944 assertEquals(HA + "n" + "flower", mTextView.getText().toString()); 1945 mTextView.onTextContextMenuItem(android.R.id.redo); 1946 assertEquals("flower", mTextView.getText().toString()); 1947 } 1948 1949 @UiThreadTest 1950 @Test 1951 public void testUndo_imeEmptyBatch() { 1952 InputConnection input = initTextViewForSimulatedIme(); 1953 mTextView.setText("flower"); 1954 1955 // Send an empty batch edit. This happens if the IME is hidden and shown. 1956 input.beginBatchEdit(); 1957 input.endBatchEdit(); 1958 1959 // Undo and redo do nothing. 1960 mTextView.onTextContextMenuItem(android.R.id.undo); 1961 assertEquals("flower", mTextView.getText().toString()); 1962 mTextView.onTextContextMenuItem(android.R.id.redo); 1963 assertEquals("flower", mTextView.getText().toString()); 1964 } 1965 1966 @Test 1967 public void testUndo_setText() throws Throwable { 1968 initTextViewForTypingOnUiThread(); 1969 1970 // Create two undo operations, an insert and a delete. 1971 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "xyz"); 1972 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL, 1973 KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL); 1974 mActivityRule.runOnUiThread(() -> { 1975 // Calling setText() clears both undo operations, so undo doesn't happen. 1976 mTextView.setText("Hello", BufferType.EDITABLE); 1977 mTextView.onTextContextMenuItem(android.R.id.undo); 1978 assertEquals("Hello", mTextView.getText().toString()); 1979 1980 // Clearing text programmatically does not undo either. 1981 mTextView.setText("", BufferType.EDITABLE); 1982 mTextView.onTextContextMenuItem(android.R.id.undo); 1983 assertEquals("", mTextView.getText().toString()); 1984 }); 1985 mInstrumentation.waitForIdleSync(); 1986 } 1987 1988 @Test 1989 public void testRedo_setText() throws Throwable { 1990 initTextViewForTypingOnUiThread(); 1991 1992 // Type some text. This creates an undo entry. 1993 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 1994 mActivityRule.runOnUiThread(() -> { 1995 // Undo the typing to create a redo entry. 1996 mTextView.onTextContextMenuItem(android.R.id.undo); 1997 1998 // Calling setText() clears the redo stack, so redo doesn't happen. 1999 mTextView.setText("Hello", BufferType.EDITABLE); 2000 mTextView.onTextContextMenuItem(android.R.id.redo); 2001 assertEquals("Hello", mTextView.getText().toString()); 2002 }); 2003 mInstrumentation.waitForIdleSync(); 2004 } 2005 2006 @Test 2007 public void testUndo_directAppend() throws Throwable { 2008 initTextViewForTypingOnUiThread(); 2009 2010 // Type some text. 2011 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 2012 mActivityRule.runOnUiThread(() -> { 2013 // Programmatically append some text. 2014 mTextView.append("def"); 2015 assertEquals("abcdef", mTextView.getText().toString()); 2016 2017 // Undo removes the append as a separate step. 2018 mTextView.onTextContextMenuItem(android.R.id.undo); 2019 assertEquals("abc", mTextView.getText().toString()); 2020 2021 // Another undo removes the original typing. 2022 mTextView.onTextContextMenuItem(android.R.id.undo); 2023 assertEquals("", mTextView.getText().toString()); 2024 }); 2025 mInstrumentation.waitForIdleSync(); 2026 } 2027 2028 @Test 2029 public void testUndo_directInsert() throws Throwable { 2030 initTextViewForTypingOnUiThread(); 2031 2032 // Type some text. 2033 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 2034 mActivityRule.runOnUiThread(() -> { 2035 // Directly modify the underlying Editable to insert some text. 2036 // NOTE: This is a violation of the API of getText() which specifies that the 2037 // returned object should not be modified. However, some apps do this anyway and 2038 // the framework needs to handle it. 2039 Editable text = (Editable) mTextView.getText(); 2040 text.insert(0, "def"); 2041 assertEquals("defabc", mTextView.getText().toString()); 2042 2043 // Undo removes the insert as a separate step. 2044 mTextView.onTextContextMenuItem(android.R.id.undo); 2045 assertEquals("abc", mTextView.getText().toString()); 2046 2047 // Another undo removes the original typing. 2048 mTextView.onTextContextMenuItem(android.R.id.undo); 2049 assertEquals("", mTextView.getText().toString()); 2050 }); 2051 mInstrumentation.waitForIdleSync(); 2052 } 2053 2054 @UiThreadTest 2055 @Test 2056 public void testUndo_noCursor() { 2057 initTextViewForTyping(); 2058 2059 // Append some text to create an undo operation. There is no cursor present. 2060 mTextView.append("cat"); 2061 2062 // Place the cursor at the end of the text so the undo will have to change it. 2063 Selection.setSelection((Spannable) mTextView.getText(), 3); 2064 2065 // Undo the append. This should not crash, despite not having a valid cursor 2066 // position in the undo operation. 2067 mTextView.onTextContextMenuItem(android.R.id.undo); 2068 } 2069 2070 @Test 2071 public void testUndo_textWatcher() throws Throwable { 2072 initTextViewForTypingOnUiThread(); 2073 2074 // Add a TextWatcher that converts the text to spaces on each change. 2075 mTextView.addTextChangedListener(new ConvertToSpacesTextWatcher()); 2076 2077 // Type some text. 2078 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 2079 mActivityRule.runOnUiThread(() -> { 2080 // TextWatcher altered the text. 2081 assertEquals(" ", mTextView.getText().toString()); 2082 2083 // Undo reverses both changes in one step. 2084 mTextView.onTextContextMenuItem(android.R.id.undo); 2085 assertEquals("", mTextView.getText().toString()); 2086 }); 2087 mInstrumentation.waitForIdleSync(); 2088 } 2089 2090 @UiThreadTest 2091 @Test 2092 public void testUndo_textWatcherDirectAppend() { 2093 initTextViewForTyping(); 2094 2095 // Add a TextWatcher that converts the text to spaces on each change. 2096 mTextView.addTextChangedListener(new ConvertToSpacesTextWatcher()); 2097 2098 // Programmatically append some text. The TextWatcher changes it to spaces. 2099 mTextView.append("abc"); 2100 assertEquals(" ", mTextView.getText().toString()); 2101 2102 // Undo reverses both changes in one step. 2103 mTextView.onTextContextMenuItem(android.R.id.undo); 2104 assertEquals("", mTextView.getText().toString()); 2105 } 2106 2107 @Test 2108 public void testUndo_shortcuts() throws Throwable { 2109 initTextViewForTypingOnUiThread(); 2110 2111 // Type some text. 2112 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 2113 mActivityRule.runOnUiThread(() -> { 2114 // Pressing Control-Z triggers undo. 2115 KeyEvent control = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_Z, 0, 2116 KeyEvent.META_CTRL_LEFT_ON); 2117 assertTrue(mTextView.onKeyShortcut(KeyEvent.KEYCODE_Z, control)); 2118 assertEquals("", mTextView.getText().toString()); 2119 2120 // Pressing Control-Shift-Z triggers redo. 2121 KeyEvent controlShift = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_Z, 2122 0, KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_SHIFT_LEFT_ON); 2123 assertTrue(mTextView.onKeyShortcut(KeyEvent.KEYCODE_Z, controlShift)); 2124 assertEquals("abc", mTextView.getText().toString()); 2125 }); 2126 mInstrumentation.waitForIdleSync(); 2127 } 2128 2129 @Test 2130 public void testUndo_saveInstanceState() throws Throwable { 2131 initTextViewForTypingOnUiThread(); 2132 2133 // Type some text to create an undo operation. 2134 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 2135 mActivityRule.runOnUiThread(() -> { 2136 // Parcel and unparcel the TextView. 2137 Parcelable state = mTextView.onSaveInstanceState(); 2138 mTextView.onRestoreInstanceState(state); 2139 }); 2140 mInstrumentation.waitForIdleSync(); 2141 2142 // Delete a character to create a new undo operation. 2143 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 2144 mActivityRule.runOnUiThread(() -> { 2145 assertEquals("ab", mTextView.getText().toString()); 2146 2147 // Undo the delete. 2148 mTextView.onTextContextMenuItem(android.R.id.undo); 2149 assertEquals("abc", mTextView.getText().toString()); 2150 2151 // Undo the typing, which verifies that the original undo operation was parceled 2152 // correctly. 2153 mTextView.onTextContextMenuItem(android.R.id.undo); 2154 assertEquals("", mTextView.getText().toString()); 2155 2156 // Parcel and unparcel the undo stack (which is empty but has been used and may 2157 // contain other state). 2158 Parcelable state = mTextView.onSaveInstanceState(); 2159 mTextView.onRestoreInstanceState(state); 2160 }); 2161 mInstrumentation.waitForIdleSync(); 2162 } 2163 2164 @Test 2165 public void testUndo_saveInstanceStateEmpty() throws Throwable { 2166 initTextViewForTypingOnUiThread(); 2167 2168 // Type and delete to create two new undo operations. 2169 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a"); 2170 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 2171 mActivityRule.runOnUiThread(() -> { 2172 // Empty the undo stack then parcel and unparcel the TextView. While the undo 2173 // stack contains no operations it may contain other state. 2174 mTextView.onTextContextMenuItem(android.R.id.undo); 2175 mTextView.onTextContextMenuItem(android.R.id.undo); 2176 Parcelable state = mTextView.onSaveInstanceState(); 2177 mTextView.onRestoreInstanceState(state); 2178 }); 2179 mInstrumentation.waitForIdleSync(); 2180 2181 // Create two more undo operations. 2182 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "b"); 2183 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 2184 mActivityRule.runOnUiThread(() -> { 2185 // Verify undo still works. 2186 mTextView.onTextContextMenuItem(android.R.id.undo); 2187 assertEquals("b", mTextView.getText().toString()); 2188 mTextView.onTextContextMenuItem(android.R.id.undo); 2189 assertEquals("", mTextView.getText().toString()); 2190 }); 2191 mInstrumentation.waitForIdleSync(); 2192 } 2193 2194 @UiThreadTest 2195 @Test 2196 public void testCopyAndPaste() { 2197 initTextViewForTyping(); 2198 2199 mTextView.setText("abcd", BufferType.EDITABLE); 2200 mTextView.setSelected(true); 2201 2202 // Copy "bc". 2203 Selection.setSelection((Spannable) mTextView.getText(), 1, 3); 2204 mTextView.onTextContextMenuItem(android.R.id.copy); 2205 2206 // Paste "bc" between "b" and "c". 2207 Selection.setSelection((Spannable) mTextView.getText(), 2, 2); 2208 mTextView.onTextContextMenuItem(android.R.id.paste); 2209 assertEquals("abbccd", mTextView.getText().toString()); 2210 2211 // Select entire text and paste "bc". 2212 Selection.selectAll((Spannable) mTextView.getText()); 2213 mTextView.onTextContextMenuItem(android.R.id.paste); 2214 assertEquals("bc", mTextView.getText().toString()); 2215 } 2216 2217 @Test 2218 public void testCopyAndPaste_byKey() throws Throwable { 2219 initTextViewForTypingOnUiThread(); 2220 2221 // Type "abc". 2222 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 2223 mActivityRule.runOnUiThread(() -> { 2224 // Select "bc" 2225 Selection.setSelection((Spannable) mTextView.getText(), 1, 3); 2226 }); 2227 mInstrumentation.waitForIdleSync(); 2228 // Copy "bc" 2229 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_COPY); 2230 2231 mActivityRule.runOnUiThread(() -> { 2232 // Set cursor between 'b' and 'c'. 2233 Selection.setSelection((Spannable) mTextView.getText(), 2, 2); 2234 }); 2235 mInstrumentation.waitForIdleSync(); 2236 // Paste "bc" 2237 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PASTE); 2238 assertEquals("abbcc", mTextView.getText().toString()); 2239 2240 mActivityRule.runOnUiThread(() -> { 2241 Selection.selectAll((Spannable) mTextView.getText()); 2242 KeyEvent copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, 2243 KeyEvent.KEYCODE_COPY, 0, KeyEvent.META_SHIFT_LEFT_ON); 2244 // Shift + copy doesn't perform copy. 2245 mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta); 2246 Selection.setSelection((Spannable) mTextView.getText(), 0, 0); 2247 mTextView.onTextContextMenuItem(android.R.id.paste); 2248 assertEquals("bcabbcc", mTextView.getText().toString()); 2249 2250 Selection.selectAll((Spannable) mTextView.getText()); 2251 copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_COPY, 0, 2252 KeyEvent.META_CTRL_LEFT_ON); 2253 // Control + copy doesn't perform copy. 2254 mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta); 2255 Selection.setSelection((Spannable) mTextView.getText(), 0, 0); 2256 mTextView.onTextContextMenuItem(android.R.id.paste); 2257 assertEquals("bcbcabbcc", mTextView.getText().toString()); 2258 2259 Selection.selectAll((Spannable) mTextView.getText()); 2260 copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_COPY, 0, 2261 KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_CTRL_LEFT_ON); 2262 // Control + Shift + copy doesn't perform copy. 2263 mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta); 2264 Selection.setSelection((Spannable) mTextView.getText(), 0, 0); 2265 mTextView.onTextContextMenuItem(android.R.id.paste); 2266 assertEquals("bcbcbcabbcc", mTextView.getText().toString()); 2267 }); 2268 mInstrumentation.waitForIdleSync(); 2269 } 2270 2271 @UiThreadTest 2272 @Test 2273 public void testCutAndPaste() { 2274 initTextViewForTyping(); 2275 2276 mTextView.setText("abcd", BufferType.EDITABLE); 2277 mTextView.setSelected(true); 2278 2279 // Cut "bc". 2280 Selection.setSelection((Spannable) mTextView.getText(), 1, 3); 2281 mTextView.onTextContextMenuItem(android.R.id.cut); 2282 assertEquals("ad", mTextView.getText().toString()); 2283 2284 // Cut "ad". 2285 Selection.setSelection((Spannable) mTextView.getText(), 0, 2); 2286 mTextView.onTextContextMenuItem(android.R.id.cut); 2287 assertEquals("", mTextView.getText().toString()); 2288 2289 // Paste "ad". 2290 mTextView.onTextContextMenuItem(android.R.id.paste); 2291 assertEquals("ad", mTextView.getText().toString()); 2292 } 2293 2294 @Test 2295 public void testCutAndPaste_byKey() throws Throwable { 2296 initTextViewForTypingOnUiThread(); 2297 2298 // Type "abc". 2299 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc"); 2300 mActivityRule.runOnUiThread(() -> { 2301 // Select "bc" 2302 Selection.setSelection((Spannable) mTextView.getText(), 1, 3); 2303 }); 2304 mInstrumentation.waitForIdleSync(); 2305 // Cut "bc" 2306 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_CUT); 2307 2308 mActivityRule.runOnUiThread(() -> { 2309 assertEquals("a", mTextView.getText().toString()); 2310 // Move cursor to the head 2311 Selection.setSelection((Spannable) mTextView.getText(), 0, 0); 2312 }); 2313 mInstrumentation.waitForIdleSync(); 2314 // Paste "bc" 2315 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PASTE); 2316 assertEquals("bca", mTextView.getText().toString()); 2317 2318 mActivityRule.runOnUiThread(() -> { 2319 Selection.selectAll((Spannable) mTextView.getText()); 2320 KeyEvent cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, 2321 KeyEvent.KEYCODE_CUT, 0, KeyEvent.META_SHIFT_LEFT_ON); 2322 // Shift + cut doesn't perform cut. 2323 mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta); 2324 assertEquals("bca", mTextView.getText().toString()); 2325 2326 cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CUT, 0, 2327 KeyEvent.META_CTRL_LEFT_ON); 2328 // Control + cut doesn't perform cut. 2329 mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta); 2330 assertEquals("bca", mTextView.getText().toString()); 2331 2332 cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CUT, 0, 2333 KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_CTRL_LEFT_ON); 2334 // Control + Shift + cut doesn't perform cut. 2335 mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta); 2336 assertEquals("bca", mTextView.getText().toString()); 2337 }); 2338 mInstrumentation.waitForIdleSync(); 2339 } 2340 2341 private static boolean hasSpansAtMiddleOfText(final TextView textView, final Class<?> type) { 2342 final Spannable spannable = (Spannable)textView.getText(); 2343 final int at = spannable.length() / 2; 2344 return spannable.getSpans(at, at, type).length > 0; 2345 } 2346 2347 @UiThreadTest 2348 @Test 2349 public void testCutAndPaste_withAndWithoutStyle() { 2350 initTextViewForTyping(); 2351 2352 mTextView.setText("example", BufferType.EDITABLE); 2353 mTextView.setSelected(true); 2354 2355 // Set URLSpan. 2356 final Spannable spannable = (Spannable) mTextView.getText(); 2357 spannable.setSpan(new URLSpan("http://example.com"), 0, spannable.length(), 0); 2358 assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class)); 2359 2360 // Cut entire text. 2361 Selection.selectAll((Spannable) mTextView.getText()); 2362 mTextView.onTextContextMenuItem(android.R.id.cut); 2363 assertEquals("", mTextView.getText().toString()); 2364 2365 // Paste without style. 2366 mTextView.onTextContextMenuItem(android.R.id.pasteAsPlainText); 2367 assertEquals("example", mTextView.getText().toString()); 2368 // Check that the text doesn't have URLSpan. 2369 assertFalse(hasSpansAtMiddleOfText(mTextView, URLSpan.class)); 2370 2371 // Paste with style. 2372 Selection.selectAll((Spannable) mTextView.getText()); 2373 mTextView.onTextContextMenuItem(android.R.id.paste); 2374 assertEquals("example", mTextView.getText().toString()); 2375 // Check that the text has URLSpan. 2376 assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class)); 2377 } 2378 2379 @UiThreadTest 2380 @Test 2381 public void testSaveInstanceState() { 2382 // should save text when freezesText=true 2383 TextView originalTextView = new TextView(mActivity); 2384 final String text = "This is a string"; 2385 originalTextView.setText(text); 2386 originalTextView.setFreezesText(true); // needed to actually save state 2387 Parcelable state = originalTextView.onSaveInstanceState(); 2388 2389 TextView restoredTextView = new TextView(mActivity); 2390 restoredTextView.onRestoreInstanceState(state); 2391 assertEquals(text, restoredTextView.getText().toString()); 2392 } 2393 2394 @UiThreadTest 2395 @Test 2396 public void testOnSaveInstanceState_whenFreezesTextIsFalse() { 2397 final String text = "This is a string"; 2398 { // should not save text when freezesText=false 2399 // prepare TextView for before saveInstanceState 2400 TextView textView1 = new TextView(mActivity); 2401 textView1.setFreezesText(false); 2402 textView1.setText(text); 2403 2404 // prepare TextView for after saveInstanceState 2405 TextView textView2 = new TextView(mActivity); 2406 textView2.setFreezesText(false); 2407 2408 textView2.onRestoreInstanceState(textView1.onSaveInstanceState()); 2409 2410 assertEquals("", textView2.getText().toString()); 2411 } 2412 2413 { // should not save text even when textIsSelectable=true 2414 // prepare TextView for before saveInstanceState 2415 TextView textView1 = new TextView(mActivity); 2416 textView1.setFreezesText(false); 2417 textView1.setTextIsSelectable(true); 2418 textView1.setText(text); 2419 2420 // prepare TextView for after saveInstanceState 2421 TextView textView2 = new TextView(mActivity); 2422 textView2.setFreezesText(false); 2423 textView2.setTextIsSelectable(true); 2424 2425 textView2.onRestoreInstanceState(textView1.onSaveInstanceState()); 2426 2427 assertEquals("", textView2.getText().toString()); 2428 } 2429 } 2430 2431 @UiThreadTest 2432 @SmallTest 2433 @Test 2434 public void testOnSaveInstanceState_doesNotSaveSelectionWhenDoesNotExist() { 2435 // prepare TextView for before saveInstanceState 2436 final String text = "This is a string"; 2437 TextView textView1 = new TextView(mActivity); 2438 textView1.setFreezesText(true); 2439 textView1.setText(text); 2440 2441 // prepare TextView for after saveInstanceState 2442 TextView textView2 = new TextView(mActivity); 2443 textView2.setFreezesText(true); 2444 2445 textView2.onRestoreInstanceState(textView1.onSaveInstanceState()); 2446 2447 assertEquals(-1, textView2.getSelectionStart()); 2448 assertEquals(-1, textView2.getSelectionEnd()); 2449 } 2450 2451 @UiThreadTest 2452 @SmallTest 2453 @Test 2454 public void testOnSaveInstanceState_doesNotRestoreSelectionWhenTextIsAbsent() { 2455 // prepare TextView for before saveInstanceState 2456 final String text = "This is a string"; 2457 TextView textView1 = new TextView(mActivity); 2458 textView1.setFreezesText(false); 2459 textView1.setTextIsSelectable(true); 2460 textView1.setText(text); 2461 Selection.setSelection((Spannable) textView1.getText(), 2, text.length() - 2); 2462 2463 // prepare TextView for after saveInstanceState 2464 TextView textView2 = new TextView(mActivity); 2465 textView2.setFreezesText(false); 2466 textView2.setTextIsSelectable(true); 2467 2468 textView2.onRestoreInstanceState(textView1.onSaveInstanceState()); 2469 2470 assertEquals("", textView2.getText().toString()); 2471 //when textIsSelectable, selection start and end are initialized to 0 2472 assertEquals(0, textView2.getSelectionStart()); 2473 assertEquals(0, textView2.getSelectionEnd()); 2474 } 2475 2476 @UiThreadTest 2477 @SmallTest 2478 @Test 2479 public void testOnSaveInstanceState_savesSelectionWhenExists() { 2480 final String text = "This is a string"; 2481 // prepare TextView for before saveInstanceState 2482 TextView textView1 = new TextView(mActivity); 2483 textView1.setFreezesText(true); 2484 textView1.setTextIsSelectable(true); 2485 textView1.setText(text); 2486 Selection.setSelection((Spannable) textView1.getText(), 2, text.length() - 2); 2487 2488 // prepare TextView for after saveInstanceState 2489 TextView textView2 = new TextView(mActivity); 2490 textView2.setFreezesText(true); 2491 textView2.setTextIsSelectable(true); 2492 2493 textView2.onRestoreInstanceState(textView1.onSaveInstanceState()); 2494 2495 assertEquals(textView1.getSelectionStart(), textView2.getSelectionStart()); 2496 assertEquals(textView1.getSelectionEnd(), textView2.getSelectionEnd()); 2497 } 2498 2499 @UiThreadTest 2500 @Test 2501 public void testSetText() { 2502 TextView tv = findTextView(R.id.textview_text); 2503 2504 int resId = R.string.text_view_hint; 2505 String result = mActivity.getResources().getString(resId); 2506 2507 tv.setText(resId, BufferType.EDITABLE); 2508 assertEquals(result, tv.getText().toString()); 2509 assertTrue(tv.getText() instanceof Editable); 2510 2511 tv.setText(resId, BufferType.SPANNABLE); 2512 assertEquals(result, tv.getText().toString()); 2513 assertTrue(tv.getText() instanceof Spannable); 2514 2515 try { 2516 tv.setText(-1, BufferType.EDITABLE); 2517 fail("Should throw exception with illegal id"); 2518 } catch (NotFoundException e) { 2519 } 2520 } 2521 2522 @UiThreadTest 2523 @Test 2524 public void testAccessHint() { 2525 mActivity.setContentView(R.layout.textview_hint_linksclickable_freezestext); 2526 2527 mTextView = findTextView(R.id.hint_linksClickable_freezesText_default); 2528 assertNull(mTextView.getHint()); 2529 2530 mTextView = findTextView(R.id.hint_blank); 2531 assertEquals("", mTextView.getHint()); 2532 2533 mTextView = findTextView(R.id.hint_string); 2534 assertEquals(mActivity.getResources().getString(R.string.text_view_simple_hint), 2535 mTextView.getHint()); 2536 2537 mTextView = findTextView(R.id.hint_resid); 2538 assertEquals(mActivity.getResources().getString(R.string.text_view_hint), 2539 mTextView.getHint()); 2540 2541 mTextView.setHint("This is hint"); 2542 assertEquals("This is hint", mTextView.getHint().toString()); 2543 2544 mTextView.setHint(R.string.text_view_hello); 2545 assertEquals(mActivity.getResources().getString(R.string.text_view_hello), 2546 mTextView.getHint().toString()); 2547 2548 // Non-exist resid 2549 try { 2550 mTextView.setHint(-1); 2551 fail("Should throw exception if id is illegal"); 2552 } catch (NotFoundException e) { 2553 } 2554 } 2555 2556 @Test 2557 public void testAccessError() throws Throwable { 2558 mTextView = findTextView(R.id.textview_text); 2559 assertNull(mTextView.getError()); 2560 2561 final String errorText = "Oops! There is an error"; 2562 2563 mActivityRule.runOnUiThread(() -> mTextView.setError(null)); 2564 mInstrumentation.waitForIdleSync(); 2565 assertNull(mTextView.getError()); 2566 2567 final Drawable icon = TestUtils.getDrawable(mActivity, R.drawable.failed); 2568 mActivityRule.runOnUiThread(() -> mTextView.setError(errorText, icon)); 2569 mInstrumentation.waitForIdleSync(); 2570 assertEquals(errorText, mTextView.getError().toString()); 2571 // can not check whether the drawable is set correctly 2572 2573 mActivityRule.runOnUiThread(() -> mTextView.setError(null, null)); 2574 mInstrumentation.waitForIdleSync(); 2575 assertNull(mTextView.getError()); 2576 2577 mActivityRule.runOnUiThread(() -> { 2578 mTextView.setKeyListener(DigitsKeyListener.getInstance("")); 2579 mTextView.setText("", BufferType.EDITABLE); 2580 mTextView.setError(errorText); 2581 mTextView.requestFocus(); 2582 }); 2583 mInstrumentation.waitForIdleSync(); 2584 2585 assertEquals(errorText, mTextView.getError().toString()); 2586 2587 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a"); 2588 // a key event that will not change the TextView's text 2589 assertEquals("", mTextView.getText().toString()); 2590 // The icon and error message will not be reset to null 2591 assertEquals(errorText, mTextView.getError().toString()); 2592 2593 mActivityRule.runOnUiThread(() -> { 2594 mTextView.setKeyListener(DigitsKeyListener.getInstance()); 2595 mTextView.setText("", BufferType.EDITABLE); 2596 mTextView.setError(errorText); 2597 mTextView.requestFocus(); 2598 }); 2599 mInstrumentation.waitForIdleSync(); 2600 2601 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "1"); 2602 // a key event cause changes to the TextView's text 2603 assertEquals("1", mTextView.getText().toString()); 2604 // the error message and icon will be cleared. 2605 assertNull(mTextView.getError()); 2606 } 2607 2608 @Test 2609 public void testAccessFilters() throws Throwable { 2610 final InputFilter[] expected = { new InputFilter.AllCaps(), 2611 new InputFilter.LengthFilter(2) }; 2612 2613 final QwertyKeyListener qwertyKeyListener 2614 = QwertyKeyListener.getInstance(false, Capitalize.NONE); 2615 mActivityRule.runOnUiThread(() -> { 2616 mTextView = findTextView(R.id.textview_text); 2617 mTextView.setKeyListener(qwertyKeyListener); 2618 mTextView.setText("", BufferType.EDITABLE); 2619 mTextView.setFilters(expected); 2620 mTextView.requestFocus(); 2621 }); 2622 mInstrumentation.waitForIdleSync(); 2623 2624 assertSame(expected, mTextView.getFilters()); 2625 2626 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a"); 2627 // the text is capitalized by InputFilter.AllCaps 2628 assertEquals("A", mTextView.getText().toString()); 2629 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "b"); 2630 // the text is capitalized by InputFilter.AllCaps 2631 assertEquals("AB", mTextView.getText().toString()); 2632 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "c"); 2633 // 'C' could not be accepted, because there is a length filter. 2634 assertEquals("AB", mTextView.getText().toString()); 2635 2636 try { 2637 mTextView.setFilters(null); 2638 fail("Should throw IllegalArgumentException!"); 2639 } catch (IllegalArgumentException e) { 2640 } 2641 } 2642 2643 @Test 2644 public void testGetFocusedRect() throws Throwable { 2645 Rect rc = new Rect(); 2646 2647 // Basic 2648 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 2649 mInstrumentation.waitForIdleSync(); 2650 mTextView.getFocusedRect(rc); 2651 assertEquals(mTextView.getScrollX(), rc.left); 2652 assertEquals(mTextView.getScrollX() + mTextView.getWidth(), rc.right); 2653 assertEquals(mTextView.getScrollY(), rc.top); 2654 assertEquals(mTextView.getScrollY() + mTextView.getHeight(), rc.bottom); 2655 2656 // Single line 2657 mTextView = findTextView(R.id.textview_text); 2658 mTextView.getFocusedRect(rc); 2659 assertEquals(mTextView.getScrollX(), rc.left); 2660 assertEquals(mTextView.getScrollX() + mTextView.getWidth(), rc.right); 2661 assertEquals(mTextView.getScrollY(), rc.top); 2662 assertEquals(mTextView.getScrollY() + mTextView.getHeight(), rc.bottom); 2663 2664 mActivityRule.runOnUiThread(() -> { 2665 final SpannableString text = new SpannableString(mTextView.getText()); 2666 mTextView.setTextIsSelectable(true); 2667 mTextView.setText(text); 2668 Selection.setSelection((Spannable) mTextView.getText(), 3, 13); 2669 }); 2670 mInstrumentation.waitForIdleSync(); 2671 mTextView.getFocusedRect(rc); 2672 assertNotNull(mTextView.getLayout()); 2673 /* Cursor coordinates from getPrimaryHorizontal() may have a fractional 2674 * component, while the result of getFocusedRect is in int coordinates. 2675 * It's not practical for these to match exactly, so we compare that the 2676 * integer components match - there can be a fractional pixel 2677 * discrepancy, which should be okay for all practical applications. */ 2678 assertEquals((int) mTextView.getLayout().getPrimaryHorizontal(3), rc.left); 2679 assertEquals((int) mTextView.getLayout().getPrimaryHorizontal(13), rc.right); 2680 assertEquals(mTextView.getLayout().getLineTop(0), rc.top); 2681 assertEquals(mTextView.getLayout().getLineBottom(0), rc.bottom); 2682 2683 mActivityRule.runOnUiThread(() -> { 2684 final SpannableString text = new SpannableString(mTextView.getText()); 2685 mTextView.setTextIsSelectable(true); 2686 mTextView.setText(text); 2687 Selection.setSelection((Spannable) mTextView.getText(), 13, 3); 2688 }); 2689 mInstrumentation.waitForIdleSync(); 2690 mTextView.getFocusedRect(rc); 2691 assertNotNull(mTextView.getLayout()); 2692 assertEquals((int) mTextView.getLayout().getPrimaryHorizontal(3) - 2, rc.left); 2693 assertEquals((int) mTextView.getLayout().getPrimaryHorizontal(3) + 2, rc.right); 2694 assertEquals(mTextView.getLayout().getLineTop(0), rc.top); 2695 assertEquals(mTextView.getLayout().getLineBottom(0), rc.bottom); 2696 2697 // Multi lines 2698 mTextView = findTextView(R.id.textview_text_two_lines); 2699 mTextView.getFocusedRect(rc); 2700 assertEquals(mTextView.getScrollX(), rc.left); 2701 assertEquals(mTextView.getScrollX() + mTextView.getWidth(), rc.right); 2702 assertEquals(mTextView.getScrollY(), rc.top); 2703 assertEquals(mTextView.getScrollY() + mTextView.getHeight(), rc.bottom); 2704 2705 mActivityRule.runOnUiThread(() -> { 2706 final SpannableString text = new SpannableString(mTextView.getText()); 2707 mTextView.setTextIsSelectable(true); 2708 mTextView.setText(text); 2709 Selection.setSelection((Spannable) mTextView.getText(), 2, 4); 2710 }); 2711 mInstrumentation.waitForIdleSync(); 2712 mTextView.getFocusedRect(rc); 2713 assertNotNull(mTextView.getLayout()); 2714 assertEquals((int) mTextView.getLayout().getPrimaryHorizontal(2), rc.left); 2715 assertEquals((int) mTextView.getLayout().getPrimaryHorizontal(4), rc.right); 2716 assertEquals(mTextView.getLayout().getLineTop(0), rc.top); 2717 assertEquals(mTextView.getLayout().getLineBottom(0), rc.bottom); 2718 2719 mActivityRule.runOnUiThread(() -> { 2720 final SpannableString text = new SpannableString(mTextView.getText()); 2721 mTextView.setTextIsSelectable(true); 2722 mTextView.setText(text); 2723 // cross the "\n" and two lines 2724 Selection.setSelection((Spannable) mTextView.getText(), 2, 10); 2725 }); 2726 mInstrumentation.waitForIdleSync(); 2727 mTextView.getFocusedRect(rc); 2728 Path path = new Path(); 2729 mTextView.getLayout().getSelectionPath(2, 10, path); 2730 RectF rcf = new RectF(); 2731 path.computeBounds(rcf, true); 2732 assertNotNull(mTextView.getLayout()); 2733 assertEquals(rcf.left - 1, (float) rc.left, 0.0f); 2734 assertEquals(rcf.right + 1, (float) rc.right, 0.0f); 2735 assertEquals(mTextView.getLayout().getLineTop(0), rc.top); 2736 assertEquals(mTextView.getLayout().getLineBottom(1), rc.bottom); 2737 2738 // Exception 2739 try { 2740 mTextView.getFocusedRect(null); 2741 fail("Should throw NullPointerException!"); 2742 } catch (NullPointerException e) { 2743 } 2744 } 2745 2746 @Test 2747 public void testGetLineCount() throws Throwable { 2748 mActivityRule.runOnUiThread(() -> mTextView = findTextView(R.id.textview_text)); 2749 mInstrumentation.waitForIdleSync(); 2750 // this is an one line text with default setting. 2751 assertEquals(1, mTextView.getLineCount()); 2752 2753 // make it multi-lines 2754 setMaxWidth(mTextView.getWidth() / 3); 2755 assertTrue(1 < mTextView.getLineCount()); 2756 2757 // make it to an one line 2758 setMaxWidth(Integer.MAX_VALUE); 2759 assertEquals(1, mTextView.getLineCount()); 2760 2761 // set min lines don't effect the lines count for actual text. 2762 setMinLines(12); 2763 assertEquals(1, mTextView.getLineCount()); 2764 2765 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 2766 mInstrumentation.waitForIdleSync(); 2767 // the internal Layout has not been built. 2768 assertNull(mTextView.getLayout()); 2769 assertEquals(0, mTextView.getLineCount()); 2770 } 2771 2772 @Test 2773 public void testGetLineBounds() throws Throwable { 2774 Rect rc = new Rect(); 2775 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 2776 mInstrumentation.waitForIdleSync(); 2777 assertEquals(0, mTextView.getLineBounds(0, null)); 2778 2779 assertEquals(0, mTextView.getLineBounds(0, rc)); 2780 assertEquals(0, rc.left); 2781 assertEquals(0, rc.right); 2782 assertEquals(0, rc.top); 2783 assertEquals(0, rc.bottom); 2784 2785 mTextView = findTextView(R.id.textview_text); 2786 assertEquals(mTextView.getBaseline(), mTextView.getLineBounds(0, null)); 2787 2788 assertEquals(mTextView.getBaseline(), mTextView.getLineBounds(0, rc)); 2789 assertEquals(0, rc.left); 2790 assertEquals(mTextView.getWidth(), rc.right); 2791 assertEquals(0, rc.top); 2792 assertEquals(mTextView.getHeight(), rc.bottom); 2793 2794 mActivityRule.runOnUiThread(() -> { 2795 mTextView.setPadding(1, 2, 3, 4); 2796 mTextView.setGravity(Gravity.BOTTOM); 2797 }); 2798 mInstrumentation.waitForIdleSync(); 2799 assertEquals(mTextView.getBaseline(), mTextView.getLineBounds(0, rc)); 2800 assertEquals(mTextView.getTotalPaddingLeft(), rc.left); 2801 assertEquals(mTextView.getWidth() - mTextView.getTotalPaddingRight(), rc.right); 2802 assertEquals(mTextView.getTotalPaddingTop(), rc.top); 2803 assertEquals(mTextView.getHeight() - mTextView.getTotalPaddingBottom(), rc.bottom); 2804 } 2805 2806 @Test 2807 public void testGetBaseLine() throws Throwable { 2808 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 2809 mInstrumentation.waitForIdleSync(); 2810 assertEquals(-1, mTextView.getBaseline()); 2811 2812 mTextView = findTextView(R.id.textview_text); 2813 assertEquals(mTextView.getLayout().getLineBaseline(0), mTextView.getBaseline()); 2814 2815 mActivityRule.runOnUiThread(() -> { 2816 mTextView.setPadding(1, 2, 3, 4); 2817 mTextView.setGravity(Gravity.BOTTOM); 2818 }); 2819 mInstrumentation.waitForIdleSync(); 2820 int expected = mTextView.getTotalPaddingTop() + mTextView.getLayout().getLineBaseline(0); 2821 assertEquals(expected, mTextView.getBaseline()); 2822 } 2823 2824 @Test 2825 public void testPressKey() throws Throwable { 2826 initTextViewForTypingOnUiThread(); 2827 2828 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a"); 2829 assertEquals("a", mTextView.getText().toString()); 2830 CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "b"); 2831 assertEquals("ab", mTextView.getText().toString()); 2832 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 2833 assertEquals("a", mTextView.getText().toString()); 2834 } 2835 2836 @Test 2837 public void testKeyNavigation() throws Throwable { 2838 initTextViewForTypingOnUiThread(); 2839 mActivityRule.runOnUiThread(() -> { 2840 mActivity.findViewById(R.id.textview_singleLine).setFocusableInTouchMode(true); 2841 mActivity.findViewById(R.id.textview_text_two_lines).setFocusableInTouchMode(true); 2842 mTextView.setMovementMethod(ArrowKeyMovementMethod.getInstance()); 2843 mTextView.setText("abc"); 2844 mTextView.setFocusableInTouchMode(true); 2845 }); 2846 2847 mTextView.requestFocus(); 2848 mInstrumentation.waitForIdleSync(); 2849 assertTrue(mTextView.isFocused()); 2850 2851 // Pure-keyboard arrows should not cause focus to leave the textfield 2852 CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP); 2853 mInstrumentation.waitForIdleSync(); 2854 assertTrue(mTextView.isFocused()); 2855 2856 // Non-pure-keyboard arrows, however, should. 2857 int dpadRemote = InputDevice.SOURCE_DPAD | InputDevice.SOURCE_KEYBOARD; 2858 sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP, dpadRemote); 2859 mInstrumentation.waitForIdleSync(); 2860 assertFalse(mTextView.isFocused()); 2861 2862 sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_DOWN, dpadRemote); 2863 mInstrumentation.waitForIdleSync(); 2864 assertTrue(mTextView.isFocused()); 2865 2866 // Tab should 2867 CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_TAB); 2868 mInstrumentation.waitForIdleSync(); 2869 assertFalse(mTextView.isFocused()); 2870 } 2871 2872 private void sendSourceKeyDownUp(Instrumentation instrumentation, View targetView, int key, 2873 int source) { 2874 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, key); 2875 event.setSource(source); 2876 CtsKeyEventUtil.sendKey(instrumentation, targetView, event); 2877 event = new KeyEvent(KeyEvent.ACTION_UP, key); 2878 event.setSource(source); 2879 CtsKeyEventUtil.sendKey(instrumentation, targetView, event); 2880 } 2881 2882 @Test 2883 public void testSetIncludeFontPadding() throws Throwable { 2884 mTextView = findTextView(R.id.textview_text); 2885 assertTrue(mTextView.getIncludeFontPadding()); 2886 mActivityRule.runOnUiThread(() -> { 2887 mTextView.setWidth(mTextView.getWidth() / 3); 2888 mTextView.setPadding(1, 2, 3, 4); 2889 mTextView.setGravity(Gravity.BOTTOM); 2890 }); 2891 mInstrumentation.waitForIdleSync(); 2892 2893 int oldHeight = mTextView.getHeight(); 2894 mActivityRule.runOnUiThread(() -> mTextView.setIncludeFontPadding(false)); 2895 mInstrumentation.waitForIdleSync(); 2896 2897 assertTrue(mTextView.getHeight() < oldHeight); 2898 assertFalse(mTextView.getIncludeFontPadding()); 2899 } 2900 2901 @UiThreadTest 2902 @Test 2903 public void testScroll() { 2904 mTextView = new TextView(mActivity); 2905 2906 assertEquals(0, mTextView.getScrollX()); 2907 assertEquals(0, mTextView.getScrollY()); 2908 2909 //don't set the Scroller, nothing changed. 2910 mTextView.computeScroll(); 2911 assertEquals(0, mTextView.getScrollX()); 2912 assertEquals(0, mTextView.getScrollY()); 2913 2914 //set the Scroller 2915 Scroller s = new Scroller(mActivity); 2916 assertNotNull(s); 2917 s.startScroll(0, 0, 320, 480, 0); 2918 s.abortAnimation(); 2919 s.forceFinished(false); 2920 mTextView.setScroller(s); 2921 2922 mTextView.computeScroll(); 2923 assertEquals(320, mTextView.getScrollX()); 2924 assertEquals(480, mTextView.getScrollY()); 2925 } 2926 2927 @Test 2928 public void testDebug() throws Throwable { 2929 mActivityRule.runOnUiThread(() -> { 2930 mTextView = new TextView(mActivity); 2931 mTextView.debug(0); 2932 mTextView.setText("Hello!"); 2933 }); 2934 mInstrumentation.waitForIdleSync(); 2935 2936 layout(mTextView); 2937 mTextView.debug(1); 2938 } 2939 2940 @UiThreadTest 2941 @Test 2942 public void testSelection() throws Throwable { 2943 mTextView = new TextView(mActivity); 2944 String text = "This is the content"; 2945 mTextView.setText(text, BufferType.SPANNABLE); 2946 assertFalse(mTextView.hasSelection()); 2947 2948 Selection.selectAll((Spannable) mTextView.getText()); 2949 assertEquals(0, mTextView.getSelectionStart()); 2950 assertEquals(text.length(), mTextView.getSelectionEnd()); 2951 assertTrue(mTextView.hasSelection()); 2952 2953 int selectionStart = 5; 2954 int selectionEnd = 7; 2955 Selection.setSelection((Spannable) mTextView.getText(), selectionStart); 2956 assertEquals(selectionStart, mTextView.getSelectionStart()); 2957 assertEquals(selectionStart, mTextView.getSelectionEnd()); 2958 assertFalse(mTextView.hasSelection()); 2959 2960 Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd); 2961 assertEquals(selectionStart, mTextView.getSelectionStart()); 2962 assertEquals(selectionEnd, mTextView.getSelectionEnd()); 2963 assertTrue(mTextView.hasSelection()); 2964 } 2965 2966 @Test 2967 public void testOnSelectionChangedIsTriggeredWhenSelectionChanges() throws Throwable { 2968 final String text = "any text"; 2969 mActivityRule.runOnUiThread(() -> mTextView = spy(new MockTextView(mActivity))); 2970 mInstrumentation.waitForIdleSync(); 2971 mTextView.setText(text, BufferType.SPANNABLE); 2972 2973 // assert that there is currently no selection 2974 assertFalse(mTextView.hasSelection()); 2975 2976 // select all 2977 Selection.selectAll((Spannable) mTextView.getText()); 2978 // After selectAll OnSelectionChanged should have been called 2979 ((MockTextView) verify(mTextView, times(1))).onSelectionChanged(0, text.length()); 2980 2981 reset(mTextView); 2982 // change selection 2983 Selection.setSelection((Spannable) mTextView.getText(), 1, 5); 2984 ((MockTextView) verify(mTextView, times(1))).onSelectionChanged(1, 5); 2985 2986 reset(mTextView); 2987 // clear selection 2988 Selection.removeSelection((Spannable) mTextView.getText()); 2989 ((MockTextView) verify(mTextView, times(1))).onSelectionChanged(-1, -1); 2990 } 2991 2992 @UiThreadTest 2993 @Test 2994 public void testAccessEllipsize() { 2995 mActivity.setContentView(R.layout.textview_ellipsize); 2996 2997 mTextView = findTextView(R.id.ellipsize_default); 2998 assertNull(mTextView.getEllipsize()); 2999 3000 mTextView = findTextView(R.id.ellipsize_none); 3001 assertNull(mTextView.getEllipsize()); 3002 3003 mTextView = findTextView(R.id.ellipsize_start); 3004 assertSame(TruncateAt.START, mTextView.getEllipsize()); 3005 3006 mTextView = findTextView(R.id.ellipsize_middle); 3007 assertSame(TruncateAt.MIDDLE, mTextView.getEllipsize()); 3008 3009 mTextView = findTextView(R.id.ellipsize_end); 3010 assertSame(TruncateAt.END, mTextView.getEllipsize()); 3011 3012 mTextView.setEllipsize(TextUtils.TruncateAt.START); 3013 assertSame(TextUtils.TruncateAt.START, mTextView.getEllipsize()); 3014 3015 mTextView.setEllipsize(TextUtils.TruncateAt.MIDDLE); 3016 assertSame(TextUtils.TruncateAt.MIDDLE, mTextView.getEllipsize()); 3017 3018 mTextView.setEllipsize(TextUtils.TruncateAt.END); 3019 assertSame(TextUtils.TruncateAt.END, mTextView.getEllipsize()); 3020 3021 mTextView.setEllipsize(null); 3022 assertNull(mTextView.getEllipsize()); 3023 3024 mTextView.setWidth(10); 3025 mTextView.setEllipsize(TextUtils.TruncateAt.START); 3026 mTextView.setText("ThisIsAVeryLongVeryLongVeryLongVeryLongVeryLongWord"); 3027 mTextView.invalidate(); 3028 3029 assertSame(TextUtils.TruncateAt.START, mTextView.getEllipsize()); 3030 // there is no method to check if '...yLongVeryLongWord' is painted in the screen. 3031 } 3032 3033 @Test 3034 public void testEllipsizeAndMaxLinesForSingleLine() throws Throwable { 3035 // no maxline or ellipsize set, single line text 3036 final TextView tvNoMaxLine = new TextView(mActivity); 3037 tvNoMaxLine.setLineSpacing(0, 1.5f); 3038 tvNoMaxLine.setText("a"); 3039 3040 // maxline set, no ellipsize, text with two lines 3041 final TextView tvEllipsizeNone = new TextView(mActivity); 3042 tvEllipsizeNone.setMaxLines(1); 3043 tvEllipsizeNone.setLineSpacing(0, 1.5f); 3044 tvEllipsizeNone.setText("a\na"); 3045 3046 // maxline set, ellipsize end, text with two lines 3047 final TextView tvEllipsizeEnd = new TextView(mActivity); 3048 tvEllipsizeEnd.setEllipsize(TruncateAt.END); 3049 tvEllipsizeEnd.setMaxLines(1); 3050 tvEllipsizeEnd.setLineSpacing(0, 1.5f); 3051 tvEllipsizeEnd.setText("a\na"); 3052 3053 final FrameLayout layout = new FrameLayout(mActivity); 3054 ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 3055 ViewGroup.LayoutParams.WRAP_CONTENT, 3056 ViewGroup.LayoutParams.WRAP_CONTENT); 3057 layout.addView(tvEllipsizeEnd, layoutParams); 3058 layout.addView(tvEllipsizeNone, layoutParams); 3059 layout.addView(tvNoMaxLine, layoutParams); 3060 3061 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout, 3062 new ViewGroup.LayoutParams( 3063 ViewGroup.LayoutParams.MATCH_PARENT, 3064 ViewGroup.LayoutParams.MATCH_PARENT))); 3065 mInstrumentation.waitForIdleSync(); 3066 3067 assertEquals(tvEllipsizeEnd.getHeight(), tvEllipsizeNone.getHeight()); 3068 3069 assertEquals(tvEllipsizeEnd.getHeight(), tvNoMaxLine.getHeight()); 3070 3071 assertEquals(tvEllipsizeEnd.getLayout().getLineBaseline(0), 3072 tvEllipsizeNone.getLayout().getLineBaseline(0)); 3073 3074 assertEquals(tvEllipsizeEnd.getLayout().getLineBaseline(0), 3075 tvNoMaxLine.getLayout().getLineBaseline(0)); 3076 } 3077 3078 @Test 3079 public void testEllipsizeAndMaxLinesForMultiLine() throws Throwable { 3080 // no maxline, no ellipsize, text with two lines 3081 final TextView tvNoMaxLine = new TextView(mActivity); 3082 tvNoMaxLine.setLineSpacing(0, 1.5f); 3083 tvNoMaxLine.setText("a\na"); 3084 3085 // maxline set, no ellipsize, text with three lines 3086 final TextView tvEllipsizeNone = new TextView(mActivity); 3087 tvEllipsizeNone.setMaxLines(2); 3088 tvEllipsizeNone.setLineSpacing(0, 1.5f); 3089 tvEllipsizeNone.setText("a\na\na"); 3090 3091 // maxline set, ellipsize end, text with three lines 3092 final TextView tvEllipsizeEnd = new TextView(mActivity); 3093 tvEllipsizeEnd.setEllipsize(TruncateAt.END); 3094 tvEllipsizeEnd.setMaxLines(2); 3095 tvEllipsizeEnd.setLineSpacing(0, 1.5f); 3096 tvEllipsizeEnd.setText("a\na\na"); 3097 3098 final FrameLayout layout = new FrameLayout(mActivity); 3099 ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 3100 ViewGroup.LayoutParams.WRAP_CONTENT, 3101 ViewGroup.LayoutParams.WRAP_CONTENT); 3102 3103 layout.addView(tvNoMaxLine, layoutParams); 3104 layout.addView(tvEllipsizeEnd, layoutParams); 3105 layout.addView(tvEllipsizeNone, layoutParams); 3106 3107 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout, 3108 new ViewGroup.LayoutParams( 3109 ViewGroup.LayoutParams.MATCH_PARENT, 3110 ViewGroup.LayoutParams.MATCH_PARENT))); 3111 mInstrumentation.waitForIdleSync(); 3112 3113 assertEquals(tvEllipsizeEnd.getHeight(), tvEllipsizeNone.getHeight()); 3114 3115 assertEquals(tvEllipsizeEnd.getHeight(), tvNoMaxLine.getHeight()); 3116 3117 for (int i = 0; i < tvEllipsizeEnd.getLineCount(); i++) { 3118 assertEquals("Should have the same baseline for line " + i, 3119 tvEllipsizeEnd.getLayout().getLineBaseline(i), 3120 tvEllipsizeNone.getLayout().getLineBaseline(i)); 3121 3122 assertEquals("Should have the same baseline for line " + i, 3123 tvEllipsizeEnd.getLayout().getLineBaseline(i), 3124 tvNoMaxLine.getLayout().getLineBaseline(i)); 3125 } 3126 } 3127 3128 @Test 3129 public void testEllipsizeAndMaxLinesForHint() throws Throwable { 3130 // no maxline, no ellipsize, hint with two lines 3131 final TextView tvTwoLines = new TextView(mActivity); 3132 tvTwoLines.setLineSpacing(0, 1.5f); 3133 tvTwoLines.setHint("a\na"); 3134 3135 // no maxline, no ellipsize, hint with three lines 3136 final TextView tvThreeLines = new TextView(mActivity); 3137 tvThreeLines.setLineSpacing(0, 1.5f); 3138 tvThreeLines.setHint("a\na\na"); 3139 3140 // maxline set, ellipsize end, hint with three lines 3141 final TextView tvEllipsizeEnd = new TextView(mActivity); 3142 tvEllipsizeEnd.setEllipsize(TruncateAt.END); 3143 tvEllipsizeEnd.setMaxLines(2); 3144 tvEllipsizeEnd.setLineSpacing(0, 1.5f); 3145 tvEllipsizeEnd.setHint("a\na\na"); 3146 3147 // maxline set, no ellipsize, hint with three lines 3148 final TextView tvEllipsizeNone = new TextView(mActivity); 3149 tvEllipsizeNone.setMaxLines(2); 3150 tvEllipsizeNone.setLineSpacing(0, 1.5f); 3151 tvEllipsizeNone.setHint("a\na\na"); 3152 3153 final FrameLayout layout = new FrameLayout(mActivity); 3154 ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 3155 ViewGroup.LayoutParams.WRAP_CONTENT, 3156 ViewGroup.LayoutParams.WRAP_CONTENT); 3157 3158 layout.addView(tvTwoLines, layoutParams); 3159 layout.addView(tvEllipsizeEnd, layoutParams); 3160 layout.addView(tvEllipsizeNone, layoutParams); 3161 layout.addView(tvThreeLines, layoutParams); 3162 3163 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout, 3164 new ViewGroup.LayoutParams( 3165 ViewGroup.LayoutParams.MATCH_PARENT, 3166 ViewGroup.LayoutParams.MATCH_PARENT))); 3167 mInstrumentation.waitForIdleSync(); 3168 3169 assertEquals("Non-ellipsized hint should not crop text at maxLines", 3170 tvThreeLines.getHeight(), tvEllipsizeNone.getHeight()); 3171 3172 assertEquals("Ellipsized hint should crop text at maxLines", 3173 tvTwoLines.getHeight(), tvEllipsizeEnd.getHeight()); 3174 } 3175 3176 @UiThreadTest 3177 @Test 3178 public void testAccessCursorVisible() { 3179 mTextView = new TextView(mActivity); 3180 3181 mTextView.setCursorVisible(true); 3182 assertTrue(mTextView.isCursorVisible()); 3183 mTextView.setCursorVisible(false); 3184 assertFalse(mTextView.isCursorVisible()); 3185 } 3186 3187 @UiThreadTest 3188 @Test 3189 public void testPerformLongClick() { 3190 mTextView = findTextView(R.id.textview_text); 3191 mTextView.setText("This is content"); 3192 3193 View.OnLongClickListener mockOnLongClickListener = mock(View.OnLongClickListener.class); 3194 when(mockOnLongClickListener.onLongClick(any(View.class))).thenReturn(Boolean.TRUE); 3195 3196 View.OnCreateContextMenuListener mockOnCreateContextMenuListener = 3197 mock(View.OnCreateContextMenuListener.class); 3198 doAnswer((InvocationOnMock invocation) -> { 3199 ((ContextMenu) invocation.getArguments() [0]).add("menu item"); 3200 return null; 3201 }).when(mockOnCreateContextMenuListener).onCreateContextMenu( 3202 any(ContextMenu.class), any(View.class), any()); 3203 3204 mTextView.setOnLongClickListener(mockOnLongClickListener); 3205 mTextView.setOnCreateContextMenuListener(mockOnCreateContextMenuListener); 3206 assertTrue(mTextView.performLongClick()); 3207 verify(mockOnLongClickListener, times(1)).onLongClick(mTextView); 3208 verifyZeroInteractions(mockOnCreateContextMenuListener); 3209 3210 reset(mockOnLongClickListener); 3211 when(mockOnLongClickListener.onLongClick(any(View.class))).thenReturn(Boolean.FALSE); 3212 assertTrue(mTextView.performLongClick()); 3213 verify(mockOnLongClickListener, times(1)).onLongClick(mTextView); 3214 verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu( 3215 any(ContextMenu.class), eq(mTextView), any()); 3216 3217 reset(mockOnCreateContextMenuListener); 3218 mTextView.setOnLongClickListener(null); 3219 doNothing().when(mockOnCreateContextMenuListener).onCreateContextMenu( 3220 any(ContextMenu.class), any(View.class), any()); 3221 assertFalse(mTextView.performLongClick()); 3222 verifyNoMoreInteractions(mockOnLongClickListener); 3223 verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu( 3224 any(ContextMenu.class), eq(mTextView), any()); 3225 } 3226 3227 @UiThreadTest 3228 @Test 3229 public void testTextAttr() { 3230 mTextView = findTextView(R.id.textview_textAttr); 3231 // getText 3232 assertEquals(mActivity.getString(R.string.text_view_hello), mTextView.getText().toString()); 3233 3234 // getCurrentTextColor 3235 assertEquals(mActivity.getResources().getColor(R.drawable.black), 3236 mTextView.getCurrentTextColor()); 3237 assertEquals(mActivity.getResources().getColor(R.drawable.red), 3238 mTextView.getCurrentHintTextColor()); 3239 assertEquals(mActivity.getResources().getColor(R.drawable.red), 3240 mTextView.getHintTextColors().getDefaultColor()); 3241 assertEquals(mActivity.getResources().getColor(R.drawable.blue), 3242 mTextView.getLinkTextColors().getDefaultColor()); 3243 3244 // getTextScaleX 3245 assertEquals(1.2f, mTextView.getTextScaleX(), 0.01f); 3246 3247 // setTextScaleX 3248 mTextView.setTextScaleX(2.4f); 3249 assertEquals(2.4f, mTextView.getTextScaleX(), 0.01f); 3250 3251 mTextView.setTextScaleX(0f); 3252 assertEquals(0f, mTextView.getTextScaleX(), 0.01f); 3253 3254 mTextView.setTextScaleX(- 2.4f); 3255 assertEquals(- 2.4f, mTextView.getTextScaleX(), 0.01f); 3256 3257 // getTextSize 3258 assertEquals(20f, mTextView.getTextSize(), 0.01f); 3259 3260 // getTypeface 3261 // getTypeface will be null if android:typeface is set to normal, 3262 // and android:style is not set or is set to normal, and 3263 // android:fontFamily is not set 3264 assertNull(mTextView.getTypeface()); 3265 3266 mTextView.setTypeface(Typeface.DEFAULT); 3267 assertSame(Typeface.DEFAULT, mTextView.getTypeface()); 3268 // null type face 3269 mTextView.setTypeface(null); 3270 assertNull(mTextView.getTypeface()); 3271 3272 // default type face, bold style, note: the type face will be changed 3273 // after call set method 3274 mTextView.setTypeface(Typeface.DEFAULT, Typeface.BOLD); 3275 assertSame(Typeface.BOLD, mTextView.getTypeface().getStyle()); 3276 3277 // null type face, BOLD style 3278 mTextView.setTypeface(null, Typeface.BOLD); 3279 assertSame(Typeface.BOLD, mTextView.getTypeface().getStyle()); 3280 3281 // old type face, null style 3282 mTextView.setTypeface(Typeface.DEFAULT, 0); 3283 assertEquals(Typeface.NORMAL, mTextView.getTypeface().getStyle()); 3284 } 3285 3286 @UiThreadTest 3287 @Test 3288 public void testAppend() { 3289 mTextView = new TextView(mActivity); 3290 3291 // 1: check the original length, should be blank as initialised. 3292 assertEquals(0, mTextView.getText().length()); 3293 3294 // 2: append a string use append(CharSquence) into the original blank 3295 // buffer, check the content. And upgrading it to BufferType.EDITABLE if it was 3296 // not already editable. 3297 assertFalse(mTextView.getText() instanceof Editable); 3298 mTextView.append("Append."); 3299 assertEquals("Append.", mTextView.getText().toString()); 3300 assertTrue(mTextView.getText() instanceof Editable); 3301 3302 // 3: append a string from 0~3. 3303 mTextView.append("Append", 0, 3); 3304 assertEquals("Append.App", mTextView.getText().toString()); 3305 assertTrue(mTextView.getText() instanceof Editable); 3306 3307 // 4: append a string from 0~0, nothing will be append as expected. 3308 mTextView.append("Append", 0, 0); 3309 assertEquals("Append.App", mTextView.getText().toString()); 3310 assertTrue(mTextView.getText() instanceof Editable); 3311 3312 // 5: append a string from -3~3. check the wrong left edge. 3313 try { 3314 mTextView.append("Append", -3, 3); 3315 fail("Should throw StringIndexOutOfBoundsException"); 3316 } catch (StringIndexOutOfBoundsException e) { 3317 } 3318 3319 // 6: append a string from 3~10. check the wrong right edge. 3320 try { 3321 mTextView.append("Append", 3, 10); 3322 fail("Should throw StringIndexOutOfBoundsException"); 3323 } catch (StringIndexOutOfBoundsException e) { 3324 } 3325 3326 // 7: append a null string. 3327 try { 3328 mTextView.append(null); 3329 fail("Should throw NullPointerException"); 3330 } catch (NullPointerException e) { 3331 } 3332 } 3333 3334 @UiThreadTest 3335 @Test 3336 public void testAppend_doesNotAddLinksWhenAppendedTextDoesNotContainLinks() { 3337 mTextView = new TextView(mActivity); 3338 mTextView.setAutoLinkMask(Linkify.ALL); 3339 mTextView.setText("text without URL"); 3340 3341 mTextView.append(" another text without URL"); 3342 3343 Spannable text = (Spannable) mTextView.getText(); 3344 URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class); 3345 assertEquals("URLSpan count should be zero", 0, urlSpans.length); 3346 assertEquals("text without URL another text without URL", text.toString()); 3347 } 3348 3349 @UiThreadTest 3350 @Test 3351 public void testAppend_doesNotAddLinksWhenAutoLinkIsNotEnabled() { 3352 mTextView = new TextView(mActivity); 3353 mTextView.setText("text without URL"); 3354 3355 mTextView.append(" text with URL http://android.com"); 3356 3357 Spannable text = (Spannable) mTextView.getText(); 3358 URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class); 3359 assertEquals("URLSpan count should be zero", 0, urlSpans.length); 3360 assertEquals("text without URL text with URL http://android.com", text.toString()); 3361 } 3362 3363 @UiThreadTest 3364 @Test 3365 public void testAppend_addsLinksWhenAutoLinkIsEnabled() { 3366 mTextView = new TextView(mActivity); 3367 mTextView.setAutoLinkMask(Linkify.ALL); 3368 mTextView.setText("text without URL"); 3369 3370 mTextView.append(" text with URL http://android.com"); 3371 3372 Spannable text = (Spannable) mTextView.getText(); 3373 URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class); 3374 assertEquals("URLSpan count should be one after appending a URL", 1, urlSpans.length); 3375 assertEquals("URLSpan URL should be same as the appended URL", 3376 urlSpans[0].getURL(), "http://android.com"); 3377 assertEquals("text without URL text with URL http://android.com", text.toString()); 3378 } 3379 3380 @UiThreadTest 3381 @Test 3382 public void testAppend_addsLinksEvenWhenThereAreUrlsSetBefore() { 3383 mTextView = new TextView(mActivity); 3384 mTextView.setAutoLinkMask(Linkify.ALL); 3385 mTextView.setText("text with URL http://android.com/before"); 3386 3387 mTextView.append(" text with URL http://android.com"); 3388 3389 Spannable text = (Spannable) mTextView.getText(); 3390 URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class); 3391 assertEquals("URLSpan count should be two after appending another URL", 2, urlSpans.length); 3392 assertEquals("First URLSpan URL should be same", 3393 urlSpans[0].getURL(), "http://android.com/before"); 3394 assertEquals("URLSpan URL should be same as the appended URL", 3395 urlSpans[1].getURL(), "http://android.com"); 3396 assertEquals("text with URL http://android.com/before text with URL http://android.com", 3397 text.toString()); 3398 } 3399 3400 @UiThreadTest 3401 @Test 3402 public void testAppend_setsMovementMethodWhenTextContainsUrlAndAutoLinkIsEnabled() { 3403 mTextView = new TextView(mActivity); 3404 mTextView.setAutoLinkMask(Linkify.ALL); 3405 mTextView.setText("text without a URL"); 3406 3407 mTextView.append(" text with a url: http://android.com"); 3408 3409 assertNotNull("MovementMethod should not be null when text contains url", 3410 mTextView.getMovementMethod()); 3411 assertTrue("MovementMethod should be instance of LinkMovementMethod when text contains url", 3412 mTextView.getMovementMethod() instanceof LinkMovementMethod); 3413 } 3414 3415 @UiThreadTest 3416 @Test 3417 public void testAppend_addsLinksWhenTextIsSpannableAndContainsUrlAndAutoLinkIsEnabled() { 3418 mTextView = new TextView(mActivity); 3419 mTextView.setAutoLinkMask(Linkify.ALL); 3420 mTextView.setText("text without a URL"); 3421 3422 mTextView.append(new SpannableString(" text with a url: http://android.com")); 3423 3424 Spannable text = (Spannable) mTextView.getText(); 3425 URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class); 3426 assertEquals("URLSpan count should be one after appending a URL", 1, urlSpans.length); 3427 assertEquals("URLSpan URL should be same as the appended URL", 3428 urlSpans[0].getURL(), "http://android.com"); 3429 } 3430 3431 @UiThreadTest 3432 @Test 3433 public void testAppend_addsLinkIfAppendedTextCompletesPartialUrlAtTheEndOfExistingText() { 3434 mTextView = new TextView(mActivity); 3435 mTextView.setAutoLinkMask(Linkify.ALL); 3436 mTextView.setText("text with a partial url android."); 3437 3438 mTextView.append("com"); 3439 3440 Spannable text = (Spannable) mTextView.getText(); 3441 URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class); 3442 assertEquals("URLSpan count should be one after appending to partial URL", 3443 1, urlSpans.length); 3444 assertEquals("URLSpan URL should be same as the appended URL", 3445 urlSpans[0].getURL(), "http://android.com"); 3446 } 3447 3448 @UiThreadTest 3449 @Test 3450 public void testAppend_addsLinkIfAppendedTextUpdatesUrlAtTheEndOfExistingText() { 3451 mTextView = new TextView(mActivity); 3452 mTextView.setAutoLinkMask(Linkify.ALL); 3453 mTextView.setText("text with a url http://android.com"); 3454 3455 mTextView.append("/textview"); 3456 3457 Spannable text = (Spannable) mTextView.getText(); 3458 URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class); 3459 assertEquals("URLSpan count should still be one after extending a URL", 1, urlSpans.length); 3460 assertEquals("URLSpan URL should be same as the new URL", 3461 urlSpans[0].getURL(), "http://android.com/textview"); 3462 } 3463 3464 @UiThreadTest 3465 @Test 3466 public void testGetLetterSpacing_returnsValueThatWasSet() { 3467 mTextView = new TextView(mActivity); 3468 mTextView.setLetterSpacing(2f); 3469 assertEquals("getLetterSpacing should return the value that was set", 3470 2f, mTextView.getLetterSpacing(), 0.0f); 3471 } 3472 3473 @Test 3474 public void testSetLetterSpacingChangesTextWidth() throws Throwable { 3475 mActivityRule.runOnUiThread(() -> { 3476 mTextView = new TextView(mActivity); 3477 mTextView.setText("aa"); 3478 mTextView.setLetterSpacing(0f); 3479 mTextView.setTextSize(8f); 3480 }); 3481 mInstrumentation.waitForIdleSync(); 3482 3483 final FrameLayout layout = new FrameLayout(mActivity); 3484 final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 3485 ViewGroup.LayoutParams.WRAP_CONTENT, 3486 ViewGroup.LayoutParams.MATCH_PARENT); 3487 layout.addView(mTextView, layoutParams); 3488 layout.setLayoutParams(layoutParams); 3489 3490 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout)); 3491 mInstrumentation.waitForIdleSync(); 3492 3493 // measure text with zero letter spacing 3494 final float zeroSpacing = mTextView.getLayout().getLineWidth(0); 3495 3496 mActivityRule.runOnUiThread(() -> mTextView.setLetterSpacing(1f)); 3497 mInstrumentation.waitForIdleSync(); 3498 3499 // measure text with single letter spacing 3500 final float singleSpacing = mTextView.getLayout().getLineWidth(0); 3501 3502 mActivityRule.runOnUiThread(() -> mTextView.setLetterSpacing(2f)); 3503 mInstrumentation.waitForIdleSync(); 3504 3505 // measure text with double letter spacing 3506 final float doubleSpacing = mTextView.getLayout().getLineWidth(0); 3507 3508 assertEquals("Double spacing should have two times the spacing of single spacing", 3509 doubleSpacing - zeroSpacing, 2f * (singleSpacing - zeroSpacing), 1f); 3510 } 3511 3512 @UiThreadTest 3513 @Test 3514 public void testGetFontFeatureSettings_returnsValueThatWasSet() { 3515 mTextView = new TextView(mActivity); 3516 mTextView.setFontFeatureSettings("\"smcp\" on"); 3517 assertEquals("getFontFeatureSettings should return the value that was set", 3518 "\"smcp\" on", mTextView.getFontFeatureSettings()); 3519 } 3520 3521 @UiThreadTest 3522 @Test 3523 public void testSetGetFontVariationSettings() { 3524 mTextView = new TextView(mActivity); 3525 Context context = InstrumentationRegistry.getTargetContext(); 3526 Typeface typeface = Typeface.createFromAsset(context.getAssets(), "multiaxis.ttf"); 3527 mTextView.setTypeface(typeface); 3528 3529 // multiaxis.ttf supports "aaaa", "BBBB", "a b ", " C D" axes. 3530 3531 // The default variation settings should be null. 3532 assertNull(mTextView.getFontVariationSettings()); 3533 3534 final String[] invalidFormatSettings = { 3535 "invalid syntax", 3536 "'aaa' 1.0", // tag is not 4 ascii chars 3537 }; 3538 for (String settings : invalidFormatSettings) { 3539 try { 3540 mTextView.setFontVariationSettings(settings); 3541 fail(); 3542 } catch (IllegalArgumentException e) { 3543 // pass. 3544 } 3545 assertNull("Must not change settings for " + settings, 3546 mTextView.getFontVariationSettings()); 3547 } 3548 3549 final String[] nonEffectiveSettings = { 3550 "'bbbb' 1.0", // unsupported tag 3551 "' ' 1.0", // unsupported tag 3552 "'AAAA' 0.7", // unsupported tag (case sensitive) 3553 "' a b' 1.3", // unsupported tag (white space should not be ignored) 3554 "'C D ' 1.3", // unsupported tag (white space should not be ignored) 3555 "'bbbb' 1.0, 'cccc' 2.0", // none of them are supported. 3556 }; 3557 3558 for (String notEffectiveSetting : nonEffectiveSettings) { 3559 assertFalse("Must return false for " + notEffectiveSetting, 3560 mTextView.setFontVariationSettings(notEffectiveSetting)); 3561 assertNull("Must not change settings for " + notEffectiveSetting, 3562 mTextView.getFontVariationSettings()); 3563 } 3564 3565 String retainSettings = "'aaaa' 1.0"; 3566 assertTrue(mTextView.setFontVariationSettings(retainSettings)); 3567 for (String notEffectiveSetting : nonEffectiveSettings) { 3568 assertFalse(mTextView.setFontVariationSettings(notEffectiveSetting)); 3569 assertEquals("Must not change settings for " + notEffectiveSetting, 3570 retainSettings, mTextView.getFontVariationSettings()); 3571 } 3572 3573 // At least one axis is supported, the settings should be applied. 3574 final String[] effectiveSettings = { 3575 "'aaaa' 1.0", // supported tag 3576 "'a b ' .7", // supported tag (contains whitespace) 3577 "'aaaa' 1.0, 'BBBB' 0.5", // both are supported 3578 "'aaaa' 1.0, ' C D' 0.5", // both are supported 3579 "'aaaa' 1.0, 'bbbb' 0.4", // 'bbbb' is unspported. 3580 }; 3581 3582 for (String effectiveSetting : effectiveSettings) { 3583 assertTrue(mTextView.setFontVariationSettings(effectiveSetting)); 3584 assertEquals(effectiveSetting, mTextView.getFontVariationSettings()); 3585 } 3586 3587 mTextView.setFontVariationSettings(""); 3588 assertNull(mTextView.getFontVariationSettings()); 3589 } 3590 3591 @Test 3592 public void testGetOffsetForPositionSingleLineLtr() throws Throwable { 3593 // asserts getOffsetPosition returns correct values for a single line LTR text 3594 final String text = "aaaaa"; 3595 3596 mActivityRule.runOnUiThread(() -> { 3597 mTextView = new TextView(mActivity); 3598 mTextView.setText(text); 3599 mTextView.setTextSize(8f); 3600 mTextView.setSingleLine(true); 3601 }); 3602 mInstrumentation.waitForIdleSync(); 3603 3604 // add a compound drawable to TextView to make offset calculation more interesting 3605 final Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red); 3606 drawable.setBounds(0, 0, 10, 10); 3607 mTextView.setCompoundDrawables(drawable, drawable, drawable, drawable); 3608 3609 final FrameLayout layout = new FrameLayout(mActivity); 3610 final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 3611 ViewGroup.LayoutParams.MATCH_PARENT, 3612 ViewGroup.LayoutParams.WRAP_CONTENT); 3613 layout.addView(mTextView, layoutParams); 3614 layout.setLayoutParams(layoutParams); 3615 3616 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout)); 3617 mInstrumentation.waitForIdleSync(); 3618 3619 final float horizontalPosFix = (float) Math.ceil( 3620 mTextView.getPaint().measureText("a") * 2f / 3f); 3621 final int paddingTop = mTextView.getTotalPaddingTop(); 3622 final int paddingLeft = mTextView.getTotalPaddingLeft(); 3623 3624 final int firstOffset = 0; 3625 final int lastOffset = text.length() - 1; 3626 final int midOffset = text.length() / 2; 3627 3628 // left edge of view 3629 float x = 0f; 3630 float y = mTextView.getHeight() / 2f + paddingTop; 3631 assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y)); 3632 3633 // right edge of text 3634 x = mTextView.getLayout().getLineWidth(0) + paddingLeft - horizontalPosFix; 3635 assertEquals(lastOffset, mTextView.getOffsetForPosition(x, y)); 3636 3637 // right edge of view 3638 x = mTextView.getWidth(); 3639 assertEquals(lastOffset + 1, mTextView.getOffsetForPosition(x, y)); 3640 3641 // left edge of view - out of bounds 3642 x = -1f; 3643 assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y)); 3644 3645 // horizontal center of text 3646 x = mTextView.getLayout().getLineWidth(0) / 2f + paddingLeft - horizontalPosFix; 3647 assertEquals(midOffset, mTextView.getOffsetForPosition(x, y)); 3648 } 3649 3650 @Test 3651 public void testGetOffsetForPositionMultiLineLtr() throws Throwable { 3652 final String line = "aaa\n"; 3653 final String threeLines = line + line + line; 3654 mActivityRule.runOnUiThread(() -> { 3655 mTextView = new TextView(mActivity); 3656 mTextView.setText(threeLines); 3657 mTextView.setTextSize(8f); 3658 mTextView.setLines(2); 3659 }); 3660 mInstrumentation.waitForIdleSync(); 3661 3662 // add a compound drawable to TextView to make offset calculation more interesting 3663 final Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red); 3664 drawable.setBounds(0, 0, 10, 10); 3665 mTextView.setCompoundDrawables(drawable, drawable, drawable, drawable); 3666 3667 final FrameLayout layout = new FrameLayout(mActivity); 3668 final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 3669 ViewGroup.LayoutParams.MATCH_PARENT, 3670 ViewGroup.LayoutParams.WRAP_CONTENT); 3671 layout.addView(mTextView, layoutParams); 3672 layout.setLayoutParams(layoutParams); 3673 3674 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout)); 3675 mInstrumentation.waitForIdleSync(); 3676 3677 final Rect lineBounds = new Rect(); 3678 mTextView.getLayout().getLineBounds(0, lineBounds); 3679 3680 final float horizontalPosFix = (float) Math.ceil( 3681 mTextView.getPaint().measureText("a") * 2f / 3f); 3682 final int paddingTop = mTextView.getTotalPaddingTop(); 3683 final int paddingLeft = mTextView.getTotalPaddingLeft(); 3684 3685 // left edge of view at first line 3686 float x = 0f; 3687 float y = lineBounds.height() / 2f + paddingTop; 3688 assertEquals(0, mTextView.getOffsetForPosition(x, y)); 3689 3690 // right edge of view at first line 3691 x = mTextView.getWidth() - 1f; 3692 assertEquals(line.length() - 1, mTextView.getOffsetForPosition(x, y)); 3693 3694 // update lineBounds to be the second line 3695 mTextView.getLayout().getLineBounds(1, lineBounds); 3696 y = lineBounds.top + lineBounds.height() / 2f + paddingTop; 3697 3698 // left edge of view at second line 3699 x = 0f; 3700 assertEquals(line.length(), mTextView.getOffsetForPosition(x, y)); 3701 3702 // right edge of text at second line 3703 x = mTextView.getLayout().getLineWidth(1) + paddingLeft - horizontalPosFix; 3704 assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y)); 3705 3706 // right edge of view at second line 3707 x = mTextView.getWidth() - 1f; 3708 assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y)); 3709 3710 // horizontal center of text at second line 3711 x = mTextView.getLayout().getLineWidth(1) / 2f + paddingLeft - horizontalPosFix; 3712 // second line mid offset should not include next line, therefore subtract one 3713 assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y)); 3714 } 3715 3716 @Test 3717 public void testGetOffsetForPositionMultiLineRtl() throws Throwable { 3718 final String line = "\u0635\u0635\u0635\n"; 3719 final String threeLines = line + line + line; 3720 mActivityRule.runOnUiThread(() -> { 3721 mTextView = new TextView(mActivity); 3722 mTextView.setText(threeLines); 3723 mTextView.setTextSize(8f); 3724 mTextView.setLines(2); 3725 }); 3726 mInstrumentation.waitForIdleSync(); 3727 3728 // add a compound drawable to TextView to make offset calculation more interesting 3729 final Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red); 3730 drawable.setBounds(0, 0, 10, 10); 3731 mTextView.setCompoundDrawables(drawable, drawable, drawable, drawable); 3732 3733 final FrameLayout layout = new FrameLayout(mActivity); 3734 final LayoutParams layoutParams = new LayoutParams( 3735 LayoutParams.MATCH_PARENT, 3736 LayoutParams.WRAP_CONTENT); 3737 layout.addView(mTextView, layoutParams); 3738 layout.setLayoutParams(layoutParams); 3739 3740 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout)); 3741 mInstrumentation.waitForIdleSync(); 3742 3743 final Rect lineBounds = new Rect(); 3744 mTextView.getLayout().getLineBounds(0, lineBounds); 3745 3746 final float horizontalPosFix = (float) Math.ceil( 3747 mTextView.getPaint().measureText("\u0635") * 2f / 3f); 3748 final int paddingTop = mTextView.getTotalPaddingTop(); 3749 final int paddingRight = mTextView.getTotalPaddingRight(); 3750 3751 // right edge of view at first line 3752 float x = mTextView.getWidth() - 1f; 3753 float y = lineBounds.height() / 2f + paddingTop; 3754 assertEquals(0, mTextView.getOffsetForPosition(x, y)); 3755 3756 // left edge of view at first line 3757 x = 0f; 3758 assertEquals(line.length() - 1, mTextView.getOffsetForPosition(x, y)); 3759 3760 // update lineBounds to be the second line 3761 mTextView.getLayout().getLineBounds(1, lineBounds); 3762 y = lineBounds.top + lineBounds.height() / 2f + paddingTop; 3763 3764 // right edge of view at second line 3765 x = mTextView.getWidth() - 1f; 3766 assertEquals(line.length(), mTextView.getOffsetForPosition(x, y)); 3767 3768 // left edge of view at second line 3769 x = 0f; 3770 assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y)); 3771 3772 // left edge of text at second line 3773 x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) + paddingRight 3774 - horizontalPosFix); 3775 assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y)); 3776 3777 // horizontal center of text at second line 3778 x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) / 2f + paddingRight 3779 - horizontalPosFix); 3780 // second line mid offset should not include next line, therefore subtract one 3781 assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y)); 3782 } 3783 3784 @UiThreadTest 3785 @Test 3786 public void testIsTextSelectable_returnsFalseByDefault() { 3787 final TextView textView = new TextView(mActivity); 3788 textView.setText("any text"); 3789 assertFalse(textView.isTextSelectable()); 3790 } 3791 3792 @UiThreadTest 3793 @Test 3794 public void testIsTextSelectable_returnsTrueIfSetTextIsSelectableCalledWithTrue() { 3795 final TextView textView = new TextView(mActivity); 3796 textView.setText("any text"); 3797 textView.setTextIsSelectable(true); 3798 assertTrue(textView.isTextSelectable()); 3799 } 3800 3801 @UiThreadTest 3802 @Test 3803 public void testSetIsTextSelectable() { 3804 final TextView textView = new TextView(mActivity); 3805 3806 assertFalse(textView.isTextSelectable()); 3807 assertFalse(textView.isFocusable()); 3808 assertFalse(textView.isFocusableInTouchMode()); 3809 assertFalse(textView.isClickable()); 3810 assertFalse(textView.isLongClickable()); 3811 3812 textView.setTextIsSelectable(true); 3813 3814 assertTrue(textView.isTextSelectable()); 3815 assertTrue(textView.isFocusable()); 3816 assertTrue(textView.isFocusableInTouchMode()); 3817 assertTrue(textView.isClickable()); 3818 assertTrue(textView.isLongClickable()); 3819 assertNotNull(textView.getMovementMethod()); 3820 } 3821 3822 @Test 3823 public void testAccessTransformationMethod() throws Throwable { 3824 // check the password attribute in xml 3825 mTextView = findTextView(R.id.textview_password); 3826 assertNotNull(mTextView); 3827 assertSame(PasswordTransformationMethod.getInstance(), 3828 mTextView.getTransformationMethod()); 3829 3830 // check the singleLine attribute in xml 3831 mTextView = findTextView(R.id.textview_singleLine); 3832 assertNotNull(mTextView); 3833 assertSame(SingleLineTransformationMethod.getInstance(), 3834 mTextView.getTransformationMethod()); 3835 3836 final QwertyKeyListener qwertyKeyListener = QwertyKeyListener.getInstance(false, 3837 Capitalize.NONE); 3838 final TransformationMethod method = PasswordTransformationMethod.getInstance(); 3839 // change transformation method by function 3840 mActivityRule.runOnUiThread(() -> { 3841 mTextView.setKeyListener(qwertyKeyListener); 3842 mTextView.setTransformationMethod(method); 3843 mTransformedText = method.getTransformation(mTextView.getText(), mTextView); 3844 3845 mTextView.requestFocus(); 3846 }); 3847 mInstrumentation.waitForIdleSync(); 3848 assertSame(PasswordTransformationMethod.getInstance(), 3849 mTextView.getTransformationMethod()); 3850 3851 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, "H E 2*L O"); 3852 mActivityRule.runOnUiThread(() -> mTextView.append(" ")); 3853 mInstrumentation.waitForIdleSync(); 3854 3855 // It will get transformed after a while 3856 // We're waiting for transformation to "******" 3857 PollingCheck.waitFor(TIMEOUT, () -> mTransformedText.toString() 3858 .equals("\u2022\u2022\u2022\u2022\u2022\u2022")); 3859 3860 // set null 3861 mActivityRule.runOnUiThread(() -> mTextView.setTransformationMethod(null)); 3862 mInstrumentation.waitForIdleSync(); 3863 assertNull(mTextView.getTransformationMethod()); 3864 } 3865 3866 @UiThreadTest 3867 @Test 3868 public void testCompound() { 3869 mTextView = new TextView(mActivity); 3870 int padding = 3; 3871 Drawable[] drawables = mTextView.getCompoundDrawables(); 3872 assertNull(drawables[0]); 3873 assertNull(drawables[1]); 3874 assertNull(drawables[2]); 3875 assertNull(drawables[3]); 3876 3877 // test setCompoundDrawablePadding and getCompoundDrawablePadding 3878 mTextView.setCompoundDrawablePadding(padding); 3879 assertEquals(padding, mTextView.getCompoundDrawablePadding()); 3880 3881 // using resid, 0 represents null 3882 mTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.start, R.drawable.pass, 3883 R.drawable.failed, 0); 3884 drawables = mTextView.getCompoundDrawables(); 3885 3886 // drawableLeft 3887 WidgetTestUtils.assertEquals(TestUtils.getBitmap(mActivity, R.drawable.start), 3888 ((BitmapDrawable) drawables[0]).getBitmap()); 3889 // drawableTop 3890 WidgetTestUtils.assertEquals(TestUtils.getBitmap(mActivity, R.drawable.pass), 3891 ((BitmapDrawable) drawables[1]).getBitmap()); 3892 // drawableRight 3893 WidgetTestUtils.assertEquals(TestUtils.getBitmap(mActivity, R.drawable.failed), 3894 ((BitmapDrawable) drawables[2]).getBitmap()); 3895 // drawableBottom 3896 assertNull(drawables[3]); 3897 3898 Drawable left = TestUtils.getDrawable(mActivity, R.drawable.blue); 3899 Drawable right = TestUtils.getDrawable(mActivity, R.drawable.yellow); 3900 Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red); 3901 3902 // using drawables directly 3903 mTextView.setCompoundDrawablesWithIntrinsicBounds(left, top, right, null); 3904 drawables = mTextView.getCompoundDrawables(); 3905 3906 // drawableLeft 3907 assertSame(left, drawables[0]); 3908 // drawableTop 3909 assertSame(top, drawables[1]); 3910 // drawableRight 3911 assertSame(right, drawables[2]); 3912 // drawableBottom 3913 assertNull(drawables[3]); 3914 3915 // check compound padding 3916 assertEquals(mTextView.getPaddingLeft() + padding + left.getIntrinsicWidth(), 3917 mTextView.getCompoundPaddingLeft()); 3918 assertEquals(mTextView.getPaddingTop() + padding + top.getIntrinsicHeight(), 3919 mTextView.getCompoundPaddingTop()); 3920 assertEquals(mTextView.getPaddingRight() + padding + right.getIntrinsicWidth(), 3921 mTextView.getCompoundPaddingRight()); 3922 assertEquals(mTextView.getPaddingBottom(), mTextView.getCompoundPaddingBottom()); 3923 3924 // set bounds to drawables and set them again. 3925 left.setBounds(0, 0, 1, 2); 3926 right.setBounds(0, 0, 3, 4); 3927 top.setBounds(0, 0, 5, 6); 3928 // usinf drawables 3929 mTextView.setCompoundDrawables(left, top, right, null); 3930 drawables = mTextView.getCompoundDrawables(); 3931 3932 // drawableLeft 3933 assertSame(left, drawables[0]); 3934 // drawableTop 3935 assertSame(top, drawables[1]); 3936 // drawableRight 3937 assertSame(right, drawables[2]); 3938 // drawableBottom 3939 assertNull(drawables[3]); 3940 3941 // check compound padding 3942 assertEquals(mTextView.getPaddingLeft() + padding + left.getBounds().width(), 3943 mTextView.getCompoundPaddingLeft()); 3944 assertEquals(mTextView.getPaddingTop() + padding + top.getBounds().height(), 3945 mTextView.getCompoundPaddingTop()); 3946 assertEquals(mTextView.getPaddingRight() + padding + right.getBounds().width(), 3947 mTextView.getCompoundPaddingRight()); 3948 assertEquals(mTextView.getPaddingBottom(), mTextView.getCompoundPaddingBottom()); 3949 } 3950 3951 @UiThreadTest 3952 @Test 3953 public void testGetCompoundDrawablesRelative() { 3954 // prepare textview 3955 mTextView = new TextView(mActivity); 3956 3957 // prepare drawables 3958 final Drawable start = TestUtils.getDrawable(mActivity, R.drawable.blue); 3959 final Drawable end = TestUtils.getDrawable(mActivity, R.drawable.yellow); 3960 final Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red); 3961 final Drawable bottom = TestUtils.getDrawable(mActivity, R.drawable.black); 3962 assertNotNull(start); 3963 assertNotNull(end); 3964 assertNotNull(top); 3965 assertNotNull(bottom); 3966 3967 Drawable[] drawables = mTextView.getCompoundDrawablesRelative(); 3968 assertNotNull(drawables); 3969 assertEquals(4, drawables.length); 3970 assertNull(drawables[0]); 3971 assertNull(drawables[1]); 3972 assertNull(drawables[2]); 3973 assertNull(drawables[3]); 3974 3975 mTextView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); 3976 mTextView.setCompoundDrawablesRelative(start, top, end, bottom); 3977 drawables = mTextView.getCompoundDrawablesRelative(); 3978 3979 assertNotNull(drawables); 3980 assertEquals(4, drawables.length); 3981 assertSame(start, drawables[0]); 3982 assertSame(top, drawables[1]); 3983 assertSame(end, drawables[2]); 3984 assertSame(bottom, drawables[3]); 3985 3986 mTextView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); 3987 mTextView.setCompoundDrawablesRelative(start, top, end, bottom); 3988 drawables = mTextView.getCompoundDrawablesRelative(); 3989 3990 assertNotNull(drawables); 3991 assertEquals(4, drawables.length); 3992 assertSame(start, drawables[0]); 3993 assertSame(top, drawables[1]); 3994 assertSame(end, drawables[2]); 3995 assertSame(bottom, drawables[3]); 3996 3997 mTextView.setCompoundDrawablesRelative(null, null, null, null); 3998 drawables = mTextView.getCompoundDrawablesRelative(); 3999 4000 assertNotNull(drawables); 4001 assertEquals(4, drawables.length); 4002 assertNull(drawables[0]); 4003 assertNull(drawables[1]); 4004 assertNull(drawables[2]); 4005 assertNull(drawables[3]); 4006 } 4007 4008 @Test 4009 public void testSingleLine() throws Throwable { 4010 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 4011 mInstrumentation.waitForIdleSync(); 4012 4013 setSpannableText(mTextView, "This is a really long sentence" 4014 + " which can not be placed in one line on the screen."); 4015 4016 // Narrow layout assures that the text will get wrapped. 4017 final FrameLayout innerLayout = new FrameLayout(mActivity); 4018 innerLayout.setLayoutParams(new ViewGroup.LayoutParams(100, 100)); 4019 innerLayout.addView(mTextView); 4020 4021 final FrameLayout layout = new FrameLayout(mActivity); 4022 layout.addView(innerLayout); 4023 4024 mActivityRule.runOnUiThread(() -> { 4025 mActivity.setContentView(layout); 4026 mTextView.setSingleLine(true); 4027 }); 4028 mInstrumentation.waitForIdleSync(); 4029 4030 assertEquals(SingleLineTransformationMethod.getInstance(), 4031 mTextView.getTransformationMethod()); 4032 4033 int singleLineWidth = 0; 4034 int singleLineHeight = 0; 4035 4036 if (mTextView.getLayout() != null) { 4037 singleLineWidth = mTextView.getLayout().getWidth(); 4038 singleLineHeight = mTextView.getLayout().getHeight(); 4039 } 4040 4041 mActivityRule.runOnUiThread(() -> mTextView.setSingleLine(false)); 4042 mInstrumentation.waitForIdleSync(); 4043 assertEquals(null, mTextView.getTransformationMethod()); 4044 4045 if (mTextView.getLayout() != null) { 4046 assertTrue(mTextView.getLayout().getHeight() > singleLineHeight); 4047 assertTrue(mTextView.getLayout().getWidth() < singleLineWidth); 4048 } 4049 4050 // same behaviours as setSingLine(true) 4051 mActivityRule.runOnUiThread(mTextView::setSingleLine); 4052 mInstrumentation.waitForIdleSync(); 4053 assertEquals(SingleLineTransformationMethod.getInstance(), 4054 mTextView.getTransformationMethod()); 4055 4056 if (mTextView.getLayout() != null) { 4057 assertEquals(singleLineHeight, mTextView.getLayout().getHeight()); 4058 assertEquals(singleLineWidth, mTextView.getLayout().getWidth()); 4059 } 4060 } 4061 4062 @UiThreadTest 4063 @Test 4064 public void testAccessMaxLines() { 4065 mTextView = findTextView(R.id.textview_text); 4066 mTextView.setWidth((int) (mTextView.getPaint().measureText(LONG_TEXT) / 4)); 4067 mTextView.setText(LONG_TEXT); 4068 4069 final int maxLines = 2; 4070 assertTrue(mTextView.getLineCount() > maxLines); 4071 4072 mTextView.setMaxLines(maxLines); 4073 mTextView.requestLayout(); 4074 4075 assertEquals(2, mTextView.getMaxLines()); 4076 assertEquals(-1, mTextView.getMaxHeight()); 4077 assertTrue(mTextView.getHeight() <= maxLines * mTextView.getLineHeight()); 4078 } 4079 4080 @UiThreadTest 4081 @Test 4082 public void testHyphenationNotHappen_frequencyNone() { 4083 final int[] BREAK_STRATEGIES = { 4084 Layout.BREAK_STRATEGY_SIMPLE, Layout.BREAK_STRATEGY_HIGH_QUALITY, 4085 Layout.BREAK_STRATEGY_BALANCED }; 4086 4087 mTextView = findTextView(R.id.textview_text); 4088 4089 for (int breakStrategy : BREAK_STRATEGIES) { 4090 for (int charWidth = 10; charWidth < 120; charWidth += 5) { 4091 // Change the text view's width to charWidth width. 4092 final String substring = LONG_TEXT.substring(0, charWidth); 4093 mTextView.setWidth((int) Math.ceil(mTextView.getPaint().measureText(substring))); 4094 4095 mTextView.setText(LONG_TEXT); 4096 mTextView.setBreakStrategy(breakStrategy); 4097 4098 mTextView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE); 4099 4100 mTextView.requestLayout(); 4101 mTextView.onPreDraw(); // For freezing the layout. 4102 Layout layout = mTextView.getLayout(); 4103 4104 final int lineCount = layout.getLineCount(); 4105 for (int line = 0; line < lineCount; ++line) { 4106 final int lineEnd = layout.getLineEnd(line); 4107 // In any width, any break strategy, hyphenation should not happen if 4108 // HYPHENATION_FREQUENCY_NONE is specified. 4109 assertTrue(lineEnd == LONG_TEXT.length() || 4110 Character.isWhitespace(LONG_TEXT.charAt(lineEnd - 1))); 4111 } 4112 } 4113 } 4114 } 4115 4116 @UiThreadTest 4117 @Test 4118 public void testHyphenationNotHappen_breakStrategySimple() { 4119 final int[] HYPHENATION_FREQUENCIES = { 4120 Layout.HYPHENATION_FREQUENCY_NORMAL, Layout.HYPHENATION_FREQUENCY_FULL, 4121 Layout.HYPHENATION_FREQUENCY_NONE }; 4122 4123 mTextView = findTextView(R.id.textview_text); 4124 4125 for (int hyphenationFrequency: HYPHENATION_FREQUENCIES) { 4126 for (int charWidth = 10; charWidth < 120; charWidth += 5) { 4127 // Change the text view's width to charWidth width. 4128 final String substring = LONG_TEXT.substring(0, charWidth); 4129 mTextView.setWidth((int) Math.ceil(mTextView.getPaint().measureText(substring))); 4130 4131 mTextView.setText(LONG_TEXT); 4132 mTextView.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE); 4133 4134 mTextView.setHyphenationFrequency(hyphenationFrequency); 4135 4136 mTextView.requestLayout(); 4137 mTextView.onPreDraw(); // For freezing the layout. 4138 Layout layout = mTextView.getLayout(); 4139 4140 final int lineCount = layout.getLineCount(); 4141 for (int line = 0; line < lineCount; ++line) { 4142 final int lineEnd = layout.getLineEnd(line); 4143 // In any width, any hyphenation frequency, hyphenation should not happen if 4144 // BREAK_STRATEGY_SIMPLE is specified. 4145 assertTrue(lineEnd == LONG_TEXT.length() || 4146 Character.isWhitespace(LONG_TEXT.charAt(lineEnd - 1))); 4147 } 4148 } 4149 } 4150 } 4151 4152 @UiThreadTest 4153 @Test 4154 public void testSetMaxLinesException() { 4155 mTextView = new TextView(mActivity); 4156 mActivity.setContentView(mTextView); 4157 mTextView.setWidth(mTextView.getWidth() >> 3); 4158 mTextView.setMaxLines(-1); 4159 } 4160 4161 @Test 4162 public void testAccessMinLines() throws Throwable { 4163 mTextView = findTextView(R.id.textview_text); 4164 setWidth(mTextView.getWidth() >> 3); 4165 int originalLines = mTextView.getLineCount(); 4166 4167 setMinLines(originalLines - 1); 4168 assertTrue((originalLines - 1) * mTextView.getLineHeight() <= mTextView.getHeight()); 4169 assertEquals(originalLines - 1, mTextView.getMinLines()); 4170 assertEquals(-1, mTextView.getMinHeight()); 4171 4172 setMinLines(originalLines + 1); 4173 assertTrue((originalLines + 1) * mTextView.getLineHeight() <= mTextView.getHeight()); 4174 assertEquals(originalLines + 1, mTextView.getMinLines()); 4175 assertEquals(-1, mTextView.getMinHeight()); 4176 } 4177 4178 @Test 4179 public void testSetLines() throws Throwable { 4180 mTextView = findTextView(R.id.textview_text); 4181 // make it multiple lines 4182 setWidth(mTextView.getWidth() >> 3); 4183 int originalLines = mTextView.getLineCount(); 4184 4185 setLines(originalLines - 1); 4186 assertTrue((originalLines - 1) * mTextView.getLineHeight() <= mTextView.getHeight()); 4187 4188 setLines(originalLines + 1); 4189 assertTrue((originalLines + 1) * mTextView.getLineHeight() <= mTextView.getHeight()); 4190 } 4191 4192 @UiThreadTest 4193 @Test 4194 public void testSetLinesException() { 4195 mTextView = new TextView(mActivity); 4196 mActivity.setContentView(mTextView); 4197 mTextView.setWidth(mTextView.getWidth() >> 3); 4198 mTextView.setLines(-1); 4199 } 4200 4201 @UiThreadTest 4202 @Test 4203 public void testGetExtendedPaddingTop() { 4204 mTextView = findTextView(R.id.textview_text); 4205 // Initialized value 4206 assertEquals(0, mTextView.getExtendedPaddingTop()); 4207 4208 // After Set a Drawable 4209 final Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red); 4210 top.setBounds(0, 0, 100, 10); 4211 mTextView.setCompoundDrawables(null, top, null, null); 4212 assertEquals(mTextView.getCompoundPaddingTop(), mTextView.getExtendedPaddingTop()); 4213 4214 // Change line count 4215 mTextView.setLines(mTextView.getLineCount() - 1); 4216 mTextView.setGravity(Gravity.BOTTOM); 4217 4218 assertTrue(mTextView.getExtendedPaddingTop() > 0); 4219 } 4220 4221 @UiThreadTest 4222 @Test 4223 public void testGetExtendedPaddingBottom() { 4224 mTextView = findTextView(R.id.textview_text); 4225 // Initialized value 4226 assertEquals(0, mTextView.getExtendedPaddingBottom()); 4227 4228 // After Set a Drawable 4229 final Drawable bottom = TestUtils.getDrawable(mActivity, R.drawable.red); 4230 bottom.setBounds(0, 0, 100, 10); 4231 mTextView.setCompoundDrawables(null, null, null, bottom); 4232 assertEquals(mTextView.getCompoundPaddingBottom(), mTextView.getExtendedPaddingBottom()); 4233 4234 // Change line count 4235 mTextView.setLines(mTextView.getLineCount() - 1); 4236 mTextView.setGravity(Gravity.CENTER_VERTICAL); 4237 4238 assertTrue(mTextView.getExtendedPaddingBottom() > 0); 4239 } 4240 4241 @Test 4242 public void testGetTotalPaddingTop() throws Throwable { 4243 mTextView = findTextView(R.id.textview_text); 4244 // Initialized value 4245 assertEquals(0, mTextView.getTotalPaddingTop()); 4246 4247 // After Set a Drawable 4248 final Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red); 4249 top.setBounds(0, 0, 100, 10); 4250 mActivityRule.runOnUiThread(() -> { 4251 mTextView.setCompoundDrawables(null, top, null, null); 4252 mTextView.setLines(mTextView.getLineCount() - 1); 4253 mTextView.setGravity(Gravity.BOTTOM); 4254 }); 4255 mInstrumentation.waitForIdleSync(); 4256 assertEquals(mTextView.getExtendedPaddingTop(), mTextView.getTotalPaddingTop()); 4257 4258 // Change line count 4259 setLines(mTextView.getLineCount() + 1); 4260 int expected = mTextView.getHeight() 4261 - mTextView.getExtendedPaddingBottom() 4262 - mTextView.getLayout().getLineTop(mTextView.getLineCount()); 4263 assertEquals(expected, mTextView.getTotalPaddingTop()); 4264 } 4265 4266 @Test 4267 public void testGetTotalPaddingBottom() throws Throwable { 4268 mTextView = findTextView(R.id.textview_text); 4269 // Initialized value 4270 assertEquals(0, mTextView.getTotalPaddingBottom()); 4271 4272 // After Set a Drawable 4273 final Drawable bottom = TestUtils.getDrawable(mActivity, R.drawable.red); 4274 bottom.setBounds(0, 0, 100, 10); 4275 mActivityRule.runOnUiThread(() -> { 4276 mTextView.setCompoundDrawables(null, null, null, bottom); 4277 mTextView.setLines(mTextView.getLineCount() - 1); 4278 mTextView.setGravity(Gravity.CENTER_VERTICAL); 4279 }); 4280 mInstrumentation.waitForIdleSync(); 4281 assertEquals(mTextView.getExtendedPaddingBottom(), mTextView.getTotalPaddingBottom()); 4282 4283 // Change line count 4284 setLines(mTextView.getLineCount() + 1); 4285 int expected = ((mTextView.getHeight() 4286 - mTextView.getExtendedPaddingBottom() 4287 - mTextView.getExtendedPaddingTop() 4288 - mTextView.getLayout().getLineBottom(mTextView.getLineCount())) >> 1) 4289 + mTextView.getExtendedPaddingBottom(); 4290 assertEquals(expected, mTextView.getTotalPaddingBottom()); 4291 } 4292 4293 @UiThreadTest 4294 @Test 4295 public void testGetTotalPaddingLeft() { 4296 mTextView = findTextView(R.id.textview_text); 4297 // Initialized value 4298 assertEquals(0, mTextView.getTotalPaddingLeft()); 4299 4300 // After Set a Drawable 4301 Drawable left = TestUtils.getDrawable(mActivity, R.drawable.red); 4302 left.setBounds(0, 0, 10, 100); 4303 mTextView.setCompoundDrawables(left, null, null, null); 4304 mTextView.setGravity(Gravity.RIGHT); 4305 assertEquals(mTextView.getCompoundPaddingLeft(), mTextView.getTotalPaddingLeft()); 4306 4307 // Change width 4308 mTextView.setWidth(Integer.MAX_VALUE); 4309 assertEquals(mTextView.getCompoundPaddingLeft(), mTextView.getTotalPaddingLeft()); 4310 } 4311 4312 @UiThreadTest 4313 @Test 4314 public void testGetTotalPaddingRight() { 4315 mTextView = findTextView(R.id.textview_text); 4316 // Initialized value 4317 assertEquals(0, mTextView.getTotalPaddingRight()); 4318 4319 // After Set a Drawable 4320 Drawable right = TestUtils.getDrawable(mActivity, R.drawable.red); 4321 right.setBounds(0, 0, 10, 100); 4322 mTextView.setCompoundDrawables(null, null, right, null); 4323 mTextView.setGravity(Gravity.CENTER_HORIZONTAL); 4324 assertEquals(mTextView.getCompoundPaddingRight(), mTextView.getTotalPaddingRight()); 4325 4326 // Change width 4327 mTextView.setWidth(Integer.MAX_VALUE); 4328 assertEquals(mTextView.getCompoundPaddingRight(), mTextView.getTotalPaddingRight()); 4329 } 4330 4331 @UiThreadTest 4332 @Test 4333 public void testGetUrls() { 4334 mTextView = new TextView(mActivity); 4335 4336 URLSpan[] spans = mTextView.getUrls(); 4337 assertEquals(0, spans.length); 4338 4339 String url = "http://www.google.com"; 4340 String email = "name (at) gmail.com"; 4341 String string = url + " mailto:" + email; 4342 SpannableString spannable = new SpannableString(string); 4343 spannable.setSpan(new URLSpan(url), 0, url.length(), 0); 4344 mTextView.setText(spannable, BufferType.SPANNABLE); 4345 spans = mTextView.getUrls(); 4346 assertEquals(1, spans.length); 4347 assertEquals(url, spans[0].getURL()); 4348 4349 spannable.setSpan(new URLSpan(email), 0, email.length(), 0); 4350 mTextView.setText(spannable, BufferType.SPANNABLE); 4351 4352 spans = mTextView.getUrls(); 4353 assertEquals(2, spans.length); 4354 assertEquals(url, spans[0].getURL()); 4355 assertEquals(email, spans[1].getURL()); 4356 4357 // test the situation that param what is not a URLSpan 4358 spannable.setSpan(new Object(), 0, 9, 0); 4359 mTextView.setText(spannable, BufferType.SPANNABLE); 4360 spans = mTextView.getUrls(); 4361 assertEquals(2, spans.length); 4362 } 4363 4364 @UiThreadTest 4365 @Test 4366 public void testSetPadding() { 4367 mTextView = new TextView(mActivity); 4368 4369 mTextView.setPadding(0, 1, 2, 4); 4370 assertEquals(0, mTextView.getPaddingLeft()); 4371 assertEquals(1, mTextView.getPaddingTop()); 4372 assertEquals(2, mTextView.getPaddingRight()); 4373 assertEquals(4, mTextView.getPaddingBottom()); 4374 4375 mTextView.setPadding(10, 20, 30, 40); 4376 assertEquals(10, mTextView.getPaddingLeft()); 4377 assertEquals(20, mTextView.getPaddingTop()); 4378 assertEquals(30, mTextView.getPaddingRight()); 4379 assertEquals(40, mTextView.getPaddingBottom()); 4380 } 4381 4382 @UiThreadTest 4383 @Test 4384 public void testDeprecatedSetTextAppearance() { 4385 mTextView = new TextView(mActivity); 4386 4387 mTextView.setTextAppearance(mActivity, R.style.TextAppearance_All); 4388 assertEquals(mActivity.getResources().getColor(R.drawable.black), 4389 mTextView.getCurrentTextColor()); 4390 assertEquals(20f, mTextView.getTextSize(), 0.01f); 4391 assertEquals(Typeface.BOLD, mTextView.getTypeface().getStyle()); 4392 assertEquals(mActivity.getResources().getColor(R.drawable.red), 4393 mTextView.getCurrentHintTextColor()); 4394 assertEquals(mActivity.getResources().getColor(R.drawable.blue), 4395 mTextView.getLinkTextColors().getDefaultColor()); 4396 4397 mTextView.setTextAppearance(mActivity, R.style.TextAppearance_Colors); 4398 assertEquals(mActivity.getResources().getColor(R.drawable.black), 4399 mTextView.getCurrentTextColor()); 4400 assertEquals(mActivity.getResources().getColor(R.drawable.blue), 4401 mTextView.getCurrentHintTextColor()); 4402 assertEquals(mActivity.getResources().getColor(R.drawable.yellow), 4403 mTextView.getLinkTextColors().getDefaultColor()); 4404 4405 mTextView.setTextAppearance(mActivity, R.style.TextAppearance_NotColors); 4406 assertEquals(17f, mTextView.getTextSize(), 0.01f); 4407 assertEquals(Typeface.NORMAL, mTextView.getTypeface().getStyle()); 4408 4409 mTextView.setTextAppearance(mActivity, R.style.TextAppearance_Style); 4410 assertEquals(null, mTextView.getTypeface()); 4411 } 4412 4413 @UiThreadTest 4414 @Test 4415 public void testSetTextAppearance() { 4416 mTextView = new TextView(mActivity); 4417 4418 mTextView.setTextAppearance(R.style.TextAppearance_All); 4419 assertEquals(mActivity.getResources().getColor(R.drawable.black), 4420 mTextView.getCurrentTextColor()); 4421 assertEquals(20f, mTextView.getTextSize(), 0.01f); 4422 assertEquals(Typeface.BOLD, mTextView.getTypeface().getStyle()); 4423 assertEquals(mActivity.getResources().getColor(R.drawable.red), 4424 mTextView.getCurrentHintTextColor()); 4425 assertEquals(mActivity.getResources().getColor(R.drawable.blue), 4426 mTextView.getLinkTextColors().getDefaultColor()); 4427 assertEquals(mActivity.getResources().getColor(R.drawable.yellow), 4428 mTextView.getHighlightColor()); 4429 4430 mTextView.setTextAppearance(R.style.TextAppearance_Colors); 4431 assertEquals(mActivity.getResources().getColor(R.drawable.black), 4432 mTextView.getCurrentTextColor()); 4433 assertEquals(mActivity.getResources().getColor(R.drawable.blue), 4434 mTextView.getCurrentHintTextColor()); 4435 assertEquals(mActivity.getResources().getColor(R.drawable.yellow), 4436 mTextView.getLinkTextColors().getDefaultColor()); 4437 assertEquals(mActivity.getResources().getColor(R.drawable.red), 4438 mTextView.getHighlightColor()); 4439 4440 mTextView.setTextAppearance(R.style.TextAppearance_NotColors); 4441 assertEquals(17f, mTextView.getTextSize(), 0.01f); 4442 assertEquals(Typeface.NORMAL, mTextView.getTypeface().getStyle()); 4443 4444 mTextView.setTextAppearance(R.style.TextAppearance_Style); 4445 assertEquals(null, mTextView.getTypeface()); 4446 } 4447 4448 @UiThreadTest 4449 @Test 4450 public void testAccessCompoundDrawableTint() { 4451 mTextView = new TextView(mActivity); 4452 4453 ColorStateList colors = ColorStateList.valueOf(Color.RED); 4454 mTextView.setCompoundDrawableTintList(colors); 4455 mTextView.setCompoundDrawableTintMode(PorterDuff.Mode.XOR); 4456 assertSame(colors, mTextView.getCompoundDrawableTintList()); 4457 assertEquals(PorterDuff.Mode.XOR, mTextView.getCompoundDrawableTintMode()); 4458 4459 // Ensure the tint is preserved across drawable changes. 4460 mTextView.setCompoundDrawablesRelative(null, null, null, null); 4461 assertSame(colors, mTextView.getCompoundDrawableTintList()); 4462 assertEquals(PorterDuff.Mode.XOR, mTextView.getCompoundDrawableTintMode()); 4463 4464 mTextView.setCompoundDrawables(null, null, null, null); 4465 assertSame(colors, mTextView.getCompoundDrawableTintList()); 4466 assertEquals(PorterDuff.Mode.XOR, mTextView.getCompoundDrawableTintMode()); 4467 4468 ColorDrawable dr1 = new ColorDrawable(Color.RED); 4469 ColorDrawable dr2 = new ColorDrawable(Color.GREEN); 4470 ColorDrawable dr3 = new ColorDrawable(Color.BLUE); 4471 ColorDrawable dr4 = new ColorDrawable(Color.YELLOW); 4472 mTextView.setCompoundDrawables(dr1, dr2, dr3, dr4); 4473 assertSame(colors, mTextView.getCompoundDrawableTintList()); 4474 assertEquals(PorterDuff.Mode.XOR, mTextView.getCompoundDrawableTintMode()); 4475 } 4476 4477 @Test 4478 public void testSetHorizontallyScrolling() throws Throwable { 4479 // make the text view has more than one line 4480 mTextView = findTextView(R.id.textview_text); 4481 setWidth(mTextView.getWidth() >> 1); 4482 assertTrue(mTextView.getLineCount() > 1); 4483 4484 setHorizontallyScrolling(true); 4485 assertEquals(1, mTextView.getLineCount()); 4486 4487 setHorizontallyScrolling(false); 4488 assertTrue(mTextView.getLineCount() > 1); 4489 } 4490 4491 @Test 4492 public void testComputeHorizontalScrollRange() throws Throwable { 4493 mActivityRule.runOnUiThread(() -> mTextView = new MockTextView(mActivity)); 4494 mInstrumentation.waitForIdleSync(); 4495 // test when layout is null 4496 assertNull(mTextView.getLayout()); 4497 assertEquals(mTextView.getWidth(), 4498 ((MockTextView) mTextView).computeHorizontalScrollRange()); 4499 4500 mActivityRule.runOnUiThread(() -> ((MockTextView) mTextView).setFrame(0, 0, 40, 50)); 4501 mInstrumentation.waitForIdleSync(); 4502 assertEquals(mTextView.getWidth(), 4503 ((MockTextView) mTextView).computeHorizontalScrollRange()); 4504 4505 // set the layout 4506 layout(mTextView); 4507 assertEquals(mTextView.getLayout().getWidth(), 4508 ((MockTextView) mTextView).computeHorizontalScrollRange()); 4509 } 4510 4511 @Test 4512 public void testComputeVerticalScrollRange() throws Throwable { 4513 mActivityRule.runOnUiThread(() -> mTextView = new MockTextView(mActivity)); 4514 mInstrumentation.waitForIdleSync(); 4515 4516 // test when layout is null 4517 assertNull(mTextView.getLayout()); 4518 assertEquals(0, ((MockTextView) mTextView).computeVerticalScrollRange()); 4519 4520 mActivityRule.runOnUiThread(() -> ((MockTextView) mTextView).setFrame(0, 0, 40, 50)); 4521 mInstrumentation.waitForIdleSync(); 4522 assertEquals(mTextView.getHeight(), ((MockTextView) mTextView).computeVerticalScrollRange()); 4523 4524 //set the layout 4525 layout(mTextView); 4526 assertEquals(mTextView.getLayout().getHeight(), 4527 ((MockTextView) mTextView).computeVerticalScrollRange()); 4528 } 4529 4530 @Test 4531 public void testDrawableStateChanged() throws Throwable { 4532 mActivityRule.runOnUiThread(() -> mTextView = spy(new MockTextView(mActivity))); 4533 mInstrumentation.waitForIdleSync(); 4534 reset(mTextView); 4535 mTextView.refreshDrawableState(); 4536 ((MockTextView) verify(mTextView, times(1))).drawableStateChanged(); 4537 } 4538 4539 @UiThreadTest 4540 @Test 4541 public void testGetDefaultEditable() { 4542 mTextView = new MockTextView(mActivity); 4543 4544 //the TextView#getDefaultEditable() does nothing, and always return false. 4545 assertFalse(((MockTextView) mTextView).getDefaultEditable()); 4546 } 4547 4548 @UiThreadTest 4549 @Test 4550 public void testGetDefaultMovementMethod() { 4551 MockTextView textView = new MockTextView(mActivity); 4552 4553 //the TextView#getDefaultMovementMethod() does nothing, and always return null. 4554 assertNull(textView.getDefaultMovementMethod()); 4555 } 4556 4557 @UiThreadTest 4558 @Test 4559 public void testSetFrame() { 4560 MockTextView textView = new MockTextView(mActivity); 4561 4562 //Assign a new size to this view 4563 assertTrue(textView.setFrame(0, 0, 320, 480)); 4564 assertEquals(0, textView.getLeft()); 4565 assertEquals(0, textView.getTop()); 4566 assertEquals(320, textView.getRight()); 4567 assertEquals(480, textView.getBottom()); 4568 4569 //Assign a same size to this view 4570 assertFalse(textView.setFrame(0, 0, 320, 480)); 4571 4572 //negative input 4573 assertTrue(textView.setFrame(-1, -1, -1, -1)); 4574 assertEquals(-1, textView.getLeft()); 4575 assertEquals(-1, textView.getTop()); 4576 assertEquals(-1, textView.getRight()); 4577 assertEquals(-1, textView.getBottom()); 4578 } 4579 4580 @Test 4581 public void testMarquee() throws Throwable { 4582 // Both are pointing to the same object. This works around current limitation in CTS 4583 // coverage report tool for properly reporting coverage of base class method calls. 4584 mActivityRule.runOnUiThread(() -> { 4585 mSecondTextView = new MockTextView(mActivity); 4586 4587 mTextView = mSecondTextView; 4588 mTextView.setText(LONG_TEXT); 4589 mTextView.setSingleLine(); 4590 mTextView.setEllipsize(TruncateAt.MARQUEE); 4591 mTextView.setLayoutParams(new LayoutParams(100, 100)); 4592 }); 4593 mInstrumentation.waitForIdleSync(); 4594 4595 final FrameLayout layout = new FrameLayout(mActivity); 4596 layout.addView(mTextView); 4597 4598 // make the fading to be shown 4599 mTextView.setHorizontalFadingEdgeEnabled(true); 4600 4601 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout)); 4602 mInstrumentation.waitForIdleSync(); 4603 4604 TestSelectedRunnable runnable = new TestSelectedRunnable(mTextView) { 4605 public void run() { 4606 mTextView.setMarqueeRepeatLimit(-1); 4607 // force the marquee to start 4608 saveIsSelected1(); 4609 mTextView.setSelected(true); 4610 saveIsSelected2(); 4611 } 4612 }; 4613 mActivityRule.runOnUiThread(runnable); 4614 4615 // wait for the marquee to run 4616 // fading is shown on both sides if the marquee runs for a while 4617 PollingCheck.waitFor(TIMEOUT, () -> 4618 ((MockTextView) mSecondTextView).getLeftFadingEdgeStrength() > 0.0f 4619 && ((MockTextView) mSecondTextView).getRightFadingEdgeStrength() > 0.0f); 4620 4621 // wait for left marquee to fully apply 4622 PollingCheck.waitFor(TIMEOUT, () -> 4623 ((MockTextView) mSecondTextView).getLeftFadingEdgeStrength() > 0.99f); 4624 4625 assertFalse(runnable.getIsSelected1()); 4626 assertTrue(runnable.getIsSelected2()); 4627 assertEquals(-1, mTextView.getMarqueeRepeatLimit()); 4628 4629 runnable = new TestSelectedRunnable(mTextView) { 4630 public void run() { 4631 mTextView.setMarqueeRepeatLimit(0); 4632 // force the marquee to stop 4633 saveIsSelected1(); 4634 mTextView.setSelected(false); 4635 saveIsSelected2(); 4636 mTextView.setGravity(Gravity.LEFT); 4637 } 4638 }; 4639 // force the marquee to stop 4640 mActivityRule.runOnUiThread(runnable); 4641 mInstrumentation.waitForIdleSync(); 4642 assertTrue(runnable.getIsSelected1()); 4643 assertFalse(runnable.getIsSelected2()); 4644 assertEquals(0.0f, ((MockTextView) mSecondTextView).getLeftFadingEdgeStrength(), 0.01f); 4645 assertTrue(((MockTextView) mSecondTextView).getRightFadingEdgeStrength() > 0.0f); 4646 assertEquals(0, mTextView.getMarqueeRepeatLimit()); 4647 4648 mActivityRule.runOnUiThread(() -> mTextView.setGravity(Gravity.RIGHT)); 4649 mInstrumentation.waitForIdleSync(); 4650 assertTrue(((MockTextView) mSecondTextView).getLeftFadingEdgeStrength() > 0.0f); 4651 assertEquals(0.0f, ((MockTextView) mSecondTextView).getRightFadingEdgeStrength(), 0.01f); 4652 4653 mActivityRule.runOnUiThread(() -> mTextView.setGravity(Gravity.CENTER_HORIZONTAL)); 4654 mInstrumentation.waitForIdleSync(); 4655 // there is no left fading (Is it correct?) 4656 assertEquals(0.0f, ((MockTextView) mSecondTextView).getLeftFadingEdgeStrength(), 0.01f); 4657 assertTrue(((MockTextView) mSecondTextView).getRightFadingEdgeStrength() > 0.0f); 4658 } 4659 4660 @UiThreadTest 4661 @Test 4662 public void testGetMarqueeRepeatLimit() { 4663 final TextView textView = new TextView(mActivity); 4664 4665 textView.setMarqueeRepeatLimit(10); 4666 assertEquals(10, textView.getMarqueeRepeatLimit()); 4667 } 4668 4669 @UiThreadTest 4670 @Test 4671 public void testAccessInputExtras() throws XmlPullParserException, IOException { 4672 mTextView = new TextView(mActivity); 4673 mTextView.setText(null, BufferType.EDITABLE); 4674 mTextView.setInputType(InputType.TYPE_CLASS_TEXT); 4675 4676 // do not create the extras 4677 assertNull(mTextView.getInputExtras(false)); 4678 4679 // create if it does not exist 4680 Bundle inputExtras = mTextView.getInputExtras(true); 4681 assertNotNull(inputExtras); 4682 assertTrue(inputExtras.isEmpty()); 4683 4684 // it is created already 4685 assertNotNull(mTextView.getInputExtras(false)); 4686 4687 try { 4688 mTextView.setInputExtras(R.xml.input_extras); 4689 fail("Should throw NullPointerException!"); 4690 } catch (NullPointerException e) { 4691 } 4692 } 4693 4694 @UiThreadTest 4695 @Test 4696 public void testAccessContentType() { 4697 mTextView = new TextView(mActivity); 4698 mTextView.setText(null, BufferType.EDITABLE); 4699 mTextView.setKeyListener(null); 4700 mTextView.setTransformationMethod(null); 4701 4702 mTextView.setInputType(InputType.TYPE_CLASS_DATETIME 4703 | InputType.TYPE_DATETIME_VARIATION_NORMAL); 4704 assertEquals(InputType.TYPE_CLASS_DATETIME 4705 | InputType.TYPE_DATETIME_VARIATION_NORMAL, mTextView.getInputType()); 4706 assertTrue(mTextView.getKeyListener() instanceof DateTimeKeyListener); 4707 4708 mTextView.setInputType(InputType.TYPE_CLASS_DATETIME 4709 | InputType.TYPE_DATETIME_VARIATION_DATE); 4710 assertEquals(InputType.TYPE_CLASS_DATETIME 4711 | InputType.TYPE_DATETIME_VARIATION_DATE, mTextView.getInputType()); 4712 assertTrue(mTextView.getKeyListener() instanceof DateKeyListener); 4713 4714 mTextView.setInputType(InputType.TYPE_CLASS_DATETIME 4715 | InputType.TYPE_DATETIME_VARIATION_TIME); 4716 assertEquals(InputType.TYPE_CLASS_DATETIME 4717 | InputType.TYPE_DATETIME_VARIATION_TIME, mTextView.getInputType()); 4718 assertTrue(mTextView.getKeyListener() instanceof TimeKeyListener); 4719 4720 mTextView.setInputType(InputType.TYPE_CLASS_NUMBER 4721 | InputType.TYPE_NUMBER_FLAG_DECIMAL 4722 | InputType.TYPE_NUMBER_FLAG_SIGNED); 4723 assertEquals(InputType.TYPE_CLASS_NUMBER 4724 | InputType.TYPE_NUMBER_FLAG_DECIMAL 4725 | InputType.TYPE_NUMBER_FLAG_SIGNED, mTextView.getInputType()); 4726 assertSame(mTextView.getKeyListener(), 4727 DigitsKeyListener.getInstance(null, true, true)); 4728 4729 mTextView.setInputType(InputType.TYPE_CLASS_PHONE); 4730 assertEquals(InputType.TYPE_CLASS_PHONE, mTextView.getInputType()); 4731 assertTrue(mTextView.getKeyListener() instanceof DialerKeyListener); 4732 4733 mTextView.setInputType(InputType.TYPE_CLASS_TEXT 4734 | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); 4735 assertEquals(InputType.TYPE_CLASS_TEXT 4736 | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT, mTextView.getInputType()); 4737 assertSame(mTextView.getKeyListener(), TextKeyListener.getInstance(true, Capitalize.NONE)); 4738 4739 mTextView.setSingleLine(); 4740 assertTrue(mTextView.getTransformationMethod() instanceof SingleLineTransformationMethod); 4741 mTextView.setInputType(InputType.TYPE_CLASS_TEXT 4742 | InputType.TYPE_TEXT_FLAG_MULTI_LINE 4743 | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS); 4744 assertEquals(InputType.TYPE_CLASS_TEXT 4745 | InputType.TYPE_TEXT_FLAG_MULTI_LINE 4746 | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS, mTextView.getInputType()); 4747 assertSame(mTextView.getKeyListener(), 4748 TextKeyListener.getInstance(false, Capitalize.CHARACTERS)); 4749 assertNull(mTextView.getTransformationMethod()); 4750 4751 mTextView.setInputType(InputType.TYPE_CLASS_TEXT 4752 | InputType.TYPE_TEXT_FLAG_CAP_WORDS); 4753 assertEquals(InputType.TYPE_CLASS_TEXT 4754 | InputType.TYPE_TEXT_FLAG_CAP_WORDS, mTextView.getInputType()); 4755 assertSame(mTextView.getKeyListener(), 4756 TextKeyListener.getInstance(false, Capitalize.WORDS)); 4757 assertTrue(mTextView.getTransformationMethod() instanceof SingleLineTransformationMethod); 4758 4759 mTextView.setInputType(InputType.TYPE_CLASS_TEXT 4760 | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 4761 assertEquals(InputType.TYPE_CLASS_TEXT 4762 | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES, mTextView.getInputType()); 4763 assertSame(mTextView.getKeyListener(), 4764 TextKeyListener.getInstance(false, Capitalize.SENTENCES)); 4765 4766 mTextView.setInputType(InputType.TYPE_NULL); 4767 assertEquals(InputType.TYPE_NULL, mTextView.getInputType()); 4768 assertTrue(mTextView.getKeyListener() instanceof TextKeyListener); 4769 } 4770 4771 @UiThreadTest 4772 @Test 4773 public void testAccessRawContentType() { 4774 mTextView = new TextView(mActivity); 4775 mTextView.setText(null, BufferType.EDITABLE); 4776 mTextView.setKeyListener(null); 4777 mTextView.setTransformationMethod(null); 4778 4779 mTextView.setRawInputType(InputType.TYPE_CLASS_DATETIME 4780 | InputType.TYPE_DATETIME_VARIATION_NORMAL); 4781 assertEquals(InputType.TYPE_CLASS_DATETIME 4782 | InputType.TYPE_DATETIME_VARIATION_NORMAL, mTextView.getInputType()); 4783 assertNull(mTextView.getTransformationMethod()); 4784 assertNull(mTextView.getKeyListener()); 4785 4786 mTextView.setRawInputType(InputType.TYPE_CLASS_DATETIME 4787 | InputType.TYPE_DATETIME_VARIATION_DATE); 4788 assertEquals(InputType.TYPE_CLASS_DATETIME 4789 | InputType.TYPE_DATETIME_VARIATION_DATE, mTextView.getInputType()); 4790 assertNull(mTextView.getTransformationMethod()); 4791 assertNull(mTextView.getKeyListener()); 4792 4793 mTextView.setRawInputType(InputType.TYPE_CLASS_DATETIME 4794 | InputType.TYPE_DATETIME_VARIATION_TIME); 4795 assertEquals(InputType.TYPE_CLASS_DATETIME 4796 | InputType.TYPE_DATETIME_VARIATION_TIME, mTextView.getInputType()); 4797 assertNull(mTextView.getTransformationMethod()); 4798 assertNull(mTextView.getKeyListener()); 4799 4800 mTextView.setRawInputType(InputType.TYPE_CLASS_NUMBER 4801 | InputType.TYPE_NUMBER_FLAG_DECIMAL 4802 | InputType.TYPE_NUMBER_FLAG_SIGNED); 4803 assertEquals(InputType.TYPE_CLASS_NUMBER 4804 | InputType.TYPE_NUMBER_FLAG_DECIMAL 4805 | InputType.TYPE_NUMBER_FLAG_SIGNED, mTextView.getInputType()); 4806 assertNull(mTextView.getTransformationMethod()); 4807 assertNull(mTextView.getKeyListener()); 4808 4809 mTextView.setRawInputType(InputType.TYPE_CLASS_PHONE); 4810 assertEquals(InputType.TYPE_CLASS_PHONE, mTextView.getInputType()); 4811 assertNull(mTextView.getTransformationMethod()); 4812 assertNull(mTextView.getKeyListener()); 4813 4814 mTextView.setRawInputType(InputType.TYPE_CLASS_TEXT 4815 | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); 4816 assertEquals(InputType.TYPE_CLASS_TEXT 4817 | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT, mTextView.getInputType()); 4818 assertNull(mTextView.getTransformationMethod()); 4819 assertNull(mTextView.getKeyListener()); 4820 4821 mTextView.setSingleLine(); 4822 assertTrue(mTextView.getTransformationMethod() instanceof SingleLineTransformationMethod); 4823 mTextView.setRawInputType(InputType.TYPE_CLASS_TEXT 4824 | InputType.TYPE_TEXT_FLAG_MULTI_LINE 4825 | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS); 4826 assertEquals(InputType.TYPE_CLASS_TEXT 4827 | InputType.TYPE_TEXT_FLAG_MULTI_LINE 4828 | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS, mTextView.getInputType()); 4829 assertTrue(mTextView.getTransformationMethod() instanceof SingleLineTransformationMethod); 4830 assertNull(mTextView.getKeyListener()); 4831 4832 mTextView.setRawInputType(InputType.TYPE_CLASS_TEXT 4833 | InputType.TYPE_TEXT_FLAG_CAP_WORDS); 4834 assertEquals(InputType.TYPE_CLASS_TEXT 4835 | InputType.TYPE_TEXT_FLAG_CAP_WORDS, mTextView.getInputType()); 4836 assertTrue(mTextView.getTransformationMethod() instanceof SingleLineTransformationMethod); 4837 assertNull(mTextView.getKeyListener()); 4838 4839 mTextView.setRawInputType(InputType.TYPE_CLASS_TEXT 4840 | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 4841 assertEquals(InputType.TYPE_CLASS_TEXT 4842 | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES, mTextView.getInputType()); 4843 assertTrue(mTextView.getTransformationMethod() instanceof SingleLineTransformationMethod); 4844 assertNull(mTextView.getKeyListener()); 4845 4846 mTextView.setRawInputType(InputType.TYPE_NULL); 4847 assertTrue(mTextView.getTransformationMethod() instanceof SingleLineTransformationMethod); 4848 assertNull(mTextView.getKeyListener()); 4849 } 4850 4851 @UiThreadTest 4852 @Test 4853 public void testVerifyDrawable() { 4854 mTextView = new MockTextView(mActivity); 4855 4856 Drawable d = TestUtils.getDrawable(mActivity, R.drawable.pass); 4857 assertFalse(((MockTextView ) mTextView).verifyDrawable(d)); 4858 4859 mTextView.setCompoundDrawables(null, d, null, null); 4860 assertTrue(((MockTextView ) mTextView).verifyDrawable(d)); 4861 } 4862 4863 @Test 4864 public void testAccessPrivateImeOptions() { 4865 mTextView = findTextView(R.id.textview_text); 4866 assertNull(mTextView.getPrivateImeOptions()); 4867 4868 mTextView.setPrivateImeOptions("com.example.myapp.SpecialMode=3"); 4869 assertEquals("com.example.myapp.SpecialMode=3", mTextView.getPrivateImeOptions()); 4870 4871 mTextView.setPrivateImeOptions(null); 4872 assertNull(mTextView.getPrivateImeOptions()); 4873 } 4874 4875 @Test 4876 public void testSetOnEditorActionListener() { 4877 mTextView = findTextView(R.id.textview_text); 4878 4879 final TextView.OnEditorActionListener mockOnEditorActionListener = 4880 mock(TextView.OnEditorActionListener.class); 4881 verifyZeroInteractions(mockOnEditorActionListener); 4882 4883 mTextView.setOnEditorActionListener(mockOnEditorActionListener); 4884 verifyZeroInteractions(mockOnEditorActionListener); 4885 4886 mTextView.onEditorAction(EditorInfo.IME_ACTION_DONE); 4887 verify(mockOnEditorActionListener, times(1)).onEditorAction(mTextView, 4888 EditorInfo.IME_ACTION_DONE, null); 4889 } 4890 4891 @Test 4892 public void testAccessImeOptions() { 4893 mTextView = findTextView(R.id.textview_text); 4894 assertEquals(EditorInfo.IME_NULL, mTextView.getImeOptions()); 4895 4896 mTextView.setImeOptions(EditorInfo.IME_ACTION_GO); 4897 assertEquals(EditorInfo.IME_ACTION_GO, mTextView.getImeOptions()); 4898 4899 mTextView.setImeOptions(EditorInfo.IME_ACTION_DONE); 4900 assertEquals(EditorInfo.IME_ACTION_DONE, mTextView.getImeOptions()); 4901 4902 mTextView.setImeOptions(EditorInfo.IME_NULL); 4903 assertEquals(EditorInfo.IME_NULL, mTextView.getImeOptions()); 4904 } 4905 4906 @Test 4907 public void testAccessImeActionLabel() { 4908 mTextView = findTextView(R.id.textview_text); 4909 assertNull(mTextView.getImeActionLabel()); 4910 assertEquals(0, mTextView.getImeActionId()); 4911 4912 mTextView.setImeActionLabel("pinyin", 1); 4913 assertEquals("pinyin", mTextView.getImeActionLabel().toString()); 4914 assertEquals(1, mTextView.getImeActionId()); 4915 } 4916 4917 @UiThreadTest 4918 @Test 4919 public void testAccessImeHintLocales() { 4920 final TextView textView = new TextView(mActivity); 4921 textView.setText("", BufferType.EDITABLE); 4922 textView.setKeyListener(null); 4923 textView.setRawInputType(InputType.TYPE_CLASS_TEXT); 4924 assertNull(textView.getImeHintLocales()); 4925 { 4926 final EditorInfo editorInfo = new EditorInfo(); 4927 textView.onCreateInputConnection(editorInfo); 4928 assertNull(editorInfo.hintLocales); 4929 } 4930 4931 final LocaleList localeList = LocaleList.forLanguageTags("en-PH,en-US"); 4932 textView.setImeHintLocales(localeList); 4933 assertEquals(localeList, textView.getImeHintLocales()); 4934 { 4935 final EditorInfo editorInfo = new EditorInfo(); 4936 textView.onCreateInputConnection(editorInfo); 4937 assertEquals(localeList, editorInfo.hintLocales); 4938 } 4939 } 4940 4941 @UiThreadTest 4942 @Test 4943 public void testSetImeHintLocalesChangesInputType() { 4944 final TextView textView = new TextView(mActivity); 4945 textView.setText("", BufferType.EDITABLE); 4946 4947 textView.setInputType(InputType.TYPE_CLASS_NUMBER); 4948 assertEquals(InputType.TYPE_CLASS_NUMBER, textView.getInputType()); 4949 4950 final LocaleList localeList = LocaleList.forLanguageTags("fa-IR"); 4951 textView.setImeHintLocales(localeList); 4952 final int textType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; 4953 // Setting IME hint locales to Persian must change the input type to a full text IME, 4954 // since the typical number input IME may not have localized digits. 4955 assertEquals(textType, textView.getInputType()); 4956 4957 // Changing the input type to datetime should keep the full text IME, since the IME hint 4958 // is still set to Persian, which needs advanced input. 4959 final int dateType = InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE; 4960 textView.setInputType(dateType); 4961 assertEquals(textType, textView.getInputType()); 4962 4963 // Changing the input type to number password should keep the full text IME, since the IME 4964 // hint is still set to Persian, which needs advanced input. But it also needs to set the 4965 // text password flag. 4966 final int numberPasswordType = InputType.TYPE_CLASS_NUMBER 4967 | InputType.TYPE_NUMBER_VARIATION_PASSWORD; 4968 final int textPasswordType = InputType.TYPE_CLASS_TEXT 4969 | InputType.TYPE_TEXT_VARIATION_PASSWORD; 4970 textView.setInputType(numberPasswordType); 4971 assertEquals(textPasswordType, textView.getInputType()); 4972 4973 // Setting the IME hint locales to null should reset the type to number password, since we 4974 // no longer need internationalized input. 4975 textView.setImeHintLocales(null); 4976 assertEquals(numberPasswordType, textView.getInputType()); 4977 } 4978 4979 @UiThreadTest 4980 @Test 4981 public void testSetImeHintLocalesDoesntLoseInputType() { 4982 final TextView textView = new TextView(mActivity); 4983 textView.setText("", BufferType.EDITABLE); 4984 final int inputType = InputType.TYPE_CLASS_TEXT 4985 | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT 4986 | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS 4987 | InputType.TYPE_TEXT_FLAG_MULTI_LINE 4988 | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; 4989 textView.setInputType(inputType); 4990 textView.setImeHintLocales(new LocaleList(Locale.US)); 4991 assertEquals(inputType, textView.getInputType()); 4992 } 4993 4994 @UiThreadTest 4995 @Test 4996 public void testSetExtractedText() { 4997 mTextView = findTextView(R.id.textview_text); 4998 assertEquals(mActivity.getResources().getString(R.string.text_view_hello), 4999 mTextView.getText().toString()); 5000 5001 ExtractedText et = new ExtractedText(); 5002 5003 // Update text and selection. 5004 et.text = "test"; 5005 et.selectionStart = 0; 5006 et.selectionEnd = 2; 5007 5008 mTextView.setExtractedText(et); 5009 assertEquals("test", mTextView.getText().toString()); 5010 assertEquals(0, mTextView.getSelectionStart()); 5011 assertEquals(2, mTextView.getSelectionEnd()); 5012 5013 // Use partialStartOffset and partialEndOffset 5014 et.partialStartOffset = 2; 5015 et.partialEndOffset = 3; 5016 et.text = "x"; 5017 et.selectionStart = 2; 5018 et.selectionEnd = 3; 5019 5020 mTextView.setExtractedText(et); 5021 assertEquals("text", mTextView.getText().toString()); 5022 assertEquals(2, mTextView.getSelectionStart()); 5023 assertEquals(3, mTextView.getSelectionEnd()); 5024 5025 // Update text with spans. 5026 final SpannableString ss = new SpannableString("ex"); 5027 ss.setSpan(new UnderlineSpan(), 0, 2, 0); 5028 ss.setSpan(new URLSpan("ctstest://TextView/test"), 1, 2, 0); 5029 5030 et.text = ss; 5031 et.partialStartOffset = 1; 5032 et.partialEndOffset = 3; 5033 mTextView.setExtractedText(et); 5034 5035 assertEquals("text", mTextView.getText().toString()); 5036 final Editable editable = mTextView.getEditableText(); 5037 final UnderlineSpan[] underlineSpans = mTextView.getEditableText().getSpans( 5038 0, editable.length(), UnderlineSpan.class); 5039 assertEquals(1, underlineSpans.length); 5040 assertEquals(1, editable.getSpanStart(underlineSpans[0])); 5041 assertEquals(3, editable.getSpanEnd(underlineSpans[0])); 5042 5043 final URLSpan[] urlSpans = mTextView.getEditableText().getSpans( 5044 0, editable.length(), URLSpan.class); 5045 assertEquals(1, urlSpans.length); 5046 assertEquals(2, editable.getSpanStart(urlSpans[0])); 5047 assertEquals(3, editable.getSpanEnd(urlSpans[0])); 5048 assertEquals("ctstest://TextView/test", urlSpans[0].getURL()); 5049 } 5050 5051 @Test 5052 public void testMoveCursorToVisibleOffset() throws Throwable { 5053 mTextView = findTextView(R.id.textview_text); 5054 5055 // not a spannable text 5056 mActivityRule.runOnUiThread(() -> assertFalse(mTextView.moveCursorToVisibleOffset())); 5057 mInstrumentation.waitForIdleSync(); 5058 5059 // a selection range 5060 final String spannableText = "text"; 5061 mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); 5062 mInstrumentation.waitForIdleSync(); 5063 5064 mActivityRule.runOnUiThread( 5065 () -> mTextView.setText(spannableText, BufferType.SPANNABLE)); 5066 mInstrumentation.waitForIdleSync(); 5067 Selection.setSelection((Spannable) mTextView.getText(), 0, spannableText.length()); 5068 5069 assertEquals(0, mTextView.getSelectionStart()); 5070 assertEquals(spannableText.length(), mTextView.getSelectionEnd()); 5071 mActivityRule.runOnUiThread(() -> assertFalse(mTextView.moveCursorToVisibleOffset())); 5072 mInstrumentation.waitForIdleSync(); 5073 5074 // a spannable without range 5075 mActivityRule.runOnUiThread(() -> { 5076 mTextView = findTextView(R.id.textview_text); 5077 mTextView.setText(spannableText, BufferType.SPANNABLE); 5078 }); 5079 mInstrumentation.waitForIdleSync(); 5080 5081 mActivityRule.runOnUiThread(() -> assertTrue(mTextView.moveCursorToVisibleOffset())); 5082 mInstrumentation.waitForIdleSync(); 5083 } 5084 5085 @Test 5086 public void testIsInputMethodTarget() throws Throwable { 5087 mTextView = findTextView(R.id.textview_text); 5088 assertFalse(mTextView.isInputMethodTarget()); 5089 5090 assertFalse(mTextView.isFocused()); 5091 mActivityRule.runOnUiThread(() -> { 5092 mTextView.setFocusable(true); 5093 mTextView.requestFocus(); 5094 }); 5095 mInstrumentation.waitForIdleSync(); 5096 assertTrue(mTextView.isFocused()); 5097 5098 PollingCheck.waitFor(mTextView::isInputMethodTarget); 5099 } 5100 5101 @UiThreadTest 5102 @Test 5103 public void testBeginEndBatchEditAreNotCalledForNonEditableText() { 5104 final TextView mockTextView = spy(new TextView(mActivity)); 5105 5106 // TextView should not call onBeginBatchEdit or onEndBatchEdit during initialization 5107 verify(mockTextView, never()).onBeginBatchEdit(); 5108 verify(mockTextView, never()).onEndBatchEdit(); 5109 5110 5111 mockTextView.beginBatchEdit(); 5112 // Since TextView doesn't support editing, the callbacks should not be called 5113 verify(mockTextView, never()).onBeginBatchEdit(); 5114 verify(mockTextView, never()).onEndBatchEdit(); 5115 5116 mockTextView.endBatchEdit(); 5117 // Since TextView doesn't support editing, the callbacks should not be called 5118 verify(mockTextView, never()).onBeginBatchEdit(); 5119 verify(mockTextView, never()).onEndBatchEdit(); 5120 } 5121 5122 @Test 5123 public void testBeginEndBatchEditCallbacksAreCalledForEditableText() throws Throwable { 5124 mActivityRule.runOnUiThread(() -> mTextView = spy(new TextView(mActivity))); 5125 mInstrumentation.waitForIdleSync(); 5126 5127 final FrameLayout layout = new FrameLayout(mActivity); 5128 ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 5129 ViewGroup.LayoutParams.MATCH_PARENT, 5130 ViewGroup.LayoutParams.MATCH_PARENT); 5131 layout.addView(mTextView, layoutParams); 5132 layout.setLayoutParams(layoutParams); 5133 5134 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout)); 5135 mInstrumentation.waitForIdleSync(); 5136 5137 mActivityRule.runOnUiThread(() -> { 5138 mTextView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE)); 5139 mTextView.setText("", BufferType.EDITABLE); 5140 mTextView.requestFocus(); 5141 }); 5142 mInstrumentation.waitForIdleSync(); 5143 5144 reset(mTextView); 5145 assertTrue(mTextView.hasFocus()); 5146 verify(mTextView, never()).onBeginBatchEdit(); 5147 verify(mTextView, never()).onEndBatchEdit(); 5148 5149 mTextView.beginBatchEdit(); 5150 5151 verify(mTextView, times(1)).onBeginBatchEdit(); 5152 verify(mTextView, never()).onEndBatchEdit(); 5153 5154 reset(mTextView); 5155 mTextView.endBatchEdit(); 5156 verify(mTextView, never()).onBeginBatchEdit(); 5157 verify(mTextView, times(1)).onEndBatchEdit(); 5158 } 5159 5160 @UiThreadTest 5161 @Test 5162 public void testBringPointIntoView() throws Throwable { 5163 mTextView = findTextView(R.id.textview_text); 5164 assertFalse(mTextView.bringPointIntoView(1)); 5165 5166 mTextView.layout(0, 0, 100, 100); 5167 assertFalse(mTextView.bringPointIntoView(2)); 5168 } 5169 5170 @Test 5171 public void testCancelLongPress() { 5172 mTextView = findTextView(R.id.textview_text); 5173 CtsTouchUtils.emulateLongPressOnViewCenter(mInstrumentation, mTextView); 5174 mTextView.cancelLongPress(); 5175 } 5176 5177 @UiThreadTest 5178 @Test 5179 public void testClearComposingText() { 5180 mTextView = findTextView(R.id.textview_text); 5181 mTextView.setText("Hello world!", BufferType.SPANNABLE); 5182 Spannable text = (Spannable) mTextView.getText(); 5183 5184 assertEquals(-1, BaseInputConnection.getComposingSpanStart(text)); 5185 assertEquals(-1, BaseInputConnection.getComposingSpanStart(text)); 5186 5187 BaseInputConnection.setComposingSpans((Spannable) mTextView.getText()); 5188 assertEquals(0, BaseInputConnection.getComposingSpanStart(text)); 5189 assertEquals(0, BaseInputConnection.getComposingSpanStart(text)); 5190 5191 mTextView.clearComposingText(); 5192 assertEquals(-1, BaseInputConnection.getComposingSpanStart(text)); 5193 assertEquals(-1, BaseInputConnection.getComposingSpanStart(text)); 5194 } 5195 5196 @UiThreadTest 5197 @Test 5198 public void testComputeVerticalScrollExtent() { 5199 mTextView = new MockTextView(mActivity); 5200 assertEquals(0, ((MockTextView) mTextView).computeVerticalScrollExtent()); 5201 5202 Drawable d = TestUtils.getDrawable(mActivity, R.drawable.pass); 5203 mTextView.setCompoundDrawables(null, d, null, d); 5204 5205 assertEquals(0, ((MockTextView) mTextView).computeVerticalScrollExtent()); 5206 } 5207 5208 @UiThreadTest 5209 @Test 5210 public void testDidTouchFocusSelect() { 5211 mTextView = new EditText(mActivity); 5212 assertFalse(mTextView.didTouchFocusSelect()); 5213 5214 mTextView.setFocusable(true); 5215 mTextView.requestFocus(); 5216 assertTrue(mTextView.didTouchFocusSelect()); 5217 } 5218 5219 @Test 5220 public void testSelectAllJustAfterTap() throws Throwable { 5221 // Prepare an EditText with focus. 5222 mActivityRule.runOnUiThread(() -> { 5223 // Make a dummy focusable so that initial focus doesn't go to our test textview 5224 LinearLayout top = new LinearLayout(mActivity); 5225 TextView dummy = new TextView(mActivity); 5226 dummy.setFocusableInTouchMode(true); 5227 top.addView(dummy, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 5228 mTextView = new EditText(mActivity); 5229 top.addView(mTextView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 5230 mActivity.setContentView(top); 5231 5232 assertFalse(mTextView.didTouchFocusSelect()); 5233 mTextView.setFocusable(true); 5234 mTextView.requestFocus(); 5235 assertTrue(mTextView.didTouchFocusSelect()); 5236 5237 mTextView.setText("Hello, World.", BufferType.SPANNABLE); 5238 }); 5239 mInstrumentation.waitForIdleSync(); 5240 5241 // Tap the view to show InsertPointController. 5242 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView); 5243 // bad workaround for waiting onStartInputView of LeanbackIme.apk done 5244 try { 5245 Thread.sleep(1000); 5246 } catch (InterruptedException e) { 5247 e.printStackTrace(); 5248 } 5249 5250 // Execute SelectAll context menu. 5251 mActivityRule.runOnUiThread(() -> mTextView.onTextContextMenuItem(android.R.id.selectAll)); 5252 mInstrumentation.waitForIdleSync(); 5253 5254 // The selection must be whole of the text contents. 5255 assertEquals(0, mTextView.getSelectionStart()); 5256 assertEquals("Hello, World.", mTextView.getText().toString()); 5257 assertEquals(mTextView.length(), mTextView.getSelectionEnd()); 5258 } 5259 5260 @UiThreadTest 5261 @Test 5262 public void testExtractText() { 5263 mTextView = new TextView(mActivity); 5264 5265 ExtractedTextRequest request = new ExtractedTextRequest(); 5266 ExtractedText outText = new ExtractedText(); 5267 5268 request.token = 0; 5269 request.flags = 10; 5270 request.hintMaxLines = 2; 5271 request.hintMaxChars = 20; 5272 assertTrue(mTextView.extractText(request, outText)); 5273 5274 mTextView = findTextView(R.id.textview_text); 5275 assertTrue(mTextView.extractText(request, outText)); 5276 5277 assertEquals(mActivity.getResources().getString(R.string.text_view_hello), 5278 outText.text.toString()); 5279 5280 // Tests for invalid arguments. 5281 assertFalse(mTextView.extractText(request, null)); 5282 assertFalse(mTextView.extractText(null, outText)); 5283 assertFalse(mTextView.extractText(null, null)); 5284 } 5285 5286 @UiThreadTest 5287 @Test 5288 public void testTextDirectionDefault() { 5289 TextView tv = new TextView(mActivity); 5290 assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getRawTextDirection()); 5291 } 5292 5293 @UiThreadTest 5294 @Test 5295 public void testSetGetTextDirection() { 5296 TextView tv = new TextView(mActivity); 5297 5298 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); 5299 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getRawTextDirection()); 5300 5301 tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); 5302 assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getRawTextDirection()); 5303 5304 tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); 5305 assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getRawTextDirection()); 5306 5307 tv.setTextDirection(View.TEXT_DIRECTION_LTR); 5308 assertEquals(View.TEXT_DIRECTION_LTR, tv.getRawTextDirection()); 5309 5310 tv.setTextDirection(View.TEXT_DIRECTION_RTL); 5311 assertEquals(View.TEXT_DIRECTION_RTL, tv.getRawTextDirection()); 5312 5313 tv.setTextDirection(View.TEXT_DIRECTION_LOCALE); 5314 assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getRawTextDirection()); 5315 5316 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5317 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getRawTextDirection()); 5318 5319 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5320 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getRawTextDirection()); 5321 } 5322 5323 @UiThreadTest 5324 @Test 5325 public void testGetResolvedTextDirectionLtr() { 5326 TextView tv = new TextView(mActivity); 5327 tv.setText("this is a test"); 5328 5329 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5330 5331 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); 5332 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5333 5334 tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); 5335 assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection()); 5336 5337 tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); 5338 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5339 5340 tv.setTextDirection(View.TEXT_DIRECTION_LTR); 5341 assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection()); 5342 5343 tv.setTextDirection(View.TEXT_DIRECTION_RTL); 5344 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5345 5346 tv.setTextDirection(View.TEXT_DIRECTION_LOCALE); 5347 assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection()); 5348 5349 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5350 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5351 5352 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5353 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5354 } 5355 5356 @UiThreadTest 5357 @Test 5358 public void testGetResolvedTextDirectionLtrWithInheritance() { 5359 LinearLayout ll = new LinearLayout(mActivity); 5360 ll.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); 5361 5362 TextView tv = new TextView(mActivity); 5363 tv.setText("this is a test"); 5364 ll.addView(tv); 5365 5366 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); 5367 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5368 5369 tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); 5370 assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection()); 5371 5372 tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); 5373 assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection()); 5374 5375 tv.setTextDirection(View.TEXT_DIRECTION_LTR); 5376 assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection()); 5377 5378 tv.setTextDirection(View.TEXT_DIRECTION_RTL); 5379 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5380 5381 tv.setTextDirection(View.TEXT_DIRECTION_LOCALE); 5382 assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection()); 5383 5384 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5385 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5386 5387 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5388 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5389 } 5390 5391 @UiThreadTest 5392 @Test 5393 public void testGetResolvedTextDirectionRtl() { 5394 TextView tv = new TextView(mActivity); 5395 tv.setText("\u05DD\u05DE"); // hebrew 5396 5397 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5398 5399 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); 5400 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5401 5402 tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); 5403 assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection()); 5404 5405 tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); 5406 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5407 5408 tv.setTextDirection(View.TEXT_DIRECTION_LTR); 5409 assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection()); 5410 5411 tv.setTextDirection(View.TEXT_DIRECTION_RTL); 5412 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5413 5414 tv.setTextDirection(View.TEXT_DIRECTION_LOCALE); 5415 assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection()); 5416 5417 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5418 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5419 5420 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5421 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5422 } 5423 5424 @UiThreadTest 5425 @Test 5426 public void testGetResolvedTextDirectionRtlWithInheritance() { 5427 LinearLayout ll = new LinearLayout(mActivity); 5428 ll.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); 5429 5430 TextView tv = new TextView(mActivity); 5431 tv.setText("\u05DD\u05DE"); // hebrew 5432 ll.addView(tv); 5433 5434 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); 5435 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5436 5437 tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); 5438 assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection()); 5439 5440 tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); 5441 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5442 5443 tv.setTextDirection(View.TEXT_DIRECTION_LTR); 5444 assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection()); 5445 5446 tv.setTextDirection(View.TEXT_DIRECTION_RTL); 5447 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5448 5449 tv.setTextDirection(View.TEXT_DIRECTION_LOCALE); 5450 assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection()); 5451 5452 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5453 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5454 5455 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5456 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5457 5458 // Force to RTL text direction on the layout 5459 ll.setTextDirection(View.TEXT_DIRECTION_RTL); 5460 5461 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); 5462 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5463 5464 tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); 5465 assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection()); 5466 5467 tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); 5468 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5469 5470 tv.setTextDirection(View.TEXT_DIRECTION_LTR); 5471 assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection()); 5472 5473 tv.setTextDirection(View.TEXT_DIRECTION_RTL); 5474 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5475 5476 tv.setTextDirection(View.TEXT_DIRECTION_LOCALE); 5477 assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection()); 5478 5479 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5480 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5481 5482 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5483 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5484 } 5485 5486 @UiThreadTest 5487 @Test 5488 public void testResetTextDirection() { 5489 LinearLayout ll = (LinearLayout) mActivity.findViewById(R.id.layout_textviewtest); 5490 TextView tv = (TextView) mActivity.findViewById(R.id.textview_rtl); 5491 5492 ll.setTextDirection(View.TEXT_DIRECTION_RTL); 5493 tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); 5494 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5495 5496 // No reset when we remove the view 5497 ll.removeView(tv); 5498 assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); 5499 5500 // Reset is done when we add the view 5501 ll.addView(tv); 5502 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); 5503 } 5504 5505 @UiThreadTest 5506 @Test 5507 public void testTextDirectionFirstStrongLtr() { 5508 { 5509 // The first directional character is LTR, the paragraph direction is LTR. 5510 LinearLayout ll = new LinearLayout(mActivity); 5511 5512 TextView tv = new TextView(mActivity); 5513 tv.setText("this is a test"); 5514 ll.addView(tv); 5515 5516 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5517 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5518 5519 tv.onPreDraw(); // For freezing layout. 5520 Layout layout = tv.getLayout(); 5521 assertEquals(Layout.DIR_LEFT_TO_RIGHT, layout.getParagraphDirection(0)); 5522 } 5523 { 5524 // The first directional character is RTL, the paragraph direction is RTL. 5525 LinearLayout ll = new LinearLayout(mActivity); 5526 5527 TextView tv = new TextView(mActivity); 5528 tv.setText("\u05DD\u05DE"); // Hebrew 5529 ll.addView(tv); 5530 5531 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5532 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5533 5534 tv.onPreDraw(); // For freezing layout. 5535 Layout layout = tv.getLayout(); 5536 assertEquals(Layout.DIR_RIGHT_TO_LEFT, layout.getParagraphDirection(0)); 5537 } 5538 { 5539 // The first directional character is not a strong directional character, the paragraph 5540 // direction is LTR. 5541 LinearLayout ll = new LinearLayout(mActivity); 5542 5543 TextView tv = new TextView(mActivity); 5544 tv.setText("\uFFFD"); // REPLACEMENT CHARACTER. Neutral direction. 5545 ll.addView(tv); 5546 5547 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR); 5548 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection()); 5549 5550 tv.onPreDraw(); // For freezing layout. 5551 Layout layout = tv.getLayout(); 5552 assertEquals(Layout.DIR_LEFT_TO_RIGHT, layout.getParagraphDirection(0)); 5553 } 5554 } 5555 5556 @UiThreadTest 5557 @Test 5558 public void testTextDirectionFirstStrongRtl() { 5559 { 5560 // The first directional character is LTR, the paragraph direction is LTR. 5561 LinearLayout ll = new LinearLayout(mActivity); 5562 5563 TextView tv = new TextView(mActivity); 5564 tv.setText("this is a test"); 5565 ll.addView(tv); 5566 5567 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5568 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5569 5570 tv.onPreDraw(); // For freezing layout. 5571 Layout layout = tv.getLayout(); 5572 assertEquals(Layout.DIR_LEFT_TO_RIGHT, layout.getParagraphDirection(0)); 5573 } 5574 { 5575 // The first directional character is RTL, the paragraph direction is RTL. 5576 LinearLayout ll = new LinearLayout(mActivity); 5577 5578 TextView tv = new TextView(mActivity); 5579 tv.setText("\u05DD\u05DE"); // Hebrew 5580 ll.addView(tv); 5581 5582 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5583 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5584 5585 tv.onPreDraw(); // For freezing layout. 5586 Layout layout = tv.getLayout(); 5587 assertEquals(Layout.DIR_RIGHT_TO_LEFT, layout.getParagraphDirection(0)); 5588 } 5589 { 5590 // The first directional character is not a strong directional character, the paragraph 5591 // direction is RTL. 5592 LinearLayout ll = new LinearLayout(mActivity); 5593 5594 TextView tv = new TextView(mActivity); 5595 tv.setText("\uFFFD"); // REPLACEMENT CHARACTER. Neutral direction. 5596 ll.addView(tv); 5597 5598 tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL); 5599 assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection()); 5600 5601 tv.onPreDraw(); // For freezing layout. 5602 Layout layout = tv.getLayout(); 5603 assertEquals(Layout.DIR_RIGHT_TO_LEFT, layout.getParagraphDirection(0)); 5604 } 5605 } 5606 5607 @UiThreadTest 5608 @Test 5609 public void testTextLocales() { 5610 TextView tv = new TextView(mActivity); 5611 assertEquals(Locale.getDefault(), tv.getTextLocale()); 5612 assertEquals(LocaleList.getDefault(), tv.getTextLocales()); 5613 5614 tv.setTextLocale(Locale.CHINESE); 5615 assertEquals(Locale.CHINESE, tv.getTextLocale()); 5616 assertEquals(new LocaleList(Locale.CHINESE), tv.getTextLocales()); 5617 5618 tv.setTextLocales(LocaleList.forLanguageTags("en,ja")); 5619 assertEquals(Locale.forLanguageTag("en"), tv.getTextLocale()); 5620 assertEquals(LocaleList.forLanguageTags("en,ja"), tv.getTextLocales()); 5621 5622 try { 5623 tv.setTextLocale(null); 5624 fail("Setting the text locale to null should throw"); 5625 } catch (Throwable e) { 5626 assertEquals(IllegalArgumentException.class, e.getClass()); 5627 } 5628 5629 try { 5630 tv.setTextLocales(null); 5631 fail("Setting the text locales to null should throw"); 5632 } catch (Throwable e) { 5633 assertEquals(IllegalArgumentException.class, e.getClass()); 5634 } 5635 5636 try { 5637 tv.setTextLocales(new LocaleList()); 5638 fail("Setting the text locale to an empty list should throw"); 5639 } catch (Throwable e) { 5640 assertEquals(IllegalArgumentException.class, e.getClass()); 5641 } 5642 } 5643 5644 @UiThreadTest 5645 @Test 5646 public void testAllCaps_Localization() { 5647 final String testString = "abcdefghijklmnopqrstuvwxyz i\u0307\u0301 "; 5648 5649 // Capital "i" in Turkish and Azerbaijani is different from English, Lithuanian has special 5650 // rules for uppercasing dotted i with accents, and Greek has complex capitalization rules. 5651 final Locale[] testLocales = { 5652 new Locale("az", "AZ"), // Azerbaijani 5653 new Locale("tr", "TR"), // Turkish 5654 new Locale("lt", "LT"), // Lithuanian 5655 new Locale("el", "GR"), // Greek 5656 Locale.US, 5657 }; 5658 5659 final TextView tv = new TextView(mActivity); 5660 tv.setAllCaps(true); 5661 for (Locale locale: testLocales) { 5662 tv.setTextLocale(locale); 5663 assertEquals("Locale: " + locale.getDisplayName(), 5664 UCharacter.toUpperCase(locale, testString), 5665 tv.getTransformationMethod().getTransformation(testString, tv).toString()); 5666 } 5667 } 5668 5669 @UiThreadTest 5670 @Test 5671 public void testAllCaps_SpansArePreserved() { 5672 final Locale greek = new Locale("el", "GR"); 5673 final String lowerString = "\u0301"; // with first letter decomposed 5674 final String upperString = ""; // uppercased 5675 // expected lowercase to uppercase index map 5676 final int[] indexMap = {0, 1, 1, 2, 3, 4, 5}; 5677 final int flags = Spanned.SPAN_INCLUSIVE_INCLUSIVE; 5678 5679 final TextView tv = new TextView(mActivity); 5680 tv.setTextLocale(greek); 5681 tv.setAllCaps(true); 5682 5683 final Spannable source = new SpannableString(lowerString); 5684 source.setSpan(new Object(), 0, 1, flags); 5685 source.setSpan(new Object(), 1, 2, flags); 5686 source.setSpan(new Object(), 2, 3, flags); 5687 source.setSpan(new Object(), 3, 4, flags); 5688 source.setSpan(new Object(), 4, 5, flags); 5689 source.setSpan(new Object(), 5, 6, flags); 5690 source.setSpan(new Object(), 0, 2, flags); 5691 source.setSpan(new Object(), 1, 3, flags); 5692 source.setSpan(new Object(), 2, 4, flags); 5693 source.setSpan(new Object(), 0, 6, flags); 5694 final Object[] sourceSpans = source.getSpans(0, source.length(), Object.class); 5695 5696 final CharSequence transformed = 5697 tv.getTransformationMethod().getTransformation(source, tv); 5698 assertTrue(transformed instanceof Spanned); 5699 final Spanned result = (Spanned) transformed; 5700 5701 assertEquals(upperString, transformed.toString()); 5702 final Object[] resultSpans = result.getSpans(0, result.length(), Object.class); 5703 assertEquals(sourceSpans.length, resultSpans.length); 5704 for (int i = 0; i < sourceSpans.length; i++) { 5705 assertSame(sourceSpans[i], resultSpans[i]); 5706 final Object span = sourceSpans[i]; 5707 assertEquals(indexMap[source.getSpanStart(span)], result.getSpanStart(span)); 5708 assertEquals(indexMap[source.getSpanEnd(span)], result.getSpanEnd(span)); 5709 assertEquals(source.getSpanFlags(span), result.getSpanFlags(span)); 5710 } 5711 } 5712 5713 @UiThreadTest 5714 @Test 5715 public void testTextAlignmentDefault() { 5716 TextView tv = new TextView(mActivity); 5717 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getRawTextAlignment()); 5718 // resolved default text alignment is GRAVITY 5719 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getTextAlignment()); 5720 } 5721 5722 @UiThreadTest 5723 @Test 5724 public void testSetGetTextAlignment() { 5725 TextView tv = new TextView(mActivity); 5726 5727 tv.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); 5728 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getRawTextAlignment()); 5729 5730 tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); 5731 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getRawTextAlignment()); 5732 5733 tv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START); 5734 assertEquals(View.TEXT_ALIGNMENT_TEXT_START, tv.getRawTextAlignment()); 5735 5736 tv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END); 5737 assertEquals(View.TEXT_ALIGNMENT_TEXT_END, tv.getRawTextAlignment()); 5738 5739 tv.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); 5740 assertEquals(View.TEXT_ALIGNMENT_VIEW_START, tv.getRawTextAlignment()); 5741 5742 tv.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END); 5743 assertEquals(View.TEXT_ALIGNMENT_VIEW_END, tv.getRawTextAlignment()); 5744 } 5745 5746 @UiThreadTest 5747 @Test 5748 public void testGetResolvedTextAlignment() { 5749 TextView tv = new TextView(mActivity); 5750 5751 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getTextAlignment()); 5752 5753 // Test center alignment first so that we dont hit the default case 5754 tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); 5755 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5756 5757 // Test the default case too 5758 tv.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); 5759 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getTextAlignment()); 5760 5761 tv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START); 5762 assertEquals(View.TEXT_ALIGNMENT_TEXT_START, tv.getTextAlignment()); 5763 5764 tv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END); 5765 assertEquals(View.TEXT_ALIGNMENT_TEXT_END, tv.getTextAlignment()); 5766 5767 tv.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); 5768 assertEquals(View.TEXT_ALIGNMENT_VIEW_START, tv.getTextAlignment()); 5769 5770 tv.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END); 5771 assertEquals(View.TEXT_ALIGNMENT_VIEW_END, tv.getTextAlignment()); 5772 } 5773 5774 @UiThreadTest 5775 @Test 5776 public void testGetResolvedTextAlignmentWithInheritance() { 5777 LinearLayout ll = new LinearLayout(mActivity); 5778 ll.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); 5779 5780 TextView tv = new TextView(mActivity); 5781 ll.addView(tv); 5782 5783 // check defaults 5784 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getRawTextAlignment()); 5785 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getTextAlignment()); 5786 5787 // set inherit and check that child is following parent 5788 tv.setTextAlignment(View.TEXT_ALIGNMENT_INHERIT); 5789 assertEquals(View.TEXT_ALIGNMENT_INHERIT, tv.getRawTextAlignment()); 5790 5791 ll.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); 5792 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5793 5794 ll.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START); 5795 assertEquals(View.TEXT_ALIGNMENT_TEXT_START, tv.getTextAlignment()); 5796 5797 ll.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END); 5798 assertEquals(View.TEXT_ALIGNMENT_TEXT_END, tv.getTextAlignment()); 5799 5800 ll.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); 5801 assertEquals(View.TEXT_ALIGNMENT_VIEW_START, tv.getTextAlignment()); 5802 5803 ll.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END); 5804 assertEquals(View.TEXT_ALIGNMENT_VIEW_END, tv.getTextAlignment()); 5805 5806 // now get rid of the inheritance but still change the parent 5807 tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); 5808 5809 ll.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); 5810 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5811 5812 ll.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START); 5813 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5814 5815 ll.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END); 5816 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5817 5818 ll.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); 5819 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5820 5821 ll.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END); 5822 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5823 } 5824 5825 @UiThreadTest 5826 @Test 5827 public void testResetTextAlignment() { 5828 LinearLayout ll = (LinearLayout) mActivity.findViewById(R.id.layout_textviewtest); 5829 TextView tv = (TextView) mActivity.findViewById(R.id.textview_rtl); 5830 5831 ll.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); 5832 tv.setTextAlignment(View.TEXT_ALIGNMENT_INHERIT); 5833 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5834 5835 // No reset when we remove the view 5836 ll.removeView(tv); 5837 assertEquals(View.TEXT_ALIGNMENT_CENTER, tv.getTextAlignment()); 5838 5839 // Reset is done when we add the view 5840 // Default text alignment is GRAVITY 5841 ll.addView(tv); 5842 assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getTextAlignment()); 5843 } 5844 5845 @UiThreadTest 5846 @Test 5847 public void testDrawableResolution() { 5848 // Case 1.1: left / right drawable defined in default LTR mode 5849 TextView tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_1); 5850 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5851 R.drawable.icon_green, R.drawable.icon_yellow); 5852 TestUtils.verifyCompoundDrawablesRelative(tv, -1, -1, 5853 R.drawable.icon_green, R.drawable.icon_yellow); 5854 5855 // Case 1.2: left / right drawable defined in default RTL mode 5856 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_2); 5857 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5858 R.drawable.icon_green, R.drawable.icon_yellow); 5859 TestUtils.verifyCompoundDrawablesRelative(tv, -1, -1, 5860 R.drawable.icon_green, R.drawable.icon_yellow); 5861 5862 // Case 2.1: start / end drawable defined in LTR mode 5863 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_2_1); 5864 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5865 R.drawable.icon_green, R.drawable.icon_yellow); 5866 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5867 R.drawable.icon_green, R.drawable.icon_yellow); 5868 5869 // Case 2.2: start / end drawable defined in RTL mode 5870 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_2_2); 5871 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue, 5872 R.drawable.icon_green, R.drawable.icon_yellow); 5873 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5874 R.drawable.icon_green, R.drawable.icon_yellow); 5875 5876 // Case 3.1: left / right / start / end drawable defined in LTR mode 5877 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_3_1); 5878 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5879 R.drawable.icon_green, R.drawable.icon_yellow); 5880 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5881 R.drawable.icon_green, R.drawable.icon_yellow); 5882 5883 // Case 3.2: left / right / start / end drawable defined in RTL mode 5884 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_3_2); 5885 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue, 5886 R.drawable.icon_green, R.drawable.icon_yellow); 5887 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5888 R.drawable.icon_green, R.drawable.icon_yellow); 5889 5890 // Case 4.1: start / end drawable defined in LTR mode inside a layout 5891 // that defines the layout direction 5892 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_4_1); 5893 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5894 R.drawable.icon_green, R.drawable.icon_yellow); 5895 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5896 R.drawable.icon_green, R.drawable.icon_yellow); 5897 5898 // Case 4.2: start / end drawable defined in RTL mode inside a layout 5899 // that defines the layout direction 5900 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_4_2); 5901 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue, 5902 R.drawable.icon_green, R.drawable.icon_yellow); 5903 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5904 R.drawable.icon_green, R.drawable.icon_yellow); 5905 5906 // Case 5.1: left / right / start / end drawable defined in LTR mode inside a layout 5907 // that defines the layout direction 5908 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_5_1); 5909 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5910 R.drawable.icon_green, R.drawable.icon_yellow); 5911 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5912 R.drawable.icon_green, R.drawable.icon_yellow); 5913 5914 // Case 5.2: left / right / start / end drawable defined in RTL mode inside a layout 5915 // that defines the layout direction 5916 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_5_2); 5917 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue, 5918 R.drawable.icon_green, R.drawable.icon_yellow); 5919 TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red, 5920 R.drawable.icon_green, R.drawable.icon_yellow); 5921 } 5922 5923 @UiThreadTest 5924 @Test 5925 public void testDrawableResolution2() { 5926 // Case 1.1: left / right drawable defined in default LTR mode 5927 TextView tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_1); 5928 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5929 R.drawable.icon_green, R.drawable.icon_yellow); 5930 5931 tv.setCompoundDrawables(null, null, 5932 TestUtils.getDrawable(mActivity, R.drawable.icon_yellow), null); 5933 TestUtils.verifyCompoundDrawables(tv, -1, R.drawable.icon_yellow, -1, -1); 5934 5935 tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_2); 5936 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, 5937 R.drawable.icon_green, R.drawable.icon_yellow); 5938 5939 tv.setCompoundDrawables(TestUtils.getDrawable(mActivity, R.drawable.icon_yellow), null, 5940 null, null); 5941 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_yellow, -1, -1, -1); 5942 5943 tv = (TextView) mActivity.findViewById(R.id.textview_ltr); 5944 TestUtils.verifyCompoundDrawables(tv, -1, -1, -1, -1); 5945 5946 tv.setCompoundDrawables(TestUtils.getDrawable(mActivity, R.drawable.icon_blue), null, 5947 TestUtils.getDrawable(mActivity, R.drawable.icon_red), null); 5948 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, -1, -1); 5949 5950 tv.setCompoundDrawablesRelative(TestUtils.getDrawable(mActivity, R.drawable.icon_yellow), 5951 null, null, null); 5952 TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_yellow, -1, -1, -1); 5953 } 5954 5955 @Test 5956 public void testCompoundAndTotalPadding() { 5957 final Resources res = mActivity.getResources(); 5958 final int drawablePadding = res.getDimensionPixelSize(R.dimen.textview_drawable_padding); 5959 final int paddingLeft = res.getDimensionPixelSize(R.dimen.textview_padding_left); 5960 final int paddingRight = res.getDimensionPixelSize(R.dimen.textview_padding_right); 5961 final int paddingTop = res.getDimensionPixelSize(R.dimen.textview_padding_top); 5962 final int paddingBottom = res.getDimensionPixelSize(R.dimen.textview_padding_bottom); 5963 final int iconSize = TestUtils.dpToPx(mActivity, 32); 5964 5965 final TextView textViewLtr = (TextView) mActivity.findViewById( 5966 R.id.textview_compound_drawable_ltr); 5967 final int combinedPaddingLeftLtr = paddingLeft + drawablePadding + iconSize; 5968 final int combinedPaddingRightLtr = paddingRight + drawablePadding + iconSize; 5969 assertEquals(combinedPaddingLeftLtr, textViewLtr.getCompoundPaddingLeft()); 5970 assertEquals(combinedPaddingLeftLtr, textViewLtr.getCompoundPaddingStart()); 5971 assertEquals(combinedPaddingLeftLtr, textViewLtr.getTotalPaddingLeft()); 5972 assertEquals(combinedPaddingLeftLtr, textViewLtr.getTotalPaddingStart()); 5973 assertEquals(combinedPaddingRightLtr, textViewLtr.getCompoundPaddingRight()); 5974 assertEquals(combinedPaddingRightLtr, textViewLtr.getCompoundPaddingEnd()); 5975 assertEquals(combinedPaddingRightLtr, textViewLtr.getTotalPaddingRight()); 5976 assertEquals(combinedPaddingRightLtr, textViewLtr.getTotalPaddingEnd()); 5977 assertEquals(paddingTop + drawablePadding + iconSize, 5978 textViewLtr.getCompoundPaddingTop()); 5979 assertEquals(paddingBottom + drawablePadding + iconSize, 5980 textViewLtr.getCompoundPaddingBottom()); 5981 5982 final TextView textViewRtl = (TextView) mActivity.findViewById( 5983 R.id.textview_compound_drawable_rtl); 5984 final int combinedPaddingLeftRtl = paddingLeft + drawablePadding + iconSize; 5985 final int combinedPaddingRightRtl = paddingRight + drawablePadding + iconSize; 5986 assertEquals(combinedPaddingLeftRtl, textViewRtl.getCompoundPaddingLeft()); 5987 assertEquals(combinedPaddingLeftRtl, textViewRtl.getCompoundPaddingEnd()); 5988 assertEquals(combinedPaddingLeftRtl, textViewRtl.getTotalPaddingLeft()); 5989 assertEquals(combinedPaddingLeftRtl, textViewRtl.getTotalPaddingEnd()); 5990 assertEquals(combinedPaddingRightRtl, textViewRtl.getCompoundPaddingRight()); 5991 assertEquals(combinedPaddingRightRtl, textViewRtl.getCompoundPaddingStart()); 5992 assertEquals(combinedPaddingRightRtl, textViewRtl.getTotalPaddingRight()); 5993 assertEquals(combinedPaddingRightRtl, textViewRtl.getTotalPaddingStart()); 5994 assertEquals(paddingTop + drawablePadding + iconSize, 5995 textViewRtl.getCompoundPaddingTop()); 5996 assertEquals(paddingBottom + drawablePadding + iconSize, 5997 textViewRtl.getCompoundPaddingBottom()); 5998 } 5999 6000 @UiThreadTest 6001 @Test 6002 public void testSetGetBreakStrategy() { 6003 TextView tv = new TextView(mActivity); 6004 6005 final PackageManager pm = mInstrumentation.getTargetContext().getPackageManager(); 6006 6007 // The default value is from the theme, here the default is BREAK_STRATEGY_HIGH_QUALITY for 6008 // TextView except for Android Wear. The default value for Android Wear is 6009 // BREAK_STRATEGY_BALANCED. 6010 if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 6011 // Android Wear 6012 assertEquals(Layout.BREAK_STRATEGY_BALANCED, tv.getBreakStrategy()); 6013 } else { 6014 // All other form factor. 6015 assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy()); 6016 } 6017 6018 tv.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE); 6019 assertEquals(Layout.BREAK_STRATEGY_SIMPLE, tv.getBreakStrategy()); 6020 6021 tv.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY); 6022 assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy()); 6023 6024 tv.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED); 6025 assertEquals(Layout.BREAK_STRATEGY_BALANCED, tv.getBreakStrategy()); 6026 6027 EditText et = new EditText(mActivity); 6028 6029 // The default value is from the theme, here the default is BREAK_STRATEGY_SIMPLE for 6030 // EditText. 6031 assertEquals(Layout.BREAK_STRATEGY_SIMPLE, et.getBreakStrategy()); 6032 6033 et.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE); 6034 assertEquals(Layout.BREAK_STRATEGY_SIMPLE, et.getBreakStrategy()); 6035 6036 et.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY); 6037 assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, et.getBreakStrategy()); 6038 6039 et.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED); 6040 assertEquals(Layout.BREAK_STRATEGY_BALANCED, et.getBreakStrategy()); 6041 } 6042 6043 @UiThreadTest 6044 @Test 6045 public void testSetGetHyphenationFrequency() { 6046 TextView tv = new TextView(mActivity); 6047 6048 assertEquals(Layout.HYPHENATION_FREQUENCY_NORMAL, tv.getHyphenationFrequency()); 6049 6050 tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE); 6051 assertEquals(Layout.HYPHENATION_FREQUENCY_NONE, tv.getHyphenationFrequency()); 6052 6053 tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); 6054 assertEquals(Layout.HYPHENATION_FREQUENCY_NORMAL, tv.getHyphenationFrequency()); 6055 6056 tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); 6057 assertEquals(Layout.HYPHENATION_FREQUENCY_FULL, tv.getHyphenationFrequency()); 6058 } 6059 6060 @UiThreadTest 6061 @Test 6062 public void testSetGetJustify() { 6063 TextView tv = new TextView(mActivity); 6064 6065 assertEquals(Layout.JUSTIFICATION_MODE_NONE, tv.getJustificationMode()); 6066 tv.setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD); 6067 assertEquals(Layout.JUSTIFICATION_MODE_INTER_WORD, tv.getJustificationMode()); 6068 tv.setJustificationMode(Layout.JUSTIFICATION_MODE_NONE); 6069 assertEquals(Layout.JUSTIFICATION_MODE_NONE, tv.getJustificationMode()); 6070 } 6071 6072 @Test 6073 public void testJustificationByStyle() { 6074 TextView defaultTv = findTextView(R.id.textview_justification_default); 6075 TextView noneTv = findTextView(R.id.textview_justification_none); 6076 TextView interWordTv = findTextView(R.id.textview_justification_inter_word); 6077 6078 assertEquals(Layout.JUSTIFICATION_MODE_NONE, defaultTv.getJustificationMode()); 6079 assertEquals(Layout.JUSTIFICATION_MODE_NONE, noneTv.getJustificationMode()); 6080 assertEquals(Layout.JUSTIFICATION_MODE_INTER_WORD, interWordTv.getJustificationMode()); 6081 } 6082 6083 @Test 6084 public void testSetAndGetCustomSelectionActionModeCallback() throws Throwable { 6085 final String text = "abcde"; 6086 mActivityRule.runOnUiThread(() -> { 6087 mTextView = new EditText(mActivity); 6088 mActivity.setContentView(mTextView); 6089 mTextView.setText(text, BufferType.SPANNABLE); 6090 mTextView.setTextIsSelectable(true); 6091 mTextView.requestFocus(); 6092 mTextView.setSelected(true); 6093 mTextView.setTextClassifier(TextClassifier.NO_OP); 6094 }); 6095 mInstrumentation.waitForIdleSync(); 6096 6097 // Check default value. 6098 assertNull(mTextView.getCustomSelectionActionModeCallback()); 6099 6100 final ActionMode.Callback mockActionModeCallback = mock(ActionMode.Callback.class); 6101 when(mockActionModeCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))). 6102 thenReturn(Boolean.FALSE); 6103 mTextView.setCustomSelectionActionModeCallback(mockActionModeCallback); 6104 assertEquals(mockActionModeCallback, 6105 mTextView.getCustomSelectionActionModeCallback()); 6106 6107 mActivityRule.runOnUiThread(() -> { 6108 // Set selection and try to start action mode. 6109 final Bundle args = new Bundle(); 6110 args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0); 6111 args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length()); 6112 mTextView.performAccessibilityAction( 6113 AccessibilityNodeInfo.ACTION_SET_SELECTION, args); 6114 }); 6115 mInstrumentation.waitForIdleSync(); 6116 6117 verify(mockActionModeCallback, times(1)).onCreateActionMode( 6118 any(ActionMode.class), any(Menu.class)); 6119 6120 mActivityRule.runOnUiThread(() -> { 6121 // Remove selection and stop action mode. 6122 mTextView.onTextContextMenuItem(android.R.id.copy); 6123 }); 6124 mInstrumentation.waitForIdleSync(); 6125 6126 // Action mode was blocked. 6127 verify(mockActionModeCallback, never()).onDestroyActionMode(any(ActionMode.class)); 6128 6129 // Reset and reconfigure callback. 6130 reset(mockActionModeCallback); 6131 when(mockActionModeCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))). 6132 thenReturn(Boolean.TRUE); 6133 assertEquals(mockActionModeCallback, mTextView.getCustomSelectionActionModeCallback()); 6134 6135 mActivityRule.runOnUiThread(() -> { 6136 // Set selection and try to start action mode. 6137 final Bundle args = new Bundle(); 6138 args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0); 6139 args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length()); 6140 mTextView.performAccessibilityAction( 6141 AccessibilityNodeInfo.ACTION_SET_SELECTION, args); 6142 6143 }); 6144 mInstrumentation.waitForIdleSync(); 6145 6146 verify(mockActionModeCallback, times(1)).onCreateActionMode( 6147 any(ActionMode.class), any(Menu.class)); 6148 6149 mActivityRule.runOnUiThread(() -> { 6150 // Remove selection and stop action mode. 6151 mTextView.onTextContextMenuItem(android.R.id.copy); 6152 }); 6153 mInstrumentation.waitForIdleSync(); 6154 6155 // Action mode was started 6156 verify(mockActionModeCallback, times(1)).onDestroyActionMode(any(ActionMode.class)); 6157 } 6158 6159 @UiThreadTest 6160 @Test 6161 public void testSetAndGetCustomInsertionActionMode() { 6162 initTextViewForTyping(); 6163 // Check default value. 6164 assertNull(mTextView.getCustomInsertionActionModeCallback()); 6165 6166 final ActionMode.Callback mockActionModeCallback = mock(ActionMode.Callback.class); 6167 when(mockActionModeCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))). 6168 thenReturn(Boolean.FALSE); 6169 mTextView.setCustomInsertionActionModeCallback(mockActionModeCallback); 6170 assertEquals(mockActionModeCallback, mTextView.getCustomInsertionActionModeCallback()); 6171 // TODO(Bug: 22033189): Tests the set callback is actually used. 6172 } 6173 6174 @UiThreadTest 6175 @Test 6176 public void testRespectsViewFocusability() { 6177 TextView v = (TextView) mActivity.findViewById(R.id.textview_singleLine); 6178 assertFalse(v.isFocusable()); 6179 // TextView used to set focusable to true or false verbatim which would break the following. 6180 v.setClickable(true); 6181 assertTrue(v.isFocusable()); 6182 } 6183 6184 @Test 6185 public void testTextShadows() throws Throwable { 6186 final TextView textViewWithConfiguredShadow = 6187 (TextView) mActivity.findViewById(R.id.textview_with_shadow); 6188 assertEquals(1.0f, textViewWithConfiguredShadow.getShadowDx(), 0.0f); 6189 assertEquals(2.0f, textViewWithConfiguredShadow.getShadowDy(), 0.0f); 6190 assertEquals(3.0f, textViewWithConfiguredShadow.getShadowRadius(), 0.0f); 6191 assertEquals(Color.GREEN, textViewWithConfiguredShadow.getShadowColor()); 6192 6193 final TextView textView = (TextView) mActivity.findViewById(R.id.textview_text); 6194 assertEquals(0.0f, textView.getShadowDx(), 0.0f); 6195 assertEquals(0.0f, textView.getShadowDy(), 0.0f); 6196 assertEquals(0.0f, textView.getShadowRadius(), 0.0f); 6197 6198 mActivityRule.runOnUiThread(() -> textView.setShadowLayer(5.0f, 3.0f, 4.0f, Color.RED)); 6199 mInstrumentation.waitForIdleSync(); 6200 assertEquals(3.0f, textView.getShadowDx(), 0.0f); 6201 assertEquals(4.0f, textView.getShadowDy(), 0.0f); 6202 assertEquals(5.0f, textView.getShadowRadius(), 0.0f); 6203 assertEquals(Color.RED, textView.getShadowColor()); 6204 } 6205 6206 @Test 6207 public void testFontFeatureSettings() throws Throwable { 6208 final TextView textView = (TextView) mActivity.findViewById(R.id.textview_text); 6209 assertTrue(TextUtils.isEmpty(textView.getFontFeatureSettings())); 6210 6211 mActivityRule.runOnUiThread(() -> textView.setFontFeatureSettings("smcp")); 6212 mInstrumentation.waitForIdleSync(); 6213 assertEquals("smcp", textView.getFontFeatureSettings()); 6214 6215 mActivityRule.runOnUiThread(() -> textView.setFontFeatureSettings("frac")); 6216 mInstrumentation.waitForIdleSync(); 6217 assertEquals("frac", textView.getFontFeatureSettings()); 6218 } 6219 6220 private static class SoftInputResultReceiver extends ResultReceiver { 6221 private boolean mIsDone; 6222 private int mResultCode; 6223 6224 public SoftInputResultReceiver(Handler handler) { 6225 super(handler); 6226 } 6227 6228 @Override 6229 protected void onReceiveResult(int resultCode, Bundle resultData) { 6230 mResultCode = resultCode; 6231 mIsDone = true; 6232 } 6233 6234 public void reset() { 6235 mIsDone = false; 6236 } 6237 } 6238 6239 @Test 6240 public void testAccessShowSoftInputOnFocus() throws Throwable { 6241 if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) { 6242 return; 6243 } 6244 6245 // prepare a test Layout 6246 // will add an focusable TextView so that EditText will not get focus at activity start 6247 final TextView textView = new TextView(mActivity); 6248 textView.setFocusable(true); 6249 textView.setFocusableInTouchMode(true); 6250 // EditText to test 6251 final EditText editText = new EditText(mActivity); 6252 editText.setShowSoftInputOnFocus(true); 6253 editText.setFocusable(true); 6254 editText.setFocusableInTouchMode(true); 6255 // prepare and set the layout 6256 final LinearLayout layout = new LinearLayout(mActivity); 6257 layout.setOrientation(LinearLayout.VERTICAL); 6258 layout.addView(textView, new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, 6259 LayoutParams.WRAP_CONTENT)); 6260 layout.addView(editText, new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, 6261 LayoutParams.WRAP_CONTENT)); 6262 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout, 6263 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 6264 ViewGroup.LayoutParams.MATCH_PARENT))); 6265 mInstrumentation.waitForIdleSync(); 6266 6267 assertTrue(editText.getShowSoftInputOnFocus()); 6268 6269 // And emulate click on it 6270 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, editText); 6271 6272 // Verify that input method manager is active and accepting text 6273 final InputMethodManager imManager = (InputMethodManager) mActivity 6274 .getSystemService(Context.INPUT_METHOD_SERVICE); 6275 PollingCheck.waitFor(imManager::isActive); 6276 assertTrue(imManager.isAcceptingText()); 6277 assertTrue(imManager.isActive(editText)); 6278 6279 // Since there is no API to check that soft input is showing, we're going to ask 6280 // the input method manager to show soft input, passing our custom result receiver. 6281 // We're expecting to get UNCHANGED_SHOWN, indicating that the soft input was already 6282 // showing before showSoftInput was called. 6283 SoftInputResultReceiver receiver = new SoftInputResultReceiver(mHandler); 6284 imManager.showSoftInput(editText, 0, receiver); 6285 PollingCheck.waitFor(() -> receiver.mIsDone); 6286 assertEquals(InputMethodManager.RESULT_UNCHANGED_SHOWN, receiver.mResultCode); 6287 6288 // Close soft input 6289 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); 6290 6291 // Reconfigure our edit text to not show soft input on focus 6292 mActivityRule.runOnUiThread(() -> editText.setShowSoftInputOnFocus(false)); 6293 mInstrumentation.waitForIdleSync(); 6294 assertFalse(editText.getShowSoftInputOnFocus()); 6295 6296 // Emulate click on it 6297 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, editText); 6298 6299 // Ask input method manager to show soft input again. This time we're expecting to get 6300 // SHOWN, indicating that the soft input was not showing before showSoftInput was called. 6301 receiver.reset(); 6302 imManager.showSoftInput(editText, 0, receiver); 6303 PollingCheck.waitFor(() -> receiver.mIsDone); 6304 assertEquals(InputMethodManager.RESULT_SHOWN, receiver.mResultCode); 6305 6306 // Close soft input 6307 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); 6308 } 6309 6310 @Test 6311 public void testIsSuggestionsEnabled() throws Throwable { 6312 mTextView = findTextView(R.id.textview_text); 6313 6314 // Anything without InputType.TYPE_CLASS_TEXT doesn't have suggestions enabled 6315 mActivityRule.runOnUiThread(() -> mTextView.setInputType(InputType.TYPE_CLASS_DATETIME)); 6316 assertFalse(mTextView.isSuggestionsEnabled()); 6317 6318 mActivityRule.runOnUiThread(() -> mTextView.setInputType(InputType.TYPE_CLASS_PHONE)); 6319 assertFalse(mTextView.isSuggestionsEnabled()); 6320 6321 mActivityRule.runOnUiThread(() -> mTextView.setInputType(InputType.TYPE_CLASS_NUMBER)); 6322 assertFalse(mTextView.isSuggestionsEnabled()); 6323 6324 // From this point our text view has InputType.TYPE_CLASS_TEXT 6325 6326 // Anything with InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS doesn't have suggestions enabled 6327 mActivityRule.runOnUiThread( 6328 () -> mTextView.setInputType( 6329 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)); 6330 assertFalse(mTextView.isSuggestionsEnabled()); 6331 6332 mActivityRule.runOnUiThread( 6333 () -> mTextView.setInputType( 6334 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL | 6335 InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)); 6336 assertFalse(mTextView.isSuggestionsEnabled()); 6337 6338 mActivityRule.runOnUiThread( 6339 () -> mTextView.setInputType( 6340 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | 6341 InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)); 6342 assertFalse(mTextView.isSuggestionsEnabled()); 6343 6344 // Otherwise suggestions are enabled for specific type variations enumerated in the 6345 // documentation of TextView.isSuggestionsEnabled 6346 mActivityRule.runOnUiThread( 6347 () -> mTextView.setInputType( 6348 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL)); 6349 assertTrue(mTextView.isSuggestionsEnabled()); 6350 6351 mActivityRule.runOnUiThread( 6352 () -> mTextView.setInputType( 6353 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT)); 6354 assertTrue(mTextView.isSuggestionsEnabled()); 6355 6356 mActivityRule.runOnUiThread( 6357 () -> mTextView.setInputType( 6358 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE)); 6359 assertTrue(mTextView.isSuggestionsEnabled()); 6360 6361 mActivityRule.runOnUiThread( 6362 () -> mTextView.setInputType( 6363 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); 6364 assertTrue(mTextView.isSuggestionsEnabled()); 6365 6366 mActivityRule.runOnUiThread( 6367 () -> mTextView.setInputType( 6368 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT)); 6369 assertTrue(mTextView.isSuggestionsEnabled()); 6370 6371 // and not on any other type variation 6372 mActivityRule.runOnUiThread( 6373 () -> mTextView.setInputType( 6374 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS)); 6375 assertFalse(mTextView.isSuggestionsEnabled()); 6376 6377 mActivityRule.runOnUiThread( 6378 () -> mTextView.setInputType( 6379 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_FILTER)); 6380 assertFalse(mTextView.isSuggestionsEnabled()); 6381 6382 mActivityRule.runOnUiThread( 6383 () -> mTextView.setInputType( 6384 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)); 6385 assertFalse(mTextView.isSuggestionsEnabled()); 6386 6387 mActivityRule.runOnUiThread( 6388 () -> mTextView.setInputType( 6389 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME)); 6390 assertFalse(mTextView.isSuggestionsEnabled()); 6391 6392 mActivityRule.runOnUiThread( 6393 () -> mTextView.setInputType( 6394 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PHONETIC)); 6395 assertFalse(mTextView.isSuggestionsEnabled()); 6396 6397 mActivityRule.runOnUiThread( 6398 () -> mTextView.setInputType( 6399 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS)); 6400 assertFalse(mTextView.isSuggestionsEnabled()); 6401 6402 mActivityRule.runOnUiThread( 6403 () -> mTextView.setInputType( 6404 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI)); 6405 assertFalse(mTextView.isSuggestionsEnabled()); 6406 6407 mActivityRule.runOnUiThread( 6408 () -> mTextView.setInputType( 6409 InputType.TYPE_CLASS_TEXT | 6410 InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)); 6411 assertFalse(mTextView.isSuggestionsEnabled()); 6412 6413 mActivityRule.runOnUiThread( 6414 () -> mTextView.setInputType( 6415 InputType.TYPE_CLASS_TEXT | 6416 InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS)); 6417 assertFalse(mTextView.isSuggestionsEnabled()); 6418 6419 mActivityRule.runOnUiThread( 6420 () -> mTextView.setInputType( 6421 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)); 6422 assertFalse(mTextView.isSuggestionsEnabled()); 6423 } 6424 6425 @Test 6426 public void testAccessLetterSpacing() throws Throwable { 6427 mTextView = findTextView(R.id.textview_text); 6428 assertEquals(0.0f, mTextView.getLetterSpacing(), 0.0f); 6429 6430 final CharSequence text = mTextView.getText(); 6431 final int textLength = text.length(); 6432 6433 // Get advance widths of each character at the default letter spacing 6434 final float[] initialWidths = new float[textLength]; 6435 mTextView.getPaint().getTextWidths(text.toString(), initialWidths); 6436 6437 // Get advance widths of each character at letter spacing = 1.0f 6438 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, 6439 () -> mTextView.setLetterSpacing(1.0f)); 6440 assertEquals(1.0f, mTextView.getLetterSpacing(), 0.0f); 6441 final float[] singleWidths = new float[textLength]; 6442 mTextView.getPaint().getTextWidths(text.toString(), singleWidths); 6443 6444 // Get advance widths of each character at letter spacing = 2.0f 6445 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, 6446 () -> mTextView.setLetterSpacing(2.0f)); 6447 assertEquals(2.0f, mTextView.getLetterSpacing(), 0.0f); 6448 final float[] doubleWidths = new float[textLength]; 6449 mTextView.getPaint().getTextWidths(text.toString(), doubleWidths); 6450 6451 // Since letter spacing setter treats the parameter as EM units, and we don't have 6452 // a way to convert EMs into pixels, go over the three arrays of advance widths and 6453 // test that the extra advance width at letter spacing 2.0f is double the extra 6454 // advance width at letter spacing 1.0f. 6455 for (int i = 0; i < textLength; i++) { 6456 float singleWidthDelta = singleWidths[i] - initialWidths[i]; 6457 float doubleWidthDelta = doubleWidths[i] - initialWidths[i]; 6458 assertEquals("At index " + i + " initial is " + initialWidths[i] + 6459 ", single is " + singleWidths[i] + " and double is " + doubleWidths[i], 6460 singleWidthDelta * 2.0f, doubleWidthDelta, 0.05f); 6461 } 6462 } 6463 6464 @Test 6465 public void testTextIsSelectableFocusAndOnClick() throws Throwable { 6466 // Prepare a focusable TextView with an onClickListener attached. 6467 final View.OnClickListener mockOnClickListener = mock(View.OnClickListener.class); 6468 final int safeDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout() + 1; 6469 mActivityRule.runOnUiThread(() -> { 6470 // set up a dummy focusable so that initial focus doesn't go to our test textview 6471 LinearLayout top = new LinearLayout(mActivity); 6472 TextView dummy = new TextView(mActivity); 6473 dummy.setFocusableInTouchMode(true); 6474 top.addView(dummy, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 6475 mTextView = new TextView(mActivity); 6476 mTextView.setText("...text 11:11. some more text is in here..."); 6477 mTextView.setFocusable(true); 6478 mTextView.setOnClickListener(mockOnClickListener); 6479 top.addView(mTextView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 6480 mActivity.setContentView(top); 6481 }); 6482 mInstrumentation.waitForIdleSync(); 6483 assertTrue(mTextView.isFocusable()); 6484 assertFalse(mTextView.isTextSelectable()); 6485 assertFalse(mTextView.isFocusableInTouchMode()); 6486 assertFalse(mTextView.isFocused()); 6487 assertFalse(mTextView.isInTouchMode()); 6488 6489 // First tap on the view triggers onClick() but does not focus the TextView. 6490 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView); 6491 SystemClock.sleep(safeDoubleTapTimeout); 6492 assertTrue(mTextView.isInTouchMode()); 6493 assertFalse(mTextView.isFocused()); 6494 verify(mockOnClickListener, times(1)).onClick(mTextView); 6495 reset(mockOnClickListener); 6496 // So does the second tap. 6497 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView); 6498 SystemClock.sleep(safeDoubleTapTimeout); 6499 assertTrue(mTextView.isInTouchMode()); 6500 assertFalse(mTextView.isFocused()); 6501 verify(mockOnClickListener, times(1)).onClick(mTextView); 6502 6503 mActivityRule.runOnUiThread(() -> mTextView.setTextIsSelectable(true)); 6504 mInstrumentation.waitForIdleSync(); 6505 assertTrue(mTextView.isFocusable()); 6506 assertTrue(mTextView.isTextSelectable()); 6507 assertTrue(mTextView.isFocusableInTouchMode()); 6508 assertFalse(mTextView.isFocused()); 6509 6510 // First tap on the view focuses the TextView but does not trigger onClick(). 6511 reset(mockOnClickListener); 6512 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView); 6513 SystemClock.sleep(safeDoubleTapTimeout); 6514 assertTrue(mTextView.isInTouchMode()); 6515 assertTrue(mTextView.isFocused()); 6516 verify(mockOnClickListener, never()).onClick(mTextView); 6517 reset(mockOnClickListener); 6518 // The second tap triggers onClick() and keeps the focus. 6519 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView); 6520 SystemClock.sleep(safeDoubleTapTimeout); 6521 assertTrue(mTextView.isInTouchMode()); 6522 assertTrue(mTextView.isFocused()); 6523 verify(mockOnClickListener, times(1)).onClick(mTextView); 6524 } 6525 6526 private void verifyGetOffsetForPosition(final int x, final int y) { 6527 final int actual = mTextView.getOffsetForPosition(x, y); 6528 6529 final Layout layout = mTextView.getLayout(); 6530 if (layout == null) { 6531 assertEquals("For [" + x + ", " + y + "]", -1, actual); 6532 return; 6533 } 6534 6535 // Get the line which corresponds to the Y position 6536 final int line = layout.getLineForVertical(y + mTextView.getScrollY()); 6537 // Get the offset in that line that corresponds to the X position 6538 final int expected = layout.getOffsetForHorizontal(line, x + mTextView.getScrollX()); 6539 assertEquals("For [" + x + ", " + y + "]", expected, actual); 6540 } 6541 6542 @Test 6543 public void testGetOffsetForPosition() throws Throwable { 6544 mTextView = findTextView(R.id.textview_text); 6545 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, () -> { 6546 mTextView.setText(LONG_TEXT); 6547 mTextView.setPadding(0, 0, 0, 0); 6548 }); 6549 6550 assertNotNull(mTextView.getLayout()); 6551 final int viewWidth = mTextView.getWidth(); 6552 final int viewHeight = mTextView.getHeight(); 6553 final int lineHeight = mTextView.getLineHeight(); 6554 6555 verifyGetOffsetForPosition(0, 0); 6556 verifyGetOffsetForPosition(0, viewHeight / 2); 6557 verifyGetOffsetForPosition(viewWidth / 3, lineHeight / 2); 6558 verifyGetOffsetForPosition(viewWidth / 2, viewHeight / 2); 6559 verifyGetOffsetForPosition(viewWidth, viewHeight); 6560 } 6561 6562 @UiThreadTest 6563 @Test 6564 public void testOnResolvePointerIcon() throws InterruptedException { 6565 final TextView selectableTextView = findTextView(R.id.textview_pointer); 6566 final MotionEvent event = createMouseHoverEvent(selectableTextView); 6567 6568 // A selectable view shows the I beam 6569 selectableTextView.setTextIsSelectable(true); 6570 6571 assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT), 6572 selectableTextView.onResolvePointerIcon(event, 0)); 6573 selectableTextView.setTextIsSelectable(false); 6574 6575 // A clickable view shows the hand 6576 selectableTextView.setLinksClickable(true); 6577 SpannableString builder = new SpannableString("hello world"); 6578 selectableTextView.setText(builder, BufferType.SPANNABLE); 6579 Spannable text = (Spannable) selectableTextView.getText(); 6580 text.setSpan( 6581 new ClickableSpan() { 6582 @Override 6583 public void onClick(View widget) { 6584 6585 } 6586 }, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 6587 6588 assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HAND), 6589 selectableTextView.onResolvePointerIcon(event, 0)); 6590 6591 // A selectable & clickable view shows hand 6592 selectableTextView.setTextIsSelectable(true); 6593 6594 assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HAND), 6595 selectableTextView.onResolvePointerIcon(event, 0)); 6596 6597 // An editable view shows the I-beam 6598 final TextView editableTextView = new EditText(mActivity); 6599 6600 assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT), 6601 editableTextView.onResolvePointerIcon(event, 0)); 6602 } 6603 6604 @Test 6605 public void testClickableSpanOnClickSingleTapInside() throws Throwable { 6606 ClickableSpanTestDetails spanDetails = prepareAndRetrieveClickableSpanDetails(); 6607 CtsTouchUtils.emulateTapOnView(mInstrumentation, mTextView, spanDetails.mXPosInside, 6608 spanDetails.mYPosInside); 6609 verify(spanDetails.mClickableSpan, times(1)).onClick(mTextView); 6610 } 6611 6612 @Test 6613 public void testClickableSpanOnClickDoubleTapInside() throws Throwable { 6614 ClickableSpanTestDetails spanDetails = prepareAndRetrieveClickableSpanDetails(); 6615 CtsTouchUtils.emulateDoubleTapOnView(mInstrumentation, mTextView, spanDetails.mXPosInside, 6616 spanDetails.mYPosInside); 6617 verify(spanDetails.mClickableSpan, times(2)).onClick(mTextView); 6618 } 6619 6620 @Test 6621 public void testClickableSpanOnClickSingleTapOutside() throws Throwable { 6622 ClickableSpanTestDetails spanDetails = prepareAndRetrieveClickableSpanDetails(); 6623 CtsTouchUtils.emulateTapOnView(mInstrumentation, mTextView, spanDetails.mXPosOutside, 6624 spanDetails.mYPosOutside); 6625 verify(spanDetails.mClickableSpan, never()).onClick(mTextView); 6626 } 6627 6628 @Test 6629 public void testClickableSpanOnClickDragOutside() throws Throwable { 6630 ClickableSpanTestDetails spanDetails = prepareAndRetrieveClickableSpanDetails(); 6631 final int[] viewOnScreenXY = new int[2]; 6632 mTextView.getLocationOnScreen(viewOnScreenXY); 6633 6634 SparseArray<Point> swipeCoordinates = new SparseArray<>(); 6635 swipeCoordinates.put(0, new Point(viewOnScreenXY[0] + spanDetails.mXPosOutside, 6636 viewOnScreenXY[1] + spanDetails.mYPosOutside)); 6637 swipeCoordinates.put(1, new Point(viewOnScreenXY[0] + spanDetails.mXPosOutside + 50, 6638 viewOnScreenXY[1] + spanDetails.mYPosOutside + 50)); 6639 CtsTouchUtils.emulateDragGesture(mInstrumentation, swipeCoordinates); 6640 verify(spanDetails.mClickableSpan, never()).onClick(mTextView); 6641 } 6642 6643 @UiThreadTest 6644 @Test 6645 public void testOnInitializeA11yNodeInfo_populatesHintTextProperly() { 6646 final TextView textView = new TextView(mActivity); 6647 textView.setText("", BufferType.EDITABLE); 6648 final String hintText = "Hint text"; 6649 textView.setHint(hintText); 6650 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 6651 textView.onInitializeAccessibilityNodeInfo(info); 6652 assertTrue("Hint text flag set incorrectly for accessibility", info.isShowingHintText()); 6653 assertTrue("Hint text not showing as accessibility text", 6654 TextUtils.equals(hintText, info.getText())); 6655 assertTrue("Hint text not provided to accessibility", 6656 TextUtils.equals(hintText, info.getHintText())); 6657 6658 final String nonHintText = "Something else"; 6659 textView.setText(nonHintText, BufferType.EDITABLE); 6660 textView.onInitializeAccessibilityNodeInfo(info); 6661 assertFalse("Hint text flag set incorrectly for accessibility", info.isShowingHintText()); 6662 assertTrue("Text not provided to accessibility", 6663 TextUtils.equals(nonHintText, info.getText())); 6664 assertTrue("Hint text not provided to accessibility", 6665 TextUtils.equals(hintText, info.getHintText())); 6666 } 6667 6668 @Test 6669 public void testAutosizeWithMaxLines_shouldNotThrowException() throws Throwable { 6670 // the layout contains an instance of CustomTextViewWithTransformationMethod 6671 final TextView textView = (TextView) mActivity.getLayoutInflater() 6672 .inflate(R.layout.textview_autosize_maxlines, null); 6673 assertTrue(textView instanceof CustomTextViewWithTransformationMethod); 6674 assertEquals(1, textView.getMaxLines()); 6675 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType()); 6676 assertTrue(textView.getTransformationMethod() instanceof SingleLineTransformationMethod); 6677 } 6678 6679 public static class CustomTextViewWithTransformationMethod extends TextView { 6680 public CustomTextViewWithTransformationMethod(Context context) { 6681 super(context); 6682 init(); 6683 } 6684 6685 public CustomTextViewWithTransformationMethod(Context context, 6686 @Nullable AttributeSet attrs) { 6687 super(context, attrs); 6688 init(); 6689 } 6690 6691 public CustomTextViewWithTransformationMethod(Context context, 6692 @Nullable AttributeSet attrs, int defStyleAttr) { 6693 super(context, attrs, defStyleAttr); 6694 init(); 6695 } 6696 6697 public CustomTextViewWithTransformationMethod(Context context, 6698 @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 6699 super(context, attrs, defStyleAttr, defStyleRes); 6700 init(); 6701 } 6702 6703 private void init() { 6704 setTransformationMethod(new SingleLineTransformationMethod()); 6705 } 6706 } 6707 6708 @Test 6709 public void testAutoSizeCallers_setText() throws Throwable { 6710 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6711 R.id.textview_autosize_uniform, false); 6712 6713 // Configure layout params and auto-size both in pixels to dodge flakiness on different 6714 // devices. 6715 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 6716 200, 200); 6717 mActivityRule.runOnUiThread(() -> { 6718 autoSizeTextView.setLayoutParams(layoutParams); 6719 autoSizeTextView.setAutoSizeTextTypeUniformWithConfiguration( 6720 1, 5000, 1, TypedValue.COMPLEX_UNIT_PX); 6721 }); 6722 mInstrumentation.waitForIdleSync(); 6723 6724 final String initialText = "13characters "; 6725 final StringBuilder textToSet = new StringBuilder().append(initialText); 6726 float initialSize = 0; 6727 6728 // As we add characters the text size shrinks. 6729 for (int i = 0; i < 10; i++) { 6730 mActivityRule.runOnUiThread(() -> 6731 autoSizeTextView.setText(textToSet.toString())); 6732 mInstrumentation.waitForIdleSync(); 6733 float expectedLargerSize = autoSizeTextView.getTextSize(); 6734 if (i == 0) { 6735 initialSize = expectedLargerSize; 6736 } 6737 6738 textToSet.append(initialText); 6739 mActivityRule.runOnUiThread(() -> 6740 autoSizeTextView.setText(textToSet.toString())); 6741 mInstrumentation.waitForIdleSync(); 6742 6743 assertTrue(expectedLargerSize >= autoSizeTextView.getTextSize()); 6744 } 6745 assertTrue(initialSize > autoSizeTextView.getTextSize()); 6746 6747 initialSize = Integer.MAX_VALUE; 6748 // As we remove characters the text size expands. 6749 for (int i = 9; i >= 0; i--) { 6750 mActivityRule.runOnUiThread(() -> 6751 autoSizeTextView.setText(textToSet.toString())); 6752 mInstrumentation.waitForIdleSync(); 6753 float expectedSmallerSize = autoSizeTextView.getTextSize(); 6754 if (i == 9) { 6755 initialSize = expectedSmallerSize; 6756 } 6757 6758 textToSet.replace((textToSet.length() - initialText.length()), textToSet.length(), ""); 6759 mActivityRule.runOnUiThread(() -> 6760 autoSizeTextView.setText(textToSet.toString())); 6761 mInstrumentation.waitForIdleSync(); 6762 6763 assertTrue(autoSizeTextView.getTextSize() >= expectedSmallerSize); 6764 } 6765 assertTrue(autoSizeTextView.getTextSize() > initialSize); 6766 } 6767 6768 @Test 6769 public void testAutoSize_setEllipsize() throws Throwable { 6770 final TextView textView = (TextView) mActivity.findViewById( 6771 R.id.textview_autosize_uniform_predef_sizes); 6772 final int initialAutoSizeType = textView.getAutoSizeTextType(); 6773 final int initialMinTextSize = textView.getAutoSizeMinTextSize(); 6774 final int initialMaxTextSize = textView.getAutoSizeMaxTextSize(); 6775 final int initialAutoSizeGranularity = textView.getAutoSizeStepGranularity(); 6776 final int initialSizes = textView.getAutoSizeTextAvailableSizes().length; 6777 6778 assertEquals(null, textView.getEllipsize()); 6779 // Verify styled attributes. 6780 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, initialAutoSizeType); 6781 assertNotEquals(-1, initialMinTextSize); 6782 assertNotEquals(-1, initialMaxTextSize); 6783 // Because this TextView has been configured to use predefined sizes. 6784 assertEquals(-1, initialAutoSizeGranularity); 6785 assertNotEquals(0, initialSizes); 6786 6787 final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END; 6788 mActivityRule.runOnUiThread(() -> 6789 textView.setEllipsize(newEllipsizeValue)); 6790 mInstrumentation.waitForIdleSync(); 6791 assertEquals(newEllipsizeValue, textView.getEllipsize()); 6792 // Beside the ellipsis no auto-size attribute has changed. 6793 assertEquals(initialAutoSizeType, textView.getAutoSizeTextType()); 6794 assertEquals(initialMinTextSize, textView.getAutoSizeMinTextSize()); 6795 assertEquals(initialMaxTextSize, textView.getAutoSizeMaxTextSize()); 6796 assertEquals(initialAutoSizeGranularity, textView.getAutoSizeStepGranularity()); 6797 assertEquals(initialSizes, textView.getAutoSizeTextAvailableSizes().length); 6798 } 6799 6800 @Test 6801 public void testEllipsize_setAutoSize() throws Throwable { 6802 TextView textView = findTextView(R.id.textview_text); 6803 final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END; 6804 mActivityRule.runOnUiThread(() -> 6805 textView.setEllipsize(newEllipsizeValue)); 6806 mInstrumentation.waitForIdleSync(); 6807 assertEquals(newEllipsizeValue, textView.getEllipsize()); 6808 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType()); 6809 assertEquals(-1, textView.getAutoSizeMinTextSize()); 6810 assertEquals(-1, textView.getAutoSizeMaxTextSize()); 6811 assertEquals(-1, textView.getAutoSizeStepGranularity()); 6812 assertEquals(0, textView.getAutoSizeTextAvailableSizes().length); 6813 6814 mActivityRule.runOnUiThread(() -> 6815 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM)); 6816 mInstrumentation.waitForIdleSync(); 6817 assertEquals(newEllipsizeValue, textView.getEllipsize()); 6818 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType()); 6819 // The auto-size defaults have been used. 6820 assertNotEquals(-1, textView.getAutoSizeMinTextSize()); 6821 assertNotEquals(-1, textView.getAutoSizeMaxTextSize()); 6822 assertNotEquals(-1, textView.getAutoSizeStepGranularity()); 6823 assertNotEquals(0, textView.getAutoSizeTextAvailableSizes().length); 6824 } 6825 6826 @Test 6827 public void testAutoSizeCallers_setTransformationMethod() throws Throwable { 6828 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6829 R.id.textview_autosize_uniform, false); 6830 // Mock transformation method to return the duplicated input text in order to measure 6831 // auto-sizing. 6832 TransformationMethod duplicateTextTransformationMethod = mock(TransformationMethod.class); 6833 when(duplicateTextTransformationMethod 6834 .getTransformation(any(CharSequence.class), any(View.class))) 6835 .thenAnswer(invocation -> { 6836 CharSequence source = (CharSequence) invocation.getArguments()[0]; 6837 return new StringBuilder().append(source).append(source).toString(); 6838 }); 6839 6840 mActivityRule.runOnUiThread(() -> 6841 autoSizeTextView.setTransformationMethod(null)); 6842 mInstrumentation.waitForIdleSync(); 6843 final float initialTextSize = autoSizeTextView.getTextSize(); 6844 mActivityRule.runOnUiThread(() -> 6845 autoSizeTextView.setTransformationMethod(duplicateTextTransformationMethod)); 6846 mInstrumentation.waitForIdleSync(); 6847 6848 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 6849 } 6850 6851 @Test 6852 public void testAutoSizeCallers_setCompoundDrawables() throws Throwable { 6853 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6854 R.id.textview_autosize_uniform, false); 6855 final float initialTextSize = autoSizeTextView.getTextSize(); 6856 Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red); 6857 drawable.setBounds(0, 0, autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3); 6858 mActivityRule.runOnUiThread(() -> 6859 autoSizeTextView.setCompoundDrawables(drawable, drawable, drawable, drawable)); 6860 mInstrumentation.waitForIdleSync(); 6861 6862 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 6863 } 6864 6865 @Test 6866 public void testAutoSizeCallers_setCompoundDrawablesRelative() throws Throwable { 6867 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6868 R.id.textview_autosize_uniform, false); 6869 final float initialTextSize = autoSizeTextView.getTextSize(); 6870 Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red); 6871 drawable.setBounds(0, 0, autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3); 6872 mActivityRule.runOnUiThread(() -> autoSizeTextView.setCompoundDrawablesRelative( 6873 drawable, drawable, drawable, drawable)); 6874 mInstrumentation.waitForIdleSync(); 6875 6876 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 6877 } 6878 6879 @Test 6880 public void testAutoSizeCallers_setCompoundDrawablePadding() throws Throwable { 6881 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6882 R.id.textview_autosize_uniform, false); 6883 // Prepare a larger layout in order not to hit the min value easily. 6884 mActivityRule.runOnUiThread(() -> { 6885 autoSizeTextView.setWidth(autoSizeTextView.getWidth() * 2); 6886 autoSizeTextView.setHeight(autoSizeTextView.getHeight() * 2); 6887 }); 6888 mInstrumentation.waitForIdleSync(); 6889 // Setup the drawables before setting their padding in order to modify the available 6890 // space and trigger a resize. 6891 Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red); 6892 drawable.setBounds(0, 0, autoSizeTextView.getWidth() / 4, autoSizeTextView.getHeight() / 4); 6893 mActivityRule.runOnUiThread(() -> autoSizeTextView.setCompoundDrawables( 6894 drawable, drawable, drawable, drawable)); 6895 mInstrumentation.waitForIdleSync(); 6896 final float initialTextSize = autoSizeTextView.getTextSize(); 6897 mActivityRule.runOnUiThread(() -> autoSizeTextView.setCompoundDrawablePadding( 6898 autoSizeTextView.getCompoundDrawablePadding() + 10)); 6899 mInstrumentation.waitForIdleSync(); 6900 6901 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 6902 } 6903 6904 @Test 6905 public void testAutoSizeCallers_setPadding() throws Throwable { 6906 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6907 R.id.textview_autosize_uniform, false); 6908 final float initialTextSize = autoSizeTextView.getTextSize(); 6909 mActivityRule.runOnUiThread(() -> autoSizeTextView.setPadding( 6910 autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3, 6911 autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3)); 6912 mInstrumentation.waitForIdleSync(); 6913 6914 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 6915 } 6916 6917 @Test 6918 public void testAutoSizeCallers_setPaddingRelative() throws Throwable { 6919 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6920 R.id.textview_autosize_uniform, false); 6921 final float initialTextSize = autoSizeTextView.getTextSize(); 6922 6923 mActivityRule.runOnUiThread(() -> autoSizeTextView.setPaddingRelative( 6924 autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3, 6925 autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3)); 6926 mInstrumentation.waitForIdleSync(); 6927 6928 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 6929 } 6930 6931 @Test 6932 public void testAutoSizeCallers_setTextScaleX() throws Throwable { 6933 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6934 R.id.textview_autosize_uniform, false); 6935 final float initialTextSize = autoSizeTextView.getTextSize(); 6936 6937 mActivityRule.runOnUiThread(() -> 6938 autoSizeTextView.setTextScaleX(autoSizeTextView.getTextScaleX() * 4.5f)); 6939 mInstrumentation.waitForIdleSync(); 6940 final float changedTextSize = autoSizeTextView.getTextSize(); 6941 6942 assertTrue(changedTextSize < initialTextSize); 6943 6944 mActivityRule.runOnUiThread(() -> 6945 autoSizeTextView.setTextScaleX(autoSizeTextView.getTextScaleX())); 6946 mInstrumentation.waitForIdleSync(); 6947 6948 assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f); 6949 } 6950 6951 @Test 6952 public void testAutoSizeCallers_setTypeface() throws Throwable { 6953 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6954 R.id.textview_autosize_uniform, false); 6955 mActivityRule.runOnUiThread(() -> 6956 autoSizeTextView.setText("The typeface change needs a bit more text then " 6957 + "the default used for this batch of tests in order to get to resize text." 6958 + " The resize function is always called but even with different typefaces " 6959 + "there may not be a need to resize text because it just fits. The longer " 6960 + "the text, the higher the chance for a resize. And here is yet another " 6961 + "sentence to make sure this test is not flaky. Not flaky at all.")); 6962 mInstrumentation.waitForIdleSync(); 6963 final float initialTextSize = autoSizeTextView.getTextSize(); 6964 6965 mActivityRule.runOnUiThread(() -> { 6966 Typeface differentTypeface = Typeface.MONOSPACE; 6967 if (autoSizeTextView.getTypeface() == Typeface.MONOSPACE) { 6968 differentTypeface = Typeface.SANS_SERIF; 6969 } 6970 autoSizeTextView.setTypeface(differentTypeface); 6971 }); 6972 mInstrumentation.waitForIdleSync(); 6973 final float changedTextSize = autoSizeTextView.getTextSize(); 6974 6975 // Don't really know if it is larger or smaller (depends on the typeface chosen above), 6976 // but it should definitely have changed. 6977 assertNotEquals(initialTextSize, changedTextSize, 0f); 6978 6979 mActivityRule.runOnUiThread(() -> 6980 autoSizeTextView.setTypeface(autoSizeTextView.getTypeface())); 6981 mInstrumentation.waitForIdleSync(); 6982 6983 assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f); 6984 } 6985 6986 @Test 6987 public void testAutoSizeCallers_setLetterSpacing() throws Throwable { 6988 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 6989 R.id.textview_autosize_uniform, false); 6990 final float initialTextSize = autoSizeTextView.getTextSize(); 6991 6992 mActivityRule.runOnUiThread(() -> 6993 // getLetterSpacing() could return 0, make sure there is enough of a difference to 6994 // trigger auto-size. 6995 autoSizeTextView.setLetterSpacing( 6996 autoSizeTextView.getLetterSpacing() * 1.5f + 4.5f)); 6997 mInstrumentation.waitForIdleSync(); 6998 final float changedTextSize = autoSizeTextView.getTextSize(); 6999 7000 assertTrue(changedTextSize < initialTextSize); 7001 7002 mActivityRule.runOnUiThread(() -> 7003 autoSizeTextView.setLetterSpacing(autoSizeTextView.getLetterSpacing())); 7004 mInstrumentation.waitForIdleSync(); 7005 7006 assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f); 7007 } 7008 7009 @Test 7010 public void testAutoSizeCallers_setHorizontallyScrolling() throws Throwable { 7011 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7012 R.id.textview_autosize_uniform, false); 7013 // Verify that we do not have horizontal scrolling turned on. 7014 assertTrue(!autoSizeTextView.getHorizontallyScrolling()); 7015 7016 final float initialTextSize = autoSizeTextView.getTextSize(); 7017 mActivityRule.runOnUiThread(() -> autoSizeTextView.setHorizontallyScrolling(true)); 7018 mInstrumentation.waitForIdleSync(); 7019 assertTrue(autoSizeTextView.getTextSize() > initialTextSize); 7020 7021 mActivityRule.runOnUiThread(() -> autoSizeTextView.setHorizontallyScrolling(false)); 7022 mInstrumentation.waitForIdleSync(); 7023 assertEquals(initialTextSize, autoSizeTextView.getTextSize(), 0f); 7024 } 7025 7026 @Test 7027 public void testAutoSizeCallers_setMaxLines() throws Throwable { 7028 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7029 R.id.textview_autosize_uniform, false); 7030 // Configure layout params and auto-size both in pixels to dodge flakiness on different 7031 // devices. 7032 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 7033 200, 200); 7034 final String text = "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten"; 7035 mActivityRule.runOnUiThread(() -> { 7036 autoSizeTextView.setLayoutParams(layoutParams); 7037 autoSizeTextView.setAutoSizeTextTypeUniformWithConfiguration( 7038 1 /* autoSizeMinTextSize */, 7039 5000 /* autoSizeMaxTextSize */, 7040 1 /* autoSizeStepGranularity */, 7041 TypedValue.COMPLEX_UNIT_PX); 7042 autoSizeTextView.setText(text); 7043 }); 7044 mInstrumentation.waitForIdleSync(); 7045 7046 float initialSize = 0; 7047 for (int i = 1; i < 10; i++) { 7048 final int maxLines = i; 7049 mActivityRule.runOnUiThread(() -> autoSizeTextView.setMaxLines(maxLines)); 7050 mInstrumentation.waitForIdleSync(); 7051 float expectedSmallerSize = autoSizeTextView.getTextSize(); 7052 if (i == 1) { 7053 initialSize = expectedSmallerSize; 7054 } 7055 7056 mActivityRule.runOnUiThread(() -> autoSizeTextView.setMaxLines(maxLines + 1)); 7057 mInstrumentation.waitForIdleSync(); 7058 assertTrue(expectedSmallerSize <= autoSizeTextView.getTextSize()); 7059 } 7060 assertTrue(initialSize < autoSizeTextView.getTextSize()); 7061 7062 initialSize = Integer.MAX_VALUE; 7063 for (int i = 10; i > 1; i--) { 7064 final int maxLines = i; 7065 mActivityRule.runOnUiThread(() -> autoSizeTextView.setMaxLines(maxLines)); 7066 mInstrumentation.waitForIdleSync(); 7067 float expectedLargerSize = autoSizeTextView.getTextSize(); 7068 if (i == 10) { 7069 initialSize = expectedLargerSize; 7070 } 7071 7072 mActivityRule.runOnUiThread(() -> autoSizeTextView.setMaxLines(maxLines - 1)); 7073 mInstrumentation.waitForIdleSync(); 7074 assertTrue(expectedLargerSize >= autoSizeTextView.getTextSize()); 7075 } 7076 assertTrue(initialSize > autoSizeTextView.getTextSize()); 7077 } 7078 7079 @Test 7080 public void testAutoSizeCallers_setMaxHeight() throws Throwable { 7081 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7082 R.id.textview_autosize_uniform, true); 7083 // Do not force exact height only. 7084 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 7085 200, 7086 LinearLayout.LayoutParams.WRAP_CONTENT); 7087 mActivityRule.runOnUiThread(() -> autoSizeTextView.setLayoutParams(layoutParams)); 7088 mInstrumentation.waitForIdleSync(); 7089 final float initialTextSize = autoSizeTextView.getTextSize(); 7090 mActivityRule.runOnUiThread(() -> autoSizeTextView.setMaxHeight( 7091 autoSizeTextView.getHeight() / 4)); 7092 mInstrumentation.waitForIdleSync(); 7093 7094 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 7095 } 7096 7097 @Test 7098 public void testAutoSizeCallers_setHeight() throws Throwable { 7099 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7100 R.id.textview_autosize_uniform, true); 7101 // Do not force exact height only. 7102 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 7103 200, 7104 LinearLayout.LayoutParams.WRAP_CONTENT); 7105 mActivityRule.runOnUiThread(() -> autoSizeTextView.setLayoutParams(layoutParams)); 7106 mInstrumentation.waitForIdleSync(); 7107 final float initialTextSize = autoSizeTextView.getTextSize(); 7108 mActivityRule.runOnUiThread(() -> autoSizeTextView.setHeight( 7109 autoSizeTextView.getHeight() / 4)); 7110 mInstrumentation.waitForIdleSync(); 7111 7112 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 7113 } 7114 7115 @Test 7116 public void testAutoSizeCallers_setLines() throws Throwable { 7117 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7118 R.id.textview_autosize_uniform, false); 7119 final float initialTextSize = autoSizeTextView.getTextSize(); 7120 mActivityRule.runOnUiThread(() -> autoSizeTextView.setLines(1)); 7121 mInstrumentation.waitForIdleSync(); 7122 7123 assertTrue(autoSizeTextView.getTextSize() < initialTextSize); 7124 } 7125 7126 @Test 7127 public void testAutoSizeCallers_setMaxWidth() throws Throwable { 7128 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7129 R.id.textview_autosize_uniform, true); 7130 // Do not force exact width only. 7131 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 7132 LinearLayout.LayoutParams.WRAP_CONTENT, 7133 200); 7134 mActivityRule.runOnUiThread(() -> autoSizeTextView.setLayoutParams(layoutParams)); 7135 mInstrumentation.waitForIdleSync(); 7136 final float initialTextSize = autoSizeTextView.getTextSize(); 7137 mActivityRule.runOnUiThread(() -> autoSizeTextView.setMaxWidth( 7138 autoSizeTextView.getWidth() / 4)); 7139 mInstrumentation.waitForIdleSync(); 7140 7141 assertTrue(autoSizeTextView.getTextSize() != initialTextSize); 7142 } 7143 7144 @Test 7145 public void testAutoSizeCallers_setWidth() throws Throwable { 7146 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7147 R.id.textview_autosize_uniform, true); 7148 // Do not force exact width only. 7149 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 7150 LinearLayout.LayoutParams.WRAP_CONTENT, 7151 200); 7152 mActivityRule.runOnUiThread(() -> autoSizeTextView.setLayoutParams(layoutParams)); 7153 mInstrumentation.waitForIdleSync(); 7154 7155 final float initialTextSize = autoSizeTextView.getTextSize(); 7156 mActivityRule.runOnUiThread(() -> autoSizeTextView.setWidth( 7157 autoSizeTextView.getWidth() / 4)); 7158 mInstrumentation.waitForIdleSync(); 7159 7160 assertTrue(autoSizeTextView.getTextSize() != initialTextSize); 7161 } 7162 7163 @Test 7164 public void testAutoSizeCallers_setLineSpacing() throws Throwable { 7165 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7166 R.id.textview_autosize_uniform, false); 7167 final float initialTextSize = autoSizeTextView.getTextSize(); 7168 7169 mActivityRule.runOnUiThread(() -> autoSizeTextView.setLineSpacing( 7170 autoSizeTextView.getLineSpacingExtra() * 4, 7171 autoSizeTextView.getLineSpacingMultiplier() * 4)); 7172 mInstrumentation.waitForIdleSync(); 7173 final float changedTextSize = autoSizeTextView.getTextSize(); 7174 7175 assertTrue(changedTextSize < initialTextSize); 7176 7177 mActivityRule.runOnUiThread(() -> autoSizeTextView.setLineSpacing( 7178 autoSizeTextView.getLineSpacingExtra(), 7179 autoSizeTextView.getLineSpacingMultiplier())); 7180 mInstrumentation.waitForIdleSync(); 7181 7182 assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f); 7183 } 7184 7185 @Test 7186 public void testAutoSizeCallers_setTextSizeIsNoOp() throws Throwable { 7187 final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData( 7188 R.id.textview_autosize_uniform, false); 7189 final float initialTextSize = autoSizeTextView.getTextSize(); 7190 7191 mActivityRule.runOnUiThread(() -> autoSizeTextView.setTextSize( 7192 initialTextSize + 123f)); 7193 mInstrumentation.waitForIdleSync(); 7194 7195 assertEquals(initialTextSize, autoSizeTextView.getTextSize(), 0f); 7196 } 7197 7198 @Test 7199 public void testAutoSizeCallers_setHeightForOneLineText() throws Throwable { 7200 final TextView autoSizeTextView = (TextView) mActivity.findViewById( 7201 R.id.textview_autosize_basic); 7202 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, autoSizeTextView.getAutoSizeTextType()); 7203 final float initialTextSize = autoSizeTextView.getTextSize(); 7204 mActivityRule.runOnUiThread(() -> autoSizeTextView.setHeight( 7205 autoSizeTextView.getHeight() * 3)); 7206 mInstrumentation.waitForIdleSync(); 7207 7208 assertTrue(autoSizeTextView.getTextSize() > initialTextSize); 7209 } 7210 7211 @Test 7212 public void testAutoSizeUniform_obtainStyledAttributes() { 7213 DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics(); 7214 TextView autoSizeTextViewUniform = (TextView) mActivity.findViewById( 7215 R.id.textview_autosize_uniform); 7216 7217 // The size has been set to 50dp in the layout but this being an AUTO_SIZE_TEXT_TYPE_UNIFORM 7218 // TextView, the size is considered max size thus the value returned by getSize() in this 7219 // case should be lower than the one set (given that there is not much available space and 7220 // the font size is very high). In theory the values could be equal for a different TextView 7221 // configuration. 7222 final float sizeSetInPixels = TypedValue.applyDimension( 7223 TypedValue.COMPLEX_UNIT_DIP, 50f, metrics); 7224 assertTrue(autoSizeTextViewUniform.getTextSize() < sizeSetInPixels); 7225 } 7226 7227 @Test 7228 public void testAutoSizeUniform_obtainStyledAttributesUsingPredefinedSizes() { 7229 DisplayMetrics m = mActivity.getResources().getDisplayMetrics(); 7230 final TextView autoSizeTextViewUniform = (TextView) mActivity.findViewById( 7231 R.id.textview_autosize_uniform_predef_sizes); 7232 7233 // In arrays.xml predefined the step sizes as: 10px, 10dp, 10sp, 10pt, 10in and 10mm. 7234 // TypedValue can not use the math library and instead naively ceils the value by adding 7235 // 0.5f when obtaining styled attributes. Check TypedValue#complexToDimensionPixelSize(...) 7236 int[] expectedSizesInPx = new int[] { 7237 (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 10f, m)), 7238 (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, m)), 7239 (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f, m)), 7240 (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, 10f, m)), 7241 (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_IN, 10f, m)), 7242 (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 10f, m))}; 7243 7244 boolean containsValueFromExpectedSizes = false; 7245 int textSize = (int) autoSizeTextViewUniform.getTextSize(); 7246 for (int i = 0; i < expectedSizesInPx.length; i++) { 7247 if (expectedSizesInPx[i] == textSize) { 7248 containsValueFromExpectedSizes = true; 7249 break; 7250 } 7251 } 7252 assertTrue(containsValueFromExpectedSizes); 7253 } 7254 7255 @Test 7256 public void testAutoSizeUniform_obtainStyledAttributesPredefinedSizesFiltering() { 7257 TextView autoSizeTextViewUniform = (TextView) mActivity.findViewById( 7258 R.id.textview_autosize_uniform_predef_sizes_redundant_values); 7259 7260 // In arrays.xml predefined the step sizes as: 40px, 10px, 10px, 10px, 0dp. 7261 final int[] expectedSizes = new int[] {10, 40}; 7262 assertArrayEquals(expectedSizes, autoSizeTextViewUniform.getAutoSizeTextAvailableSizes()); 7263 } 7264 7265 @Test 7266 public void testAutoSizeUniform_predefinedSizesFilteringAndSorting() throws Throwable { 7267 mTextView = findTextView(R.id.textview_text); 7268 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, mTextView.getAutoSizeTextType()); 7269 7270 final int[] predefinedSizes = new int[] {400, 0, 10, 40, 10, 10, 0, 0}; 7271 mActivityRule.runOnUiThread(() -> mTextView.setAutoSizeTextTypeUniformWithPresetSizes( 7272 predefinedSizes, TypedValue.COMPLEX_UNIT_PX)); 7273 mInstrumentation.waitForIdleSync(); 7274 assertArrayEquals(new int[] {10, 40, 400}, mTextView.getAutoSizeTextAvailableSizes()); 7275 } 7276 7277 @Test(expected = NullPointerException.class) 7278 public void testAutoSizeUniform_predefinedSizesNullArray() throws Throwable { 7279 mTextView = findTextView(R.id.textview_text); 7280 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, mTextView.getAutoSizeTextType()); 7281 7282 final int[] predefinedSizes = null; 7283 mActivityRule.runOnUiThread(() -> mTextView.setAutoSizeTextTypeUniformWithPresetSizes( 7284 predefinedSizes, TypedValue.COMPLEX_UNIT_PX)); 7285 mInstrumentation.waitForIdleSync(); 7286 } 7287 7288 @Test 7289 public void testAutoSizeUniform_predefinedSizesEmptyArray() throws Throwable { 7290 mTextView = findTextView(R.id.textview_text); 7291 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, mTextView.getAutoSizeTextType()); 7292 7293 mActivityRule.runOnUiThread(() -> 7294 mTextView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM)); 7295 mInstrumentation.waitForIdleSync(); 7296 7297 final int[] defaultSizes = mTextView.getAutoSizeTextAvailableSizes(); 7298 assertNotNull(defaultSizes); 7299 assertTrue(defaultSizes.length > 0); 7300 7301 final int[] predefinedSizes = new int[0]; 7302 mActivityRule.runOnUiThread(() -> mTextView.setAutoSizeTextTypeUniformWithPresetSizes( 7303 predefinedSizes, TypedValue.COMPLEX_UNIT_PX)); 7304 mInstrumentation.waitForIdleSync(); 7305 7306 final int[] newSizes = mTextView.getAutoSizeTextAvailableSizes(); 7307 assertNotNull(defaultSizes); 7308 assertArrayEquals(defaultSizes, newSizes); 7309 } 7310 7311 @Test 7312 public void testAutoSizeUniform_buildsSizes() throws Throwable { 7313 TextView autoSizeTextViewUniform = (TextView) mActivity.findViewById( 7314 R.id.textview_autosize_uniform); 7315 7316 // Verify that the interval limits are both included. 7317 mActivityRule.runOnUiThread(() -> autoSizeTextViewUniform 7318 .setAutoSizeTextTypeUniformWithConfiguration(10, 20, 2, 7319 TypedValue.COMPLEX_UNIT_PX)); 7320 mInstrumentation.waitForIdleSync(); 7321 assertArrayEquals( 7322 new int[] {10, 12, 14, 16, 18, 20}, 7323 autoSizeTextViewUniform.getAutoSizeTextAvailableSizes()); 7324 7325 mActivityRule.runOnUiThread(() -> autoSizeTextViewUniform 7326 .setAutoSizeTextTypeUniformWithConfiguration( 7327 autoSizeTextViewUniform.getAutoSizeMinTextSize(), 7328 19, 7329 autoSizeTextViewUniform.getAutoSizeStepGranularity(), 7330 TypedValue.COMPLEX_UNIT_PX)); 7331 mInstrumentation.waitForIdleSync(); 7332 assertArrayEquals( 7333 new int[] {10, 12, 14, 16, 18}, 7334 autoSizeTextViewUniform.getAutoSizeTextAvailableSizes()); 7335 7336 mActivityRule.runOnUiThread(() -> autoSizeTextViewUniform 7337 .setAutoSizeTextTypeUniformWithConfiguration( 7338 autoSizeTextViewUniform.getAutoSizeMinTextSize(), 7339 21, 7340 autoSizeTextViewUniform.getAutoSizeStepGranularity(), 7341 TypedValue.COMPLEX_UNIT_PX)); 7342 mInstrumentation.waitForIdleSync(); 7343 assertArrayEquals( 7344 new int[] {10, 12, 14, 16, 18, 20}, 7345 autoSizeTextViewUniform.getAutoSizeTextAvailableSizes()); 7346 } 7347 7348 @Test 7349 public void testAutoSizeUniform_getSetAutoSizeTextDefaults() { 7350 final TextView textView = new TextView(mActivity); 7351 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType()); 7352 // Min/Max/Granularity values for auto-sizing are 0 because they are not used. 7353 assertEquals(-1, textView.getAutoSizeMinTextSize()); 7354 assertEquals(-1, textView.getAutoSizeMaxTextSize()); 7355 assertEquals(-1, textView.getAutoSizeStepGranularity()); 7356 7357 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM); 7358 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType()); 7359 // Min/Max default values for auto-sizing XY have been loaded. 7360 final int minSize = textView.getAutoSizeMinTextSize(); 7361 final int maxSize = textView.getAutoSizeMaxTextSize(); 7362 assertTrue(0 < minSize); 7363 assertTrue(minSize < maxSize); 7364 assertNotEquals(0, textView.getAutoSizeStepGranularity()); 7365 7366 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_NONE); 7367 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType()); 7368 // Min/Max values for auto-sizing XY have been cleared. 7369 assertEquals(-1, textView.getAutoSizeMinTextSize()); 7370 assertEquals(-1, textView.getAutoSizeMaxTextSize()); 7371 assertEquals(-1, textView.getAutoSizeStepGranularity()); 7372 } 7373 7374 @Test 7375 public void testAutoSizeUniform_getSetAutoSizeStepGranularity() { 7376 final TextView textView = new TextView(mActivity); 7377 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType()); 7378 final int initialValue = -1; 7379 assertEquals(initialValue, textView.getAutoSizeStepGranularity()); 7380 7381 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM); 7382 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType()); 7383 final int defaultValue = 1; // 1px. 7384 // If the auto-size type is AUTO_SIZE_TEXT_TYPE_UNIFORM then it means textView went through 7385 // the auto-size setup and given that 0 is an invalid value it changed it to the default. 7386 assertEquals(defaultValue, textView.getAutoSizeStepGranularity()); 7387 7388 final int newValue = 33; 7389 textView.setAutoSizeTextTypeUniformWithConfiguration( 7390 textView.getAutoSizeMinTextSize(), 7391 textView.getAutoSizeMaxTextSize(), 7392 newValue, 7393 TypedValue.COMPLEX_UNIT_PX); 7394 assertEquals(newValue, textView.getAutoSizeStepGranularity()); 7395 } 7396 7397 @Test 7398 public void testAutoSizeUniform_getSetAutoSizeMinTextSize() { 7399 final TextView textView = new TextView(mActivity); 7400 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM); 7401 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType()); 7402 final int minSize = textView.getAutoSizeMinTextSize(); 7403 assertNotEquals(0, minSize); 7404 final int maxSize = textView.getAutoSizeMaxTextSize(); 7405 assertNotEquals(0, maxSize); 7406 7407 // This is just a test check to verify the next assertions. If this fails it is a problem 7408 // of this test setup (we need at least 2 units). 7409 assertTrue((maxSize - minSize) > 1); 7410 final int newMinSize = maxSize - 1; 7411 textView.setAutoSizeTextTypeUniformWithConfiguration( 7412 newMinSize, 7413 textView.getAutoSizeMaxTextSize(), 7414 textView.getAutoSizeStepGranularity(), 7415 TypedValue.COMPLEX_UNIT_PX); 7416 7417 assertEquals(newMinSize, textView.getAutoSizeMinTextSize()); 7418 // Max size has not changed. 7419 assertEquals(maxSize, textView.getAutoSizeMaxTextSize()); 7420 7421 textView.setAutoSizeTextTypeUniformWithConfiguration( 7422 newMinSize, 7423 newMinSize + 10, 7424 textView.getAutoSizeStepGranularity(), 7425 TypedValue.COMPLEX_UNIT_SP); 7426 7427 // It does not matter which unit has been used to set the min size, the getter always 7428 // returns it in pixels. 7429 assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, newMinSize, 7430 mActivity.getResources().getDisplayMetrics())), textView.getAutoSizeMinTextSize()); 7431 } 7432 7433 @Test(expected = IllegalArgumentException.class) 7434 public void testAutoSizeUniform_throwsException_whenMaxLessThanMin() { 7435 final TextView textView = new TextView(mActivity); 7436 textView.setAutoSizeTextTypeUniformWithConfiguration( 7437 10, 9, 1, TypedValue.COMPLEX_UNIT_SP); 7438 } 7439 7440 @Test(expected = IllegalArgumentException.class) 7441 public void testAutoSizeUniform_throwsException_minLessThanZero() { 7442 final TextView textView = new TextView(mActivity); 7443 textView.setAutoSizeTextTypeUniformWithConfiguration( 7444 -1, 9, 1, TypedValue.COMPLEX_UNIT_SP); 7445 } 7446 7447 @Test(expected = IllegalArgumentException.class) 7448 public void testAutoSizeUniform_throwsException_maxLessThanZero() { 7449 final TextView textView = new TextView(mActivity); 7450 textView.setAutoSizeTextTypeUniformWithConfiguration( 7451 10, -1, 1, TypedValue.COMPLEX_UNIT_SP); 7452 } 7453 7454 @Test(expected = IllegalArgumentException.class) 7455 public void testAutoSizeUniform_throwsException_granularityLessThanZero() { 7456 final TextView textView = new TextView(mActivity); 7457 textView.setAutoSizeTextTypeUniformWithConfiguration( 7458 10, 20, -1, TypedValue.COMPLEX_UNIT_SP); 7459 } 7460 7461 @Test 7462 public void testAutoSizeUniform_equivalentConfigurations() throws Throwable { 7463 final DisplayMetrics dm = mActivity.getResources().getDisplayMetrics(); 7464 final int minTextSize = 10; 7465 final int maxTextSize = 20; 7466 final int granularity = 2; 7467 final int unit = TypedValue.COMPLEX_UNIT_SP; 7468 7469 final TextView granularityTextView = new TextView(mActivity); 7470 granularityTextView.setAutoSizeTextTypeUniformWithConfiguration( 7471 minTextSize, maxTextSize, granularity, unit); 7472 7473 final TextView presetTextView = new TextView(mActivity); 7474 presetTextView.setAutoSizeTextTypeUniformWithPresetSizes( 7475 new int[] {minTextSize, 12, 14, 16, 18, maxTextSize}, unit); 7476 7477 // The TextViews have been configured differently but the end result should be nearly 7478 // identical. 7479 final int expectedAutoSizeType = TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM; 7480 assertEquals(expectedAutoSizeType, granularityTextView.getAutoSizeTextType()); 7481 assertEquals(expectedAutoSizeType, presetTextView.getAutoSizeTextType()); 7482 7483 final int expectedMinTextSizeInPx = Math.round( 7484 TypedValue.applyDimension(unit, minTextSize, dm)); 7485 assertEquals(expectedMinTextSizeInPx, granularityTextView.getAutoSizeMinTextSize()); 7486 assertEquals(expectedMinTextSizeInPx, presetTextView.getAutoSizeMinTextSize()); 7487 7488 final int expectedMaxTextSizeInPx = Math.round( 7489 TypedValue.applyDimension(unit, maxTextSize, dm)); 7490 assertEquals(expectedMaxTextSizeInPx, granularityTextView.getAutoSizeMaxTextSize()); 7491 assertEquals(expectedMaxTextSizeInPx, presetTextView.getAutoSizeMaxTextSize()); 7492 7493 // Configured with granularity. 7494 assertEquals(Math.round(TypedValue.applyDimension(unit, granularity, dm)), 7495 granularityTextView.getAutoSizeStepGranularity()); 7496 // Configured with preset values, there is no granularity. 7497 assertEquals(-1, presetTextView.getAutoSizeStepGranularity()); 7498 7499 // Both TextViews generate exactly the same sizes in pixels to choose from when auto-sizing. 7500 assertArrayEquals("Expected the granularity and preset configured auto-sized " 7501 + "TextViews to have identical available sizes for auto-sizing." 7502 + "\ngranularity sizes: " 7503 + Arrays.toString(granularityTextView.getAutoSizeTextAvailableSizes()) 7504 + "\npreset sizes: " 7505 + Arrays.toString(presetTextView.getAutoSizeTextAvailableSizes()), 7506 granularityTextView.getAutoSizeTextAvailableSizes(), 7507 presetTextView.getAutoSizeTextAvailableSizes()); 7508 7509 final String someText = "This is a string"; 7510 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 7511 200, 200); 7512 // Configure identically and attach to layout. 7513 mActivityRule.runOnUiThread(() -> { 7514 granularityTextView.setLayoutParams(layoutParams); 7515 presetTextView.setLayoutParams(layoutParams); 7516 7517 LinearLayout ll = mActivity.findViewById(R.id.layout_textviewtest); 7518 ll.removeAllViews(); 7519 ll.addView(granularityTextView); 7520 ll.addView(presetTextView); 7521 7522 granularityTextView.setText(someText); 7523 presetTextView.setText(someText); 7524 }); 7525 mInstrumentation.waitForIdleSync(); 7526 7527 assertEquals(granularityTextView.getTextSize(), presetTextView.getTextSize(), 0f); 7528 } 7529 7530 @Test 7531 public void testAutoSizeUniform_getSetAutoSizeMaxTextSize() { 7532 final TextView textView = new TextView(mActivity); 7533 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM); 7534 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType()); 7535 final int minSize = textView.getAutoSizeMinTextSize(); 7536 assertNotEquals(0, minSize); 7537 final int maxSize = textView.getAutoSizeMaxTextSize(); 7538 assertNotEquals(0, maxSize); 7539 7540 final int newMaxSize = maxSize + 11; 7541 textView.setAutoSizeTextTypeUniformWithConfiguration( 7542 textView.getAutoSizeMinTextSize(), 7543 newMaxSize, 7544 textView.getAutoSizeStepGranularity(), 7545 TypedValue.COMPLEX_UNIT_PX); 7546 7547 assertEquals(newMaxSize, textView.getAutoSizeMaxTextSize()); 7548 // Min size has not changed. 7549 assertEquals(minSize, textView.getAutoSizeMinTextSize()); 7550 textView.setAutoSizeTextTypeUniformWithConfiguration( 7551 textView.getAutoSizeMinTextSize(), 7552 newMaxSize, 7553 textView.getAutoSizeStepGranularity(), 7554 TypedValue.COMPLEX_UNIT_SP); 7555 // It does not matter which unit has been used to set the max size, the getter always 7556 // returns it in pixels. 7557 assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, newMaxSize, 7558 mActivity.getResources().getDisplayMetrics())), textView.getAutoSizeMaxTextSize()); 7559 } 7560 7561 @Test 7562 public void testAutoSizeUniform_autoSizeCalledWhenTypeChanged() throws Throwable { 7563 mTextView = findTextView(R.id.textview_text); 7564 // Make sure we pick an already inflated non auto-sized text view. 7565 assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, mTextView.getAutoSizeTextType()); 7566 // Set the text size to a very low value in order to prepare for auto-size. 7567 final int customTextSize = 3; 7568 mActivityRule.runOnUiThread(() -> 7569 mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, customTextSize)); 7570 mInstrumentation.waitForIdleSync(); 7571 assertEquals(customTextSize, mTextView.getTextSize(), 0f); 7572 mActivityRule.runOnUiThread(() -> 7573 mTextView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM)); 7574 mInstrumentation.waitForIdleSync(); 7575 // The size of the text should have changed. 7576 assertNotEquals(customTextSize, mTextView.getTextSize(), 0f); 7577 } 7578 7579 @Test 7580 public void testSmartSelection() throws Throwable { 7581 mTextView = findTextView(R.id.textview_text); 7582 String text = "The president-elect, Filip, is coming to town tomorrow."; 7583 int startIndex = text.indexOf("president"); 7584 int endIndex = startIndex + "president".length(); 7585 initializeTextForSmartSelection(text); 7586 7587 // Long-press for smart selection. Expect smart selection. 7588 Point offset = getCenterPositionOfTextAt(mTextView, startIndex, endIndex); 7589 emulateLongPressOnView(mTextView, offset.x, offset.y); 7590 PollingCheck.waitFor(() -> mTextView.getSelectionStart() == SMARTSELECT_START 7591 && mTextView.getSelectionEnd() == SMARTSELECT_END); 7592 // TODO: Test the floating toolbar content. 7593 } 7594 7595 private boolean isWatch() { 7596 return (mActivity.getResources().getConfiguration().uiMode 7597 & Configuration.UI_MODE_TYPE_WATCH) == Configuration.UI_MODE_TYPE_WATCH; 7598 } 7599 7600 @Test 7601 public void testSmartSelection_dragSelection() throws Throwable { 7602 if (isWatch()) { 7603 return; 7604 } 7605 mTextView = findTextView(R.id.textview_text); 7606 String text = "The president-elect, Filip, is coming to town tomorrow."; 7607 int startIndex = text.indexOf("is coming to town"); 7608 int endIndex = startIndex + "is coming to town".length(); 7609 initializeTextForSmartSelection(text); 7610 7611 Point start = getCenterPositionOfTextAt(mTextView, startIndex, startIndex); 7612 Point end = getCenterPositionOfTextAt(mTextView, endIndex, endIndex); 7613 int[] viewOnScreenXY = new int[2]; 7614 mTextView.getLocationOnScreen(viewOnScreenXY); 7615 int startX = start.x + viewOnScreenXY[0]; 7616 int startY = start.y + viewOnScreenXY[1]; 7617 int offsetX = end.x - start.x; 7618 7619 // Perform drag selection. 7620 CtsTouchUtils.emulateLongPressAndDragGesture( 7621 mInstrumentation, startX, startY, offsetX, 0 /* offsetY */); 7622 7623 // No smart selection on drag selection. 7624 assertEquals(startIndex, mTextView.getSelectionStart()); 7625 assertEquals(endIndex, mTextView.getSelectionEnd()); 7626 } 7627 7628 @Test 7629 public void testSmartSelection_resetSelection() throws Throwable { 7630 mTextView = findTextView(R.id.textview_text); 7631 String text = "The president-elect, Filip, is coming to town tomorrow."; 7632 int startIndex = text.indexOf("president"); 7633 int endIndex = startIndex + "president".length(); 7634 initializeTextForSmartSelection(text); 7635 7636 // Long-press for smart selection. Expect smart selection. 7637 Point offset = getCenterPositionOfTextAt(mTextView, startIndex, endIndex); 7638 emulateLongPressOnView(mTextView, offset.x, offset.y); 7639 PollingCheck.waitFor(() -> mTextView.getSelectionStart() == SMARTSELECT_START 7640 && mTextView.getSelectionEnd() == SMARTSELECT_END); 7641 7642 // Tap to reset selection. Expect tapped word to be selected. 7643 startIndex = text.indexOf("Filip"); 7644 endIndex = startIndex + "Filip".length(); 7645 offset = getCenterPositionOfTextAt(mTextView, startIndex, endIndex); 7646 emulateClickOnView(mTextView, offset.x, offset.y); 7647 final int selStart = startIndex; 7648 final int selEnd = endIndex; 7649 PollingCheck.waitFor(() -> mTextView.getSelectionStart() == selStart 7650 && mTextView.getSelectionEnd() == selEnd); 7651 7652 // Tap one more time to dismiss the selection. 7653 emulateClickOnView(mTextView, offset.x, offset.y); 7654 assertFalse(mTextView.hasSelection()); 7655 } 7656 7657 @Test 7658 public void testFontResources_setInXmlFamilyName() { 7659 mTextView = findTextView(R.id.textview_fontresource_fontfamily); 7660 Typeface expected = mActivity.getResources().getFont(R.font.samplefont); 7661 7662 assertEquals(expected, mTextView.getTypeface()); 7663 } 7664 7665 @Test 7666 public void testFontResourcesXml_setInXmlFamilyName() { 7667 mTextView = findTextView(R.id.textview_fontxmlresource_fontfamily); 7668 Typeface expected = mActivity.getResources().getFont(R.font.samplexmlfont); 7669 7670 assertEquals(expected, mTextView.getTypeface()); 7671 } 7672 7673 @Test 7674 public void testFontResources_setInXmlStyle() { 7675 mTextView = findTextView(R.id.textview_fontresource_style); 7676 Typeface expected = mActivity.getResources().getFont(R.font.samplefont); 7677 7678 assertEquals(expected, mTextView.getTypeface()); 7679 } 7680 7681 @Test 7682 public void testFontResourcesXml_setInXmlStyle() { 7683 mTextView = findTextView(R.id.textview_fontxmlresource_style); 7684 Typeface expected = mActivity.getResources().getFont(R.font.samplexmlfont); 7685 7686 assertEquals(expected, mTextView.getTypeface()); 7687 } 7688 7689 @Test 7690 public void testFontResources_setInXmlTextAppearance() { 7691 mTextView = findTextView(R.id.textview_fontresource_textAppearance); 7692 Typeface expected = mActivity.getResources().getFont(R.font.samplefont); 7693 7694 assertEquals(expected, mTextView.getTypeface()); 7695 } 7696 7697 @Test 7698 public void testFontResourcesXml_setInXmlWithStyle() { 7699 mTextView = findTextView(R.id.textview_fontxmlresource_fontfamily); 7700 Typeface expected = mActivity.getResources().getFont(R.font.samplexmlfont); 7701 7702 assertEquals(expected, mTextView.getTypeface()); 7703 7704 mTextView = findTextView(R.id.textview_fontxmlresource_withStyle); 7705 7706 Typeface resultTypeface = mTextView.getTypeface(); 7707 assertNotEquals(resultTypeface, expected); 7708 assertEquals(Typeface.create(expected, Typeface.ITALIC), resultTypeface); 7709 assertEquals(Typeface.ITALIC, resultTypeface.getStyle()); 7710 } 7711 7712 @Test 7713 public void testFontResourcesXml_setInXmlTextAppearance() { 7714 mTextView = findTextView(R.id.textview_fontxmlresource_textAppearance); 7715 Typeface expected = mActivity.getResources().getFont(R.font.samplexmlfont); 7716 7717 assertEquals(expected, mTextView.getTypeface()); 7718 } 7719 7720 @Test 7721 @MediumTest 7722 public void testFontResourcesXml_restrictedContext() 7723 throws PackageManager.NameNotFoundException { 7724 Context restrictedContext = mActivity.createPackageContext(mActivity.getPackageName(), 7725 Context.CONTEXT_RESTRICTED); 7726 LayoutInflater layoutInflater = (LayoutInflater) restrictedContext.getSystemService( 7727 Context.LAYOUT_INFLATER_SERVICE); 7728 View root = layoutInflater.inflate(R.layout.textview_restricted_layout, null); 7729 7730 mTextView = root.findViewById(R.id.textview_fontresource_fontfamily); 7731 assertEquals(Typeface.DEFAULT, mTextView.getTypeface()); 7732 mTextView = root.findViewById(R.id.textview_fontxmlresource_fontfamily); 7733 assertEquals(Typeface.DEFAULT, mTextView.getTypeface()); 7734 mTextView = root.findViewById(R.id.textview_fontxmlresource_nonFontReference); 7735 assertEquals(Typeface.DEFAULT, mTextView.getTypeface()); 7736 mTextView = root.findViewById(R.id.textview_fontresource_style); 7737 assertEquals(Typeface.DEFAULT, mTextView.getTypeface()); 7738 mTextView = root.findViewById(R.id.textview_fontxmlresource_style); 7739 assertEquals(Typeface.DEFAULT, mTextView.getTypeface()); 7740 mTextView = root.findViewById(R.id.textview_fontresource_textAppearance); 7741 assertEquals(Typeface.DEFAULT, mTextView.getTypeface()); 7742 mTextView = root.findViewById(R.id.textview_fontxmlresource_textAppearance); 7743 assertEquals(Typeface.DEFAULT, mTextView.getTypeface()); 7744 } 7745 7746 private void initializeTextForSmartSelection(CharSequence text) throws Throwable { 7747 assertTrue(text.length() >= SMARTSELECT_END); 7748 mActivityRule.runOnUiThread(() -> { 7749 mTextView.setTextIsSelectable(true); 7750 mTextView.setText(text); 7751 mTextView.setTextClassifier(FAKE_TEXT_CLASSIFIER); 7752 mTextView.requestFocus(); 7753 }); 7754 mInstrumentation.waitForIdleSync(); 7755 } 7756 7757 private void emulateClickOnView(View view, int offsetX, int offsetY) { 7758 CtsTouchUtils.emulateTapOnView(mInstrumentation, view, offsetX, offsetY); 7759 SystemClock.sleep(CLICK_TIMEOUT); 7760 } 7761 7762 private void emulateLongPressOnView(View view, int offsetX, int offsetY) { 7763 CtsTouchUtils.emulateLongPressOnView(mInstrumentation, view, offsetX, offsetY); 7764 // TODO: Ideally, we shouldn't have to wait for a click timeout after a long-press but it 7765 // seems like we have a minor bug (call it inconvenience) in TextView that requires this. 7766 SystemClock.sleep(CLICK_TIMEOUT); 7767 } 7768 7769 /** 7770 * Some TextView attributes require non-fixed width and/or layout height. This function removes 7771 * all other existing views from the layout leaving only one auto-size TextView (for exercising 7772 * the auto-size behavior) which has been set up to suit the test needs. 7773 * 7774 * @param viewId The id of the view to prepare. 7775 * @param shouldWrapLayoutContent Specifies if the layout params should wrap content 7776 * 7777 * @return a TextView configured for auto size tests. 7778 */ 7779 private TextView prepareAndRetrieveAutoSizeTestData(final int viewId, 7780 final boolean shouldWrapLayoutContent) throws Throwable { 7781 mActivityRule.runOnUiThread(() -> { 7782 LinearLayout ll = (LinearLayout) mActivity.findViewById(R.id.layout_textviewtest); 7783 TextView targetedTextView = (TextView) mActivity.findViewById(viewId); 7784 ll.removeAllViews(); 7785 ll.addView(targetedTextView); 7786 }); 7787 mInstrumentation.waitForIdleSync(); 7788 7789 final TextView textView = (TextView) mActivity.findViewById(viewId); 7790 if (shouldWrapLayoutContent) { 7791 // Do not force exact width or height. 7792 final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 7793 LinearLayout.LayoutParams.WRAP_CONTENT, 7794 LinearLayout.LayoutParams.WRAP_CONTENT); 7795 mActivityRule.runOnUiThread(() -> { 7796 textView.setLayoutParams(layoutParams); 7797 }); 7798 mInstrumentation.waitForIdleSync(); 7799 } 7800 7801 return textView; 7802 } 7803 7804 /** 7805 * Removes all existing views from the layout and adds a basic TextView (for exercising the 7806 * ClickableSpan onClick() behavior) in order to prevent scrolling. Adds a ClickableSpan to the 7807 * TextView and returns the ClickableSpan and position details about it to be used in individual 7808 * tests. 7809 */ 7810 private ClickableSpanTestDetails prepareAndRetrieveClickableSpanDetails() throws Throwable { 7811 mActivityRule.runOnUiThread(() -> { 7812 LinearLayout ll = (LinearLayout) mActivity.findViewById(R.id.layout_textviewtest); 7813 ll.removeAllViews(); 7814 mTextView = new TextView(mActivity); 7815 ll.addView(mTextView); 7816 }); 7817 mInstrumentation.waitForIdleSync(); 7818 7819 ClickableSpan mockTextLink = mock(ClickableSpan.class); 7820 StringBuilder textViewContent = new StringBuilder(); 7821 String clickableString = "clickMe!"; 7822 textViewContent.append(clickableString); 7823 final int startPos = 0; 7824 7825 // Insert more characters to make some room for swiping. 7826 for (int i = 0; i < 200; i++) { 7827 textViewContent.append(" text"); 7828 } 7829 SpannableString spannableString = new SpannableString(textViewContent); 7830 final int endPos = clickableString.length(); 7831 spannableString.setSpan(mockTextLink, startPos, endPos, 0); 7832 mActivityRule.runOnUiThread(() -> { 7833 mTextView.setText(spannableString); 7834 mTextView.setMovementMethod(LinkMovementMethod.getInstance()); 7835 }); 7836 mInstrumentation.waitForIdleSync(); 7837 7838 return new ClickableSpanTestDetails(mockTextLink, mTextView, startPos, endPos); 7839 } 7840 7841 private static final class ClickableSpanTestDetails { 7842 ClickableSpan mClickableSpan; 7843 int mXPosInside; 7844 int mYPosInside; 7845 int mXPosOutside; 7846 int mYPosOutside; 7847 7848 private int mStartCharPos; 7849 private int mEndCharPos; 7850 private TextView mParent; 7851 7852 ClickableSpanTestDetails(ClickableSpan clickableSpan, TextView parent, 7853 int startCharPos, int endCharPos) { 7854 mClickableSpan = clickableSpan; 7855 mParent = parent; 7856 mStartCharPos = startCharPos; 7857 mEndCharPos = endCharPos; 7858 7859 calculatePositions(); 7860 } 7861 7862 private void calculatePositions() { 7863 int xStart = (int) mParent.getLayout().getPrimaryHorizontal(mStartCharPos, true); 7864 int xEnd = (int) mParent.getLayout().getPrimaryHorizontal(mEndCharPos, true); 7865 int line = mParent.getLayout().getLineForOffset(mEndCharPos); 7866 int yTop = mParent.getLayout().getLineTop(line); 7867 int yBottom = mParent.getLayout().getLineBottom(line); 7868 7869 mXPosInside = (xStart + xEnd) / 2; 7870 mYPosInside = (yTop + yBottom) / 2; 7871 mXPosOutside = xEnd + 1; 7872 mYPosOutside = yBottom + 1; 7873 } 7874 } 7875 7876 private MotionEvent createMouseHoverEvent(View view) { 7877 final int[] xy = new int[2]; 7878 view.getLocationOnScreen(xy); 7879 final int viewWidth = view.getWidth(); 7880 final int viewHeight = view.getHeight(); 7881 float x = xy[0] + viewWidth / 2.0f; 7882 float y = xy[1] + viewHeight / 2.0f; 7883 long eventTime = SystemClock.uptimeMillis(); 7884 MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1]; 7885 pointerCoords[0] = new MotionEvent.PointerCoords(); 7886 pointerCoords[0].x = x; 7887 pointerCoords[0].y = y; 7888 final int[] pointerIds = new int[1]; 7889 pointerIds[0] = 0; 7890 return MotionEvent.obtain(0, eventTime, MotionEvent.ACTION_HOVER_MOVE, 1, pointerIds, 7891 pointerCoords, 0, 0, 0, 0, 0, InputDevice.SOURCE_MOUSE, 0); 7892 } 7893 7894 private void layout(final TextView textView) throws Throwable { 7895 mActivityRule.runOnUiThread(() -> mActivity.setContentView(textView)); 7896 mInstrumentation.waitForIdleSync(); 7897 } 7898 7899 private void layout(final int layoutId) throws Throwable { 7900 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layoutId)); 7901 mInstrumentation.waitForIdleSync(); 7902 } 7903 7904 private TextView findTextView(int id) { 7905 return (TextView) mActivity.findViewById(id); 7906 } 7907 7908 private int getAutoLinkMask(int id) { 7909 return findTextView(id).getAutoLinkMask(); 7910 } 7911 7912 private void setMaxLines(final int lines) throws Throwable { 7913 mActivityRule.runOnUiThread(() -> mTextView.setMaxLines(lines)); 7914 mInstrumentation.waitForIdleSync(); 7915 } 7916 7917 private void setMaxWidth(final int pixels) throws Throwable { 7918 mActivityRule.runOnUiThread(() -> mTextView.setMaxWidth(pixels)); 7919 mInstrumentation.waitForIdleSync(); 7920 } 7921 7922 private void setMinWidth(final int pixels) throws Throwable { 7923 mActivityRule.runOnUiThread(() -> mTextView.setMinWidth(pixels)); 7924 mInstrumentation.waitForIdleSync(); 7925 } 7926 7927 private void setMaxHeight(final int pixels) throws Throwable { 7928 mActivityRule.runOnUiThread(() -> mTextView.setMaxHeight(pixels)); 7929 mInstrumentation.waitForIdleSync(); 7930 } 7931 7932 private void setMinHeight(final int pixels) throws Throwable { 7933 mActivityRule.runOnUiThread(() -> mTextView.setMinHeight(pixels)); 7934 mInstrumentation.waitForIdleSync(); 7935 } 7936 7937 private void setMinLines(final int minLines) throws Throwable { 7938 mActivityRule.runOnUiThread(() -> mTextView.setMinLines(minLines)); 7939 mInstrumentation.waitForIdleSync(); 7940 } 7941 7942 /** 7943 * Convenience for {@link TextView#setText(CharSequence, BufferType)}. And 7944 * the buffer type is fixed to SPANNABLE. 7945 * 7946 * @param tv the text view 7947 * @param content the content 7948 */ 7949 private void setSpannableText(final TextView tv, final String content) throws Throwable { 7950 mActivityRule.runOnUiThread(() -> tv.setText(content, BufferType.SPANNABLE)); 7951 mInstrumentation.waitForIdleSync(); 7952 } 7953 7954 private void setLines(final int lines) throws Throwable { 7955 mActivityRule.runOnUiThread(() -> mTextView.setLines(lines)); 7956 mInstrumentation.waitForIdleSync(); 7957 } 7958 7959 private void setHorizontallyScrolling(final boolean whether) throws Throwable { 7960 mActivityRule.runOnUiThread(() -> mTextView.setHorizontallyScrolling(whether)); 7961 mInstrumentation.waitForIdleSync(); 7962 } 7963 7964 private void setWidth(final int pixels) throws Throwable { 7965 mActivityRule.runOnUiThread(() -> mTextView.setWidth(pixels)); 7966 mInstrumentation.waitForIdleSync(); 7967 } 7968 7969 private void setHeight(final int pixels) throws Throwable { 7970 mActivityRule.runOnUiThread(() -> mTextView.setHeight(pixels)); 7971 mInstrumentation.waitForIdleSync(); 7972 } 7973 7974 private void setMinEms(final int ems) throws Throwable { 7975 mActivityRule.runOnUiThread(() -> mTextView.setMinEms(ems)); 7976 mInstrumentation.waitForIdleSync(); 7977 } 7978 7979 private void setMaxEms(final int ems) throws Throwable { 7980 mActivityRule.runOnUiThread(() -> mTextView.setMaxEms(ems)); 7981 mInstrumentation.waitForIdleSync(); 7982 } 7983 7984 private void setEms(final int ems) throws Throwable { 7985 mActivityRule.runOnUiThread(() -> mTextView.setEms(ems)); 7986 mInstrumentation.waitForIdleSync(); 7987 } 7988 7989 private void setLineSpacing(final float add, final float mult) throws Throwable { 7990 mActivityRule.runOnUiThread(() -> mTextView.setLineSpacing(add, mult)); 7991 mInstrumentation.waitForIdleSync(); 7992 } 7993 7994 /** 7995 * Returns the x, y coordinates of text at a specified indices relative to the position of the 7996 * TextView. 7997 * 7998 * @param textView 7999 * @param startIndex start index of the text in the textView 8000 * @param endIndex end index of the text in the textView 8001 */ 8002 private static Point getCenterPositionOfTextAt( 8003 TextView textView, int startIndex, int endIndex) { 8004 int xStart = (int) textView.getLayout().getPrimaryHorizontal(startIndex, true); 8005 int xEnd = (int) textView.getLayout().getPrimaryHorizontal(endIndex, true); 8006 int line = textView.getLayout().getLineForOffset(endIndex); 8007 int yTop = textView.getLayout().getLineTop(line); 8008 int yBottom = textView.getLayout().getLineBottom(line); 8009 8010 return new Point((xStart + xEnd) / 2 /* x */, (yTop + yBottom) / 2 /* y */); 8011 } 8012 8013 private static abstract class TestSelectedRunnable implements Runnable { 8014 private TextView mTextView; 8015 private boolean mIsSelected1; 8016 private boolean mIsSelected2; 8017 8018 public TestSelectedRunnable(TextView textview) { 8019 mTextView = textview; 8020 } 8021 8022 public boolean getIsSelected1() { 8023 return mIsSelected1; 8024 } 8025 8026 public boolean getIsSelected2() { 8027 return mIsSelected2; 8028 } 8029 8030 public void saveIsSelected1() { 8031 mIsSelected1 = mTextView.isSelected(); 8032 } 8033 8034 public void saveIsSelected2() { 8035 mIsSelected2 = mTextView.isSelected(); 8036 } 8037 } 8038 8039 private static abstract class TestLayoutRunnable implements Runnable { 8040 private TextView mTextView; 8041 private Layout mLayout; 8042 8043 public TestLayoutRunnable(TextView textview) { 8044 mTextView = textview; 8045 } 8046 8047 public Layout getLayout() { 8048 return mLayout; 8049 } 8050 8051 public void saveLayout() { 8052 mLayout = mTextView.getLayout(); 8053 } 8054 } 8055 8056 private static class MockTextWatcher implements TextWatcher { 8057 private boolean mHasCalledAfterTextChanged; 8058 private boolean mHasCalledBeforeTextChanged; 8059 private boolean mHasOnTextChanged; 8060 8061 public void reset(){ 8062 mHasCalledAfterTextChanged = false; 8063 mHasCalledBeforeTextChanged = false; 8064 mHasOnTextChanged = false; 8065 } 8066 8067 public boolean hasCalledAfterTextChanged() { 8068 return mHasCalledAfterTextChanged; 8069 } 8070 8071 public boolean hasCalledBeforeTextChanged() { 8072 return mHasCalledBeforeTextChanged; 8073 } 8074 8075 public boolean hasCalledOnTextChanged() { 8076 return mHasOnTextChanged; 8077 } 8078 8079 public void afterTextChanged(Editable s) { 8080 mHasCalledAfterTextChanged = true; 8081 } 8082 8083 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 8084 mHasCalledBeforeTextChanged = true; 8085 } 8086 8087 public void onTextChanged(CharSequence s, int start, int before, int count) { 8088 mHasOnTextChanged = true; 8089 } 8090 } 8091 8092 /** 8093 * A TextWatcher that converts the text to spaces whenever the text changes. 8094 */ 8095 private static class ConvertToSpacesTextWatcher implements TextWatcher { 8096 boolean mChangingText; 8097 8098 @Override 8099 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 8100 } 8101 8102 @Override 8103 public void onTextChanged(CharSequence s, int start, int before, int count) { 8104 } 8105 8106 @Override 8107 public void afterTextChanged(Editable s) { 8108 // Avoid infinite recursion. 8109 if (mChangingText) { 8110 return; 8111 } 8112 mChangingText = true; 8113 // Create a string of s.length() spaces. 8114 StringBuilder builder = new StringBuilder(s.length()); 8115 for (int i = 0; i < s.length(); i++) { 8116 builder.append(' '); 8117 } 8118 s.replace(0, s.length(), builder.toString()); 8119 mChangingText = false; 8120 } 8121 } 8122 } 8123