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