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