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