The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 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 | |
Neal Nguyen | 1a44d5d | 2010-01-13 10:42:43 -0800 | [diff] [blame] | 17 | package android.widget; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 18 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 19 | import static junit.framework.Assert.assertEquals; |
| 20 | import static junit.framework.Assert.assertNotNull; |
| 21 | import static junit.framework.Assert.assertTrue; |
| 22 | |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 23 | import android.app.Activity; |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 24 | import android.app.Instrumentation; |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 25 | import android.content.Intent; |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 26 | import android.graphics.Paint; |
Chet Haase | 9392d40 | 2016-06-08 15:40:46 -0700 | [diff] [blame] | 27 | import android.platform.test.annotations.Presubmit; |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 28 | import android.support.test.InstrumentationRegistry; |
| 29 | import android.support.test.annotation.UiThreadTest; |
| 30 | import android.support.test.filters.MediumTest; |
| 31 | import android.support.test.rule.ActivityTestRule; |
| 32 | import android.support.test.runner.AndroidJUnit4; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 33 | import android.text.GetChars; |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 34 | import android.text.Layout; |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 35 | import android.text.Selection; |
| 36 | import android.text.Spannable; |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 37 | import android.view.View; |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 38 | |
| 39 | import org.junit.Before; |
| 40 | import org.junit.Rule; |
| 41 | import org.junit.Test; |
| 42 | import org.junit.runner.RunWith; |
| 43 | |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 44 | import java.util.Locale; |
Doug Felt | cb379120 | 2011-07-07 11:57:48 -0700 | [diff] [blame] | 45 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 46 | /** |
| 47 | * TextViewTest tests {@link TextView}. |
| 48 | */ |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 49 | @RunWith(AndroidJUnit4.class) |
| 50 | @MediumTest |
| 51 | public class TextViewTest { |
| 52 | @Rule |
| 53 | public ActivityTestRule<TextViewActivity> mActivityRule = new ActivityTestRule<>( |
| 54 | TextViewActivity.class); |
| 55 | private Instrumentation mInstrumentation; |
| 56 | private Activity mActivity; |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 57 | private TextView mTextView; |
Clara Bayarri | d7e48e1 | 2015-08-21 15:21:08 +0100 | [diff] [blame] | 58 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 59 | @Before |
| 60 | public void setup() { |
| 61 | mActivity = mActivityRule.getActivity(); |
| 62 | mInstrumentation = InstrumentationRegistry.getInstrumentation(); |
Clara Bayarri | d7e48e1 | 2015-08-21 15:21:08 +0100 | [diff] [blame] | 63 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 64 | |
Chet Haase | 9392d40 | 2016-06-08 15:40:46 -0700 | [diff] [blame] | 65 | @Presubmit |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 66 | @UiThreadTest |
| 67 | @Test |
| 68 | public void testArray() { |
| 69 | mTextView = new TextView(mActivity); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 70 | |
| 71 | char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', |
| 72 | 'W', 'o', 'r', 'l', 'd', '!' }; |
| 73 | |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 74 | mTextView.setText(c, 1, 4); |
| 75 | CharSequence oldText = mTextView.getText(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 76 | |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 77 | mTextView.setText(c, 4, 5); |
| 78 | CharSequence newText = mTextView.getText(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 79 | |
| 80 | assertTrue(newText == oldText); |
| 81 | |
| 82 | assertEquals(5, newText.length()); |
| 83 | assertEquals('o', newText.charAt(0)); |
| 84 | assertEquals("o Wor", newText.toString()); |
| 85 | |
| 86 | assertEquals(" Wo", newText.subSequence(1, 4)); |
| 87 | |
| 88 | char[] c2 = new char[7]; |
| 89 | ((GetChars) newText).getChars(1, 4, c2, 2); |
| 90 | assertEquals('\0', c2[1]); |
| 91 | assertEquals(' ', c2[2]); |
| 92 | assertEquals('W', c2[3]); |
| 93 | assertEquals('o', c2[4]); |
| 94 | assertEquals('\0', c2[5]); |
| 95 | } |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 96 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 97 | @Test |
| 98 | public void testProcessTextActivityResultNonEditable() throws Throwable { |
| 99 | mActivityRule.runOnUiThread(() -> mTextView = new TextView(mActivity)); |
| 100 | mInstrumentation.waitForIdleSync(); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 101 | CharSequence originalText = "This is some text."; |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 102 | mTextView.setText(originalText, TextView.BufferType.SPANNABLE); |
| 103 | assertEquals(originalText, mTextView.getText().toString()); |
| 104 | mTextView.setTextIsSelectable(true); |
| 105 | Selection.setSelection((Spannable) mTextView.getText(), 0, mTextView.getText().length()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 106 | |
Clara Bayarri | d7e48e1 | 2015-08-21 15:21:08 +0100 | [diff] [blame] | 107 | // We need to run this in the UI thread, as it will create a Toast. |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 108 | mActivityRule.runOnUiThread(() -> { |
| 109 | CharSequence newText = "Text is replaced."; |
| 110 | Intent data = new Intent(); |
| 111 | data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); |
| 112 | mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, |
| 113 | data); |
Clara Bayarri | d7e48e1 | 2015-08-21 15:21:08 +0100 | [diff] [blame] | 114 | }); |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 115 | mInstrumentation.waitForIdleSync(); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 116 | |
| 117 | // This is a TextView, which can't be modified. Hence no change should have been made. |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 118 | assertEquals(originalText, mTextView.getText().toString()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 119 | } |
| 120 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 121 | @Test |
| 122 | public void testProcessTextActivityResultEditable() throws Throwable { |
| 123 | mActivityRule.runOnUiThread(() -> mTextView = new EditText(mActivity)); |
| 124 | mInstrumentation.waitForIdleSync(); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 125 | CharSequence originalText = "This is some text."; |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 126 | mTextView.setText(originalText, TextView.BufferType.SPANNABLE); |
| 127 | assertEquals(originalText, mTextView.getText().toString()); |
| 128 | mTextView.setTextIsSelectable(true); |
| 129 | Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 130 | |
| 131 | CharSequence newText = "Text is replaced."; |
| 132 | Intent data = new Intent(); |
| 133 | data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 134 | mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 135 | |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 136 | assertEquals(newText, mTextView.getText().toString()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 137 | } |
| 138 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 139 | @Test |
| 140 | public void testProcessTextActivityResultCancel() throws Throwable { |
| 141 | mActivityRule.runOnUiThread(() -> mTextView = new EditText(mActivity)); |
| 142 | mInstrumentation.waitForIdleSync(); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 143 | CharSequence originalText = "This is some text."; |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 144 | mTextView.setText(originalText, TextView.BufferType.SPANNABLE); |
| 145 | assertEquals(originalText, mTextView.getText().toString()); |
| 146 | mTextView.setTextIsSelectable(true); |
| 147 | Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 148 | |
| 149 | CharSequence newText = "Text is replaced."; |
| 150 | Intent data = new Intent(); |
| 151 | data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 152 | mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_CANCELED, |
| 153 | data); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 154 | |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 155 | assertEquals(originalText, mTextView.getText().toString()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 156 | } |
| 157 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 158 | @Test |
| 159 | public void testProcessTextActivityNoData() throws Throwable { |
| 160 | mActivityRule.runOnUiThread(() -> mTextView = new EditText(mActivity)); |
| 161 | mInstrumentation.waitForIdleSync(); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 162 | CharSequence originalText = "This is some text."; |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 163 | mTextView.setText(originalText, TextView.BufferType.SPANNABLE); |
| 164 | assertEquals(originalText, mTextView.getText().toString()); |
| 165 | mTextView.setTextIsSelectable(true); |
| 166 | Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 167 | |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 168 | mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, null); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 169 | |
Andrei Stingaceanu | 3df24c3 | 2016-08-01 17:15:07 +0100 | [diff] [blame] | 170 | assertEquals(originalText, mTextView.getText().toString()); |
Clara Bayarri | 5b7665a | 2015-05-15 16:32:50 +0100 | [diff] [blame] | 171 | } |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 172 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 173 | @Test |
| 174 | @UiThreadTest |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 175 | public void testHyphenationWidth() { |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 176 | mTextView = new TextView(mActivity); |
| 177 | mTextView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); |
| 178 | mTextView.setTextLocale(Locale.US); |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 179 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 180 | Paint paint = mTextView.getPaint(); |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 181 | |
| 182 | String word = "thisissuperlonglongword"; |
| 183 | float wordWidth = paint.measureText(word, 0, word.length()); |
| 184 | |
| 185 | StringBuilder sb = new StringBuilder(); |
| 186 | for (int i = 0; i < 100; ++i) { |
| 187 | sb.append(word); |
| 188 | sb.append(" "); |
| 189 | } |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 190 | mTextView.setText(sb.toString()); |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 191 | |
| 192 | int width = (int)(wordWidth * 0.7); |
| 193 | int height = 4096; // enough for all text. |
| 194 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 195 | mTextView.measure( |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 196 | View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), |
| 197 | View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 198 | mTextView.layout(0, 0, width, height); |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 199 | |
Siyamed Sinir | 2191418 | 2017-06-16 15:24:00 -0700 | [diff] [blame] | 200 | Layout layout = mTextView.getLayout(); |
Seigo Nonaka | 9863f67 | 2016-12-14 17:56:10 +0900 | [diff] [blame] | 201 | assertNotNull(layout); |
| 202 | |
| 203 | int lineCount = layout.getLineCount(); |
| 204 | boolean hyphenationHappend = false; |
| 205 | for (int i = 0; i < lineCount; ++i) { |
| 206 | if (layout.getHyphen(i) != 1) { |
| 207 | continue; // Hyphantion does not happen. |
| 208 | } |
| 209 | hyphenationHappend = true; |
| 210 | |
| 211 | int start = layout.getLineStart(i); |
| 212 | int end = layout.getLineEnd(i); |
| 213 | |
| 214 | float withoutHyphenLength = paint.measureText(sb, start, end); |
| 215 | float withHyphenLength = layout.getLineWidth(i); |
| 216 | |
| 217 | assertTrue("LineWidth should take account of hyphen length.", |
| 218 | withHyphenLength > withoutHyphenLength); |
| 219 | } |
| 220 | assertTrue("Hyphenation must happen on TextView narrower than the word width", |
| 221 | hyphenationHappend); |
| 222 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 223 | } |