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