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