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