1 /* 2 * Copyright (C) 2011 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 com.android.ex.chips; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.drawable.BitmapDrawable; 22 import android.graphics.drawable.Drawable; 23 import android.test.AndroidTestCase; 24 import android.test.suitebuilder.annotation.SmallTest; 25 import android.text.Editable; 26 import android.text.SpannableStringBuilder; 27 import android.text.style.ImageSpan; 28 import android.text.util.Rfc822Tokenizer; 29 import android.widget.TextView; 30 31 import com.android.ex.chips.BaseRecipientAdapter; 32 import com.android.ex.chips.RecipientEditTextView; 33 import com.android.ex.chips.RecipientEntry; 34 import com.android.ex.chips.recipientchip.DrawableRecipientChip; 35 import com.android.ex.chips.recipientchip.VisibleRecipientChip;; 36 37 import java.util.regex.Pattern; 38 39 @SmallTest 40 public class ChipsTest extends AndroidTestCase { 41 private DrawableRecipientChip[] mMockRecips; 42 43 private RecipientEntry[] mMockEntries; 44 45 private Rfc822Tokenizer mTokenizer; 46 47 private Editable mEditable; 48 49 class BaseMockRecipientEditTextView extends RecipientEditTextView { 50 51 public BaseMockRecipientEditTextView(Context context) { 52 super(context, null); 53 mTokenizer = new Rfc822Tokenizer(); 54 setTokenizer(mTokenizer); 55 } 56 57 @Override 58 public DrawableRecipientChip[] getSortedRecipients() { 59 return mMockRecips; 60 } 61 62 @Override 63 public int getLineHeight() { 64 return 48; 65 } 66 67 @Override 68 Drawable getChipBackground(RecipientEntry contact) { 69 return createChipBackground(); 70 } 71 72 @Override 73 public int getViewWidth() { 74 return 100; 75 } 76 } 77 78 class MockRecipientEditTextView extends BaseMockRecipientEditTextView { 79 80 public MockRecipientEditTextView(Context context) { 81 super(context); 82 mTokenizer = new Rfc822Tokenizer(); 83 setTokenizer(mTokenizer); 84 } 85 86 @Override 87 public DrawableRecipientChip[] getSortedRecipients() { 88 return mMockRecips; 89 } 90 91 @Override 92 public Editable getText() { 93 return mEditable; 94 } 95 96 @Override 97 public Editable getSpannable() { 98 return mEditable; 99 } 100 101 @Override 102 public int getLineHeight() { 103 return 48; 104 } 105 106 @Override 107 Drawable getChipBackground(RecipientEntry contact) { 108 return createChipBackground(); 109 } 110 111 @Override 112 public int length() { 113 return mEditable != null ? mEditable.length() : 0; 114 } 115 116 @Override 117 public String toString() { 118 return mEditable != null ? mEditable.toString() : ""; 119 } 120 121 @Override 122 public int getViewWidth() { 123 return 100; 124 } 125 } 126 127 private class TestBaseRecipientAdapter extends BaseRecipientAdapter { 128 public TestBaseRecipientAdapter(final Context context) { 129 super(context); 130 } 131 132 public TestBaseRecipientAdapter(final Context context, final int preferredMaxResultCount, 133 final int queryMode) { 134 super(context, preferredMaxResultCount, queryMode); 135 } 136 } 137 138 private MockRecipientEditTextView createViewForTesting() { 139 mEditable = new SpannableStringBuilder(); 140 MockRecipientEditTextView view = new MockRecipientEditTextView(getContext()); 141 view.setAdapter(new TestBaseRecipientAdapter(getContext())); 142 return view; 143 } 144 145 public void testCreateDisplayText() { 146 RecipientEditTextView view = createViewForTesting(); 147 RecipientEntry entry = RecipientEntry.constructGeneratedEntry("User Name, Jr", 148 "user (at) username.com", true); 149 String testAddress = view.createAddressText(entry); 150 String testDisplay = view.createChipDisplayText(entry); 151 assertEquals("Expected a properly formatted RFC email address", 152 "\"User Name, Jr\" <user (at) username.com>, ", testAddress); 153 assertEquals("Expected a displayable name", "User Name, Jr", testDisplay); 154 155 RecipientEntry alreadyFormatted = 156 RecipientEntry.constructFakeEntry("user (at) username.com, ", true); 157 testAddress = view.createAddressText(alreadyFormatted); 158 testDisplay = view.createChipDisplayText(alreadyFormatted); 159 assertEquals("Expected a properly formatted RFC email address", "<user (at) username.com>, ", 160 testAddress); 161 assertEquals("Expected a displayable name", "user (at) username.com", testDisplay); 162 163 RecipientEntry alreadyFormattedNoSpace = RecipientEntry 164 .constructFakeEntry("user (at) username.com,", true); 165 testAddress = view.createAddressText(alreadyFormattedNoSpace); 166 assertEquals("Expected a properly formatted RFC email address", "<user (at) username.com>, ", 167 testAddress); 168 169 RecipientEntry alreadyNamed = RecipientEntry.constructGeneratedEntry("User Name", 170 "\"User Name, Jr\" <user (at) username.com>", true); 171 testAddress = view.createAddressText(alreadyNamed); 172 testDisplay = view.createChipDisplayText(alreadyNamed); 173 assertEquals( 174 "Expected address that used the name not the excess address name", 175 "User Name <user (at) username.com>, ", testAddress); 176 assertEquals("Expected a displayable name", "User Name", testDisplay); 177 } 178 179 public void testSanitizeBetween() { 180 // First, add 2 chips and then make sure we remove 181 // the extra content between them correctly. 182 populateMocks(2); 183 MockRecipientEditTextView view = createViewForTesting(); 184 String first = (String) mTokenizer.terminateToken("FIRST"); 185 String second = (String) mTokenizer.terminateToken("SECOND"); 186 String extra = "EXTRA"; 187 mEditable = new SpannableStringBuilder(); 188 mEditable.append(first + extra + second); 189 int firstStart = mEditable.toString().indexOf(first); 190 int firstEnd = firstStart + first.trim().length(); 191 int secondStart = mEditable.toString().indexOf(second); 192 int secondEnd = secondStart + second.trim().length(); 193 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], firstStart, firstEnd, 0); 194 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], secondStart, secondEnd, 0); 195 view.sanitizeBetween(); 196 String editableString = mEditable.toString(); 197 assertEquals(editableString.indexOf(extra), -1); 198 assertEquals(editableString.indexOf(first), firstStart); 199 assertEquals(editableString.indexOf(second), secondStart - extra.length()); 200 assertEquals(editableString, (first + second)); 201 202 // Add 1 chip and make sure that we remove the extra stuff before it correctly. 203 mEditable = new SpannableStringBuilder(); 204 populateMocks(1); 205 mEditable.append(extra); 206 mEditable.append(first); 207 firstStart = mEditable.toString().indexOf(first); 208 firstEnd = firstStart + first.length(); 209 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], firstStart, firstEnd, 0); 210 view.sanitizeBetween(); 211 assertEquals(mEditable.toString(), first); 212 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), firstStart 213 - extra.length()); 214 } 215 216 public void testSanitizeEnd() { 217 // First, add 2 chips and then make sure we remove 218 // the extra content between them correctly. 219 populateMocks(2); 220 MockRecipientEditTextView view = createViewForTesting(); 221 String first = (String) mTokenizer.terminateToken("FIRST"); 222 String second = (String) mTokenizer.terminateToken("SECOND"); 223 String extra = "EXTRA"; 224 mEditable = new SpannableStringBuilder(); 225 mEditable.append(first + second); 226 int firstStart = mEditable.toString().indexOf(first); 227 int firstEnd = firstStart + first.trim().length(); 228 int secondStart = mEditable.toString().indexOf(second); 229 int secondEnd = secondStart + second.trim().length(); 230 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], firstStart, firstEnd, 0); 231 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], secondStart, secondEnd, 0); 232 view.sanitizeEnd(); 233 String editableString = mEditable.toString(); 234 assertEquals(editableString.indexOf(extra), -1); 235 assertEquals(editableString.indexOf(first), firstStart); 236 assertEquals(editableString.indexOf(second), secondStart); 237 assertEquals(editableString, (first + second)); 238 mEditable.append(extra); 239 editableString = mEditable.toString(); 240 assertEquals(mEditable.toString(), (first + second + extra)); 241 view.sanitizeEnd(); 242 assertEquals(mEditable.toString(), (first + second)); 243 } 244 245 public void testMoreChipPlainText() { 246 MockRecipientEditTextView view = createViewForTesting(); 247 view.setMoreItem(createTestMoreItem()); 248 String first = (String) mTokenizer.terminateToken("FIRST"); 249 String second = (String) mTokenizer.terminateToken("SECOND"); 250 String third = (String) mTokenizer.terminateToken("THIRD"); 251 mEditable = new SpannableStringBuilder(); 252 mEditable.append(first+second+third); 253 int thirdStart = mEditable.toString().indexOf(third); 254 int thirdEnd = thirdStart + third.trim().length(); 255 view.createMoreChipPlainText(); 256 ImageSpan moreChip = view.getMoreChip(); 257 assertEquals(mEditable.getSpanStart(moreChip), thirdStart); 258 assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1); 259 } 260 261 public void testCountTokens() { 262 MockRecipientEditTextView view = createViewForTesting(); 263 view.setMoreItem(createTestMoreItem()); 264 String first = (String) mTokenizer.terminateToken("FIRST"); 265 String second = (String) mTokenizer.terminateToken("SECOND"); 266 String third = (String) mTokenizer.terminateToken("THIRD"); 267 String fourth = "FOURTH,"; 268 String fifth = "FIFTH,"; 269 mEditable = new SpannableStringBuilder(); 270 mEditable.append(first+second+third+fourth+fifth); 271 assertEquals(view.countTokens(mEditable), 5); 272 } 273 274 public void testTooManyRecips() { 275 BaseMockRecipientEditTextView view = new BaseMockRecipientEditTextView(getContext()); 276 view.setMoreItem(createTestMoreItem()); 277 for (int i = 0; i < 100; i++) { 278 view.append(mTokenizer.terminateToken(i + "")); 279 } 280 assertEquals(view.countTokens(view.getText()), 100); 281 view.handlePendingChips(); 282 view.createMoreChip(); 283 ImageSpan moreChip = view.getMoreChip(); 284 // We show 2 chips then place a more chip. 285 int secondStart = view.getText().toString().indexOf( 286 (String) mTokenizer.terminateToken(RecipientEditTextView.CHIP_LIMIT + "")); 287 assertEquals(view.getText().getSpanStart(moreChip), secondStart); 288 assertEquals(view.getText().getSpanEnd(moreChip), view.length()); 289 assertEquals(view.getSortedRecipients(), null); 290 } 291 292 public void testMoreChip() { 293 // Add 3 chips: this is the trigger point at which the more chip will be created. 294 // Test that adding the chips and then creating and removing the more chip, as if 295 // the user were focusing/ removing focus from the chips field. 296 populateMocks(3); 297 MockRecipientEditTextView view = createViewForTesting(); 298 view.setMoreItem(createTestMoreItem()); 299 String first = (String) mTokenizer.terminateToken("FIRST"); 300 String second = (String) mTokenizer.terminateToken("SECOND"); 301 String third = (String) mTokenizer.terminateToken("THIRD"); 302 mEditable = new SpannableStringBuilder(); 303 mEditable.append(first+second+third); 304 305 int firstStart = mEditable.toString().indexOf(first); 306 int firstEnd = firstStart + first.trim().length(); 307 int secondStart = mEditable.toString().indexOf(second); 308 int secondEnd = secondStart + second.trim().length(); 309 int thirdStart = mEditable.toString().indexOf(third); 310 int thirdEnd = thirdStart + third.trim().length(); 311 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 312 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 313 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 314 315 view.createMoreChip(); 316 assertEquals(mEditable.toString(), first+second+third); 317 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 318 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 319 // Find the more chip. 320 ImageSpan moreChip = view.getMoreChip(); 321 assertEquals(mEditable.getSpanStart(moreChip), thirdStart); 322 assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1); 323 324 view.removeMoreChip(); 325 assertEquals(mEditable.toString(), first+second+third); 326 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 327 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd); 328 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 329 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart); 330 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd); 331 moreChip = view.getMoreChip(); 332 assertEquals(mEditable.getSpanStart(moreChip), -1); 333 334 // Rinse and repeat, just in case! 335 view.createMoreChip(); 336 assertEquals(mEditable.toString(), first+second+third); 337 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 338 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 339 // Find the more chip. 340 moreChip = view.getMoreChip(); 341 assertEquals(mEditable.getSpanStart(moreChip), thirdStart); 342 assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1); 343 344 view.removeMoreChip(); 345 assertEquals(mEditable.toString(), first+second+third); 346 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 347 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd); 348 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 349 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart); 350 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd); 351 moreChip = view.getMoreChip(); 352 assertEquals(mEditable.getSpanStart(moreChip), -1); 353 } 354 355 public void testMoreChipLotsOfUsers() { 356 // Test adding and removing the more chip in the case where we have a lot of users. 357 populateMocks(10); 358 MockRecipientEditTextView view = createViewForTesting(); 359 view.setMoreItem(createTestMoreItem()); 360 String first = (String) mTokenizer.terminateToken("FIRST"); 361 String second = (String) mTokenizer.terminateToken("SECOND"); 362 String third = (String) mTokenizer.terminateToken("THIRD"); 363 String fourth = (String) mTokenizer.terminateToken("FOURTH"); 364 String fifth = (String) mTokenizer.terminateToken("FIFTH"); 365 String sixth = (String) mTokenizer.terminateToken("SIXTH"); 366 String seventh = (String) mTokenizer.terminateToken("SEVENTH"); 367 String eigth = (String) mTokenizer.terminateToken("EIGHTH"); 368 String ninth = (String) mTokenizer.terminateToken("NINTH"); 369 String tenth = (String) mTokenizer.terminateToken("TENTH"); 370 mEditable = new SpannableStringBuilder(); 371 mEditable.append(first+second+third+fourth+fifth+sixth+seventh+eigth+ninth+tenth); 372 373 int firstStart = mEditable.toString().indexOf(first); 374 int firstEnd = firstStart + first.trim().length(); 375 int secondStart = mEditable.toString().indexOf(second); 376 int secondEnd = secondStart + second.trim().length(); 377 int thirdStart = mEditable.toString().indexOf(third); 378 int thirdEnd = thirdStart + third.trim().length(); 379 int fourthStart = mEditable.toString().indexOf(fourth); 380 int fourthEnd = fourthStart + fourth.trim().length(); 381 int fifthStart = mEditable.toString().indexOf(fifth); 382 int fifthEnd = fifthStart + fifth.trim().length(); 383 int sixthStart = mEditable.toString().indexOf(sixth); 384 int sixthEnd = sixthStart + sixth.trim().length(); 385 int seventhStart = mEditable.toString().indexOf(seventh); 386 int seventhEnd = seventhStart + seventh.trim().length(); 387 int eighthStart = mEditable.toString().indexOf(eigth); 388 int eighthEnd = eighthStart + eigth.trim().length(); 389 int ninthStart = mEditable.toString().indexOf(ninth); 390 int ninthEnd = ninthStart + ninth.trim().length(); 391 int tenthStart = mEditable.toString().indexOf(tenth); 392 int tenthEnd = tenthStart + tenth.trim().length(); 393 mEditable.setSpan(mMockRecips[mMockRecips.length - 10], firstStart, firstEnd, 0); 394 mEditable.setSpan(mMockRecips[mMockRecips.length - 9], secondStart, secondEnd, 0); 395 mEditable.setSpan(mMockRecips[mMockRecips.length - 8], thirdStart, thirdEnd, 0); 396 mEditable.setSpan(mMockRecips[mMockRecips.length - 7], fourthStart, fourthEnd, 0); 397 mEditable.setSpan(mMockRecips[mMockRecips.length - 6], fifthStart, fifthEnd, 0); 398 mEditable.setSpan(mMockRecips[mMockRecips.length - 5], sixthStart, sixthEnd, 0); 399 mEditable.setSpan(mMockRecips[mMockRecips.length - 4], seventhStart, seventhEnd, 0); 400 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], eighthStart, eighthEnd, 0); 401 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], ninthStart, ninthEnd, 0); 402 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], tenthStart, tenthEnd, 0); 403 404 view.createMoreChip(); 405 assertEquals(mEditable.toString(), first + second + third + fourth + fifth + sixth 406 + seventh + eigth + ninth + tenth); 407 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 10]), firstStart); 408 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 9]), secondStart); 409 // Find the more chip. 410 ImageSpan moreChip = view.getMoreChip(); 411 assertEquals(mEditable.getSpanStart(moreChip), thirdStart); 412 assertEquals(mEditable.getSpanEnd(moreChip), tenthEnd + 1); 413 414 view.removeMoreChip(); 415 assertEquals(mEditable.toString(), first + second + third + fourth + fifth + sixth 416 + seventh + eigth + ninth + tenth); 417 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 10]), firstStart); 418 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 9]), secondStart); 419 420 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 8]), thirdStart); 421 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 7]), fourthStart); 422 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 6]), fifthStart); 423 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 5]), sixthStart); 424 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), seventhStart); 425 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), eighthStart); 426 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), ninthStart); 427 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), tenthStart); 428 moreChip = view.getMoreChip(); 429 assertEquals(mEditable.getSpanStart(moreChip), -1); 430 431 } 432 433 public void testMoreChipSpecialChars() { 434 // Make sure the more chip correctly handles extra tokenizer characters in the middle 435 // of chip text. 436 populateMocks(3); 437 MockRecipientEditTextView view = createViewForTesting(); 438 view.setMoreItem(createTestMoreItem()); 439 String first = (String) mTokenizer.terminateToken("FI,RST"); 440 String second = (String) mTokenizer.terminateToken("SE,COND"); 441 String third = (String) mTokenizer.terminateToken("THI,RD"); 442 mEditable = new SpannableStringBuilder(); 443 mEditable.append(first+second+third); 444 445 int firstStart = mEditable.toString().indexOf(first); 446 int firstEnd = firstStart + first.trim().length(); 447 int secondStart = mEditable.toString().indexOf(second); 448 int secondEnd = secondStart + second.trim().length(); 449 int thirdStart = mEditable.toString().indexOf(third); 450 int thirdEnd = thirdStart + third.trim().length(); 451 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 452 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 453 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 454 455 view.createMoreChip(); 456 assertEquals(mEditable.toString(), first+second+third); 457 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 458 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 459 // Find the more chip. 460 ImageSpan moreChip = view.getMoreChip(); 461 assertEquals(mEditable.getSpanStart(moreChip), thirdStart); 462 assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1); 463 464 view.removeMoreChip(); 465 assertEquals(mEditable.toString(), first+second+third); 466 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 467 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd); 468 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 469 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart); 470 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd); 471 moreChip = view.getMoreChip(); 472 assertEquals(mEditable.getSpanStart(moreChip), -1); 473 } 474 475 public void testMoreChipDupes() { 476 // Make sure the more chip is correctly added and removed when we have duplicate chips. 477 populateMocks(4); 478 MockRecipientEditTextView view = createViewForTesting(); 479 view.setMoreItem(createTestMoreItem()); 480 String first = (String) mTokenizer.terminateToken("FIRST"); 481 String second = (String) mTokenizer.terminateToken("SECOND"); 482 String third = (String) mTokenizer.terminateToken("THIRD"); 483 mEditable = new SpannableStringBuilder(); 484 mEditable.append(first+second+third+third); 485 486 int firstStart = mEditable.toString().indexOf(first); 487 int firstEnd = firstStart + first.trim().length(); 488 int secondStart = mEditable.toString().indexOf(second); 489 int secondEnd = secondStart + second.trim().length(); 490 int thirdStart = mEditable.toString().indexOf(third); 491 int thirdEnd = thirdStart + third.trim().length(); 492 int thirdNextStart = mEditable.toString().indexOf(third, thirdEnd); 493 int thirdNextEnd = thirdNextStart + third.trim().length(); 494 mEditable.setSpan(mMockRecips[mMockRecips.length - 4], firstStart, firstEnd, 0); 495 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], secondStart, secondEnd, 0); 496 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], thirdStart, thirdEnd, 0); 497 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdNextStart, thirdNextEnd, 0); 498 499 view.createMoreChip(); 500 assertEquals(mEditable.toString(), first+second+third+third); 501 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), firstStart); 502 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), secondStart); 503 // Find the more chip. 504 ImageSpan moreChip = view.getMoreChip(); 505 assertEquals(mEditable.getSpanStart(moreChip), thirdStart); 506 assertEquals(mEditable.getSpanEnd(moreChip), thirdNextEnd + 1); 507 508 view.removeMoreChip(); 509 assertEquals(mEditable.toString(), first+second+third+third); 510 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), firstStart); 511 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 4]), firstEnd); 512 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), secondStart); 513 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), thirdStart); 514 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), thirdEnd); 515 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdNextStart); 516 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdNextEnd); 517 moreChip = view.getMoreChip(); 518 assertEquals(mEditable.getSpanStart(moreChip), -1); 519 } 520 521 public void testRemoveChip() { 522 // Create 3 chips to start and test removing chips in various postions. 523 populateMocks(3); 524 MockRecipientEditTextView view = createViewForTesting(); 525 view.setMoreItem(createTestMoreItem()); 526 String first = (String) mTokenizer.terminateToken("FIRST"); 527 String second = (String) mTokenizer.terminateToken("SECOND"); 528 String third = (String) mTokenizer.terminateToken("THIRD"); 529 mEditable = new SpannableStringBuilder(); 530 mEditable.append(first + second + third); 531 532 int firstStart = mEditable.toString().indexOf(first); 533 int firstEnd = firstStart + first.length(); 534 int secondStart = mEditable.toString().indexOf(second); 535 int secondEnd = secondStart + second.length(); 536 int thirdStart = mEditable.toString().indexOf(third); 537 int thirdEnd = thirdStart + third.length(); 538 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 539 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 540 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 541 assertEquals(mEditable.toString(), first + second + third); 542 // Test removing the middle chip. 543 view.removeChip(mMockRecips[mMockRecips.length - 2]); 544 assertEquals(mEditable.toString(), first + third); 545 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 546 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd); 547 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), -1); 548 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), -1); 549 int newThirdStart = mEditable.toString().indexOf(third); 550 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), newThirdStart); 551 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), newThirdStart 552 + third.length()); 553 554 // Test removing the first chip. 555 populateMocks(3); 556 view = createViewForTesting(); 557 view.setMoreItem(createTestMoreItem()); 558 mEditable = new SpannableStringBuilder(); 559 mEditable.append(first + second + third); 560 561 firstStart = mEditable.toString().indexOf(first); 562 firstEnd = firstStart + first.length(); 563 secondStart = mEditable.toString().indexOf(second); 564 secondEnd = secondStart + second.length(); 565 thirdStart = mEditable.toString().indexOf(third); 566 thirdEnd = thirdStart + third.length(); 567 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 568 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 569 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 570 assertEquals(mEditable.toString(), first + second + third); 571 view.removeChip(mMockRecips[mMockRecips.length - 3]); 572 assertEquals(mEditable.toString(), second + third); 573 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), -1); 574 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), -1); 575 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), 0); 576 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), second.length()); 577 newThirdStart = mEditable.toString().indexOf(third); 578 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), newThirdStart); 579 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), newThirdStart 580 + third.length()); 581 582 // Test removing the last chip. 583 populateMocks(3); 584 view = createViewForTesting(); 585 view.setMoreItem(createTestMoreItem()); 586 mEditable = new SpannableStringBuilder(); 587 mEditable.append(first + second + third); 588 589 firstStart = mEditable.toString().indexOf(first); 590 firstEnd = firstStart + first.length(); 591 secondStart = mEditable.toString().indexOf(second); 592 secondEnd = secondStart + second.length(); 593 thirdStart = mEditable.toString().indexOf(third); 594 thirdEnd = thirdStart + third.length(); 595 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 596 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 597 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 598 assertEquals(mEditable.toString(), first + second + third); 599 view.removeChip(mMockRecips[mMockRecips.length - 1]); 600 assertEquals(mEditable.toString(), first + second); 601 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 602 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd); 603 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 604 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), secondEnd); 605 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), -1); 606 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), -1); 607 } 608 609 public void testReplaceChip() { 610 populateMocks(3); 611 MockRecipientEditTextView view = createViewForTesting(); 612 view.setMoreItem(createTestMoreItem()); 613 view.setChipBackground(createChipBackground()); 614 view.setChipHeight(48); 615 String first = (String) mTokenizer.terminateToken("FIRST"); 616 String second = (String) mTokenizer.terminateToken("SECOND"); 617 String third = (String) mTokenizer.terminateToken("THIRD"); 618 mEditable = new SpannableStringBuilder(); 619 mEditable.append(first + second + third); 620 621 // Test replacing the first chip with a new chip. 622 int firstStart = mEditable.toString().indexOf(first); 623 int firstEnd = firstStart + first.trim().length(); 624 int secondStart = mEditable.toString().indexOf(second); 625 int secondEnd = secondStart + second.trim().length(); 626 int thirdStart = mEditable.toString().indexOf(third); 627 int thirdEnd = thirdStart + third.trim().length(); 628 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 629 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 630 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 631 assertEquals(mEditable.toString(), first + second + third); 632 view.replaceChip(mMockRecips[mMockRecips.length - 3], RecipientEntry 633 .constructGeneratedEntry("replacement", "replacement (at) replacement.com", true)); 634 assertEquals(mEditable.toString(), mTokenizer 635 .terminateToken("replacement <replacement (at) replacement.com>") 636 + second + third); 637 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), -1); 638 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), -1); 639 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), mEditable 640 .toString().indexOf(second)); 641 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), mEditable 642 .toString().indexOf(second) 643 + second.trim().length()); 644 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), mEditable 645 .toString().indexOf(third)); 646 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable 647 .toString().indexOf(third) 648 + third.trim().length()); 649 DrawableRecipientChip[] spans = 650 mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class); 651 assertEquals(spans.length, 3); 652 spans = mEditable 653 .getSpans(0, mEditable.toString().indexOf(second) - 1, DrawableRecipientChip.class); 654 assertEquals((String) spans[0].getDisplay(), "replacement"); 655 656 657 // Test replacing the middle chip with a new chip. 658 mEditable = new SpannableStringBuilder(); 659 mEditable.append(first + second + third); 660 firstStart = mEditable.toString().indexOf(first); 661 firstEnd = firstStart + first.trim().length(); 662 secondStart = mEditable.toString().indexOf(second); 663 secondEnd = secondStart + second.trim().length(); 664 thirdStart = mEditable.toString().indexOf(third); 665 thirdEnd = thirdStart + third.trim().length(); 666 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 667 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 668 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 669 assertEquals(mEditable.toString(), first + second + third); 670 view.replaceChip(mMockRecips[mMockRecips.length - 2], RecipientEntry 671 .constructGeneratedEntry("replacement", "replacement (at) replacement.com", true)); 672 assertEquals(mEditable.toString(), first + mTokenizer 673 .terminateToken("replacement <replacement (at) replacement.com>") + third); 674 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 675 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd); 676 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), -1); 677 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), -1); 678 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), mEditable 679 .toString().indexOf(third)); 680 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable 681 .toString().indexOf(third) 682 + third.trim().length()); 683 spans = mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class); 684 assertEquals(spans.length, 3); 685 spans = mEditable.getSpans(firstEnd, mEditable.toString().indexOf(third) - 1, 686 DrawableRecipientChip.class); 687 assertEquals((String) spans[0].getDisplay(), "replacement"); 688 689 690 // Test replacing the last chip with a new chip. 691 mEditable = new SpannableStringBuilder(); 692 mEditable.append(first + second + third); 693 firstStart = mEditable.toString().indexOf(first); 694 firstEnd = firstStart + first.trim().length(); 695 secondStart = mEditable.toString().indexOf(second); 696 secondEnd = secondStart + second.trim().length(); 697 thirdStart = mEditable.toString().indexOf(third); 698 thirdEnd = thirdStart + third.trim().length(); 699 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 700 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 701 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 702 assertEquals(mEditable.toString(), first + second + third); 703 view.replaceChip(mMockRecips[mMockRecips.length - 1], RecipientEntry 704 .constructGeneratedEntry("replacement", "replacement (at) replacement.com", true)); 705 assertEquals(mEditable.toString(), first + second + mTokenizer 706 .terminateToken("replacement <replacement (at) replacement.com>")); 707 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); 708 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd); 709 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart); 710 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), secondEnd); 711 assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), -1); 712 assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), -1); 713 spans = mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class); 714 assertEquals(spans.length, 3); 715 spans = mEditable 716 .getSpans(secondEnd, mEditable.length(), DrawableRecipientChip.class); 717 assertEquals((String) spans[0].getDisplay(), "replacement"); 718 } 719 720 public void testHandlePaste() { 721 // Start with an empty edit field. 722 // Add an address; the text should be left as is. 723 MockRecipientEditTextView view = createViewForTesting(); 724 view.setMoreItem(createTestMoreItem()); 725 view.setChipBackground(createChipBackground()); 726 view.setChipHeight(48); 727 mEditable = new SpannableStringBuilder(); 728 mEditable.append("user (at) user.com"); 729 view.setSelection(mEditable.length()); 730 view.handlePaste(); 731 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 0); 732 assertEquals(mEditable.toString(), "user (at) user.com"); 733 734 // Test adding a single address to an empty chips field with a space at 735 // the end of it. The address should stay as text. 736 mEditable = new SpannableStringBuilder(); 737 String tokenizedUser = "user (at) user.com" + " "; 738 mEditable.append(tokenizedUser); 739 view.setSelection(mEditable.length()); 740 view.handlePaste(); 741 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 0); 742 assertEquals(mEditable.toString(), tokenizedUser); 743 744 // Test adding a single address to an empty chips field with a semicolon at 745 // the end of it. The address should become a chip 746 mEditable = new SpannableStringBuilder(); 747 tokenizedUser = "user (at) user.com;"; 748 mEditable.append(tokenizedUser); 749 view.setSelection(mEditable.length()); 750 view.handlePaste(); 751 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 1); 752 753 // Test adding 2 address to an empty chips field. The second to last 754 // address should become a chip and the last address should stay as 755 // text. 756 mEditable = new SpannableStringBuilder(); 757 mEditable.append("user1,user2 (at) user.com"); 758 view.setSelection(mEditable.length()); 759 view.handlePaste(); 760 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 1); 761 assertEquals(mEditable.getSpans(0, mEditable.toString().indexOf("user2 (at) user.com"), 762 DrawableRecipientChip.class).length, 1); 763 assertEquals(mEditable.toString(), "<user1>, user2 (at) user.com"); 764 765 // Test adding a single address to the end of existing chips. The existing 766 // chips should remain, and the last address should stay as text. 767 populateMocks(3); 768 String first = (String) mTokenizer.terminateToken("FIRST"); 769 String second = (String) mTokenizer.terminateToken("SECOND"); 770 String third = (String) mTokenizer.terminateToken("THIRD"); 771 mEditable = new SpannableStringBuilder(); 772 mEditable.append(first + second + third); 773 view.setSelection(mEditable.length()); 774 int firstStart = mEditable.toString().indexOf(first); 775 int firstEnd = firstStart + first.trim().length(); 776 int secondStart = mEditable.toString().indexOf(second); 777 int secondEnd = secondStart + second.trim().length(); 778 int thirdStart = mEditable.toString().indexOf(third); 779 int thirdEnd = thirdStart + third.trim().length(); 780 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 781 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 782 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 783 784 mEditable.append("user (at) user.com"); 785 view.setSelection(mEditable.length()); 786 view.handlePaste(); 787 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 788 mMockRecips.length); 789 assertEquals(mEditable.toString(), first + second + third + "user (at) user.com"); 790 791 // Paste 2 addresses after existing chips. We expect the first address to be turned into 792 // a chip and the second to be left as text. 793 populateMocks(3); 794 mEditable = new SpannableStringBuilder(); 795 mEditable.append(first + second + third); 796 797 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 798 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 799 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 800 801 mEditable.append("user1, user2 (at) user.com"); 802 view.setSelection(mEditable.length()); 803 view.handlePaste(); 804 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 805 mMockRecips.length + 1); 806 assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable 807 .toString().indexOf("user2 (at) user.com") - 1, DrawableRecipientChip.class).length, 1); 808 assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2 (at) user.com"), mEditable 809 .length(), DrawableRecipientChip.class).length, 0); 810 assertEquals(mEditable.toString(), first + second + third + "<user1>, user2 (at) user.com"); 811 812 // Paste 2 addresses after existing chips. We expect the first address to be turned into 813 // a chip and the second to be left as text. This removes the space seperator char between 814 // addresses. 815 populateMocks(3); 816 mEditable = new SpannableStringBuilder(); 817 mEditable.append(first + second + third); 818 819 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 820 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 821 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 822 823 mEditable.append("user1,user2 (at) user.com"); 824 view.setSelection(mEditable.length()); 825 view.handlePaste(); 826 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 827 mMockRecips.length + 1); 828 assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable 829 .toString().indexOf("user2 (at) user.com") - 1, DrawableRecipientChip.class).length, 1); 830 assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2 (at) user.com"), mEditable 831 .length(), DrawableRecipientChip.class).length, 0); 832 assertEquals(mEditable.toString(), first + second + third + "<user1>, user2 (at) user.com"); 833 834 // Test a complete token pasted in at the end. It should be turned into a chip. 835 mEditable = new SpannableStringBuilder(); 836 mEditable.append("user1, user2 (at) user.com,"); 837 view.setSelection(mEditable.length()); 838 view.handlePaste(); 839 assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 2); 840 assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable 841 .toString().indexOf("user2 (at) user.com") - 1, DrawableRecipientChip.class).length, 1); 842 assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2 (at) user.com"), mEditable 843 .length(), DrawableRecipientChip.class).length, 1); 844 assertEquals(mEditable.toString(), "<user1>, <user2 (at) user.com>, "); 845 } 846 847 public void testGetPastTerminators() { 848 MockRecipientEditTextView view = createViewForTesting(); 849 view.setMoreItem(createTestMoreItem()); 850 view.setChipBackground(createChipBackground()); 851 view.setChipHeight(48); 852 String test = "test"; 853 mEditable = new SpannableStringBuilder(); 854 mEditable.append(test); 855 assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)), 856 test.length()); 857 858 test = "test,"; 859 mEditable = new SpannableStringBuilder(); 860 mEditable.append(test); 861 assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)), 862 test.length()); 863 864 test = "test, "; 865 mEditable = new SpannableStringBuilder(); 866 mEditable.append(test); 867 assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)), 868 test.length()); 869 870 test = "test;"; 871 mEditable = new SpannableStringBuilder(); 872 mEditable.append(test); 873 assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)), 874 test.length()); 875 876 test = "test; "; 877 mEditable = new SpannableStringBuilder(); 878 mEditable.append(test); 879 assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)), 880 test.length()); 881 } 882 883 public void testIsCompletedToken() { 884 MockRecipientEditTextView view = createViewForTesting(); 885 view.setMoreItem(createTestMoreItem()); 886 view.setChipBackground(createChipBackground()); 887 view.setChipHeight(48); 888 assertTrue(view.isCompletedToken("test;")); 889 assertTrue(view.isCompletedToken("test,")); 890 assertFalse(view.isCompletedToken("test")); 891 assertFalse(view.isCompletedToken("test ")); 892 } 893 894 public void testGetLastChip() { 895 populateMocks(3); 896 MockRecipientEditTextView view = createViewForTesting(); 897 view.setMoreItem(createTestMoreItem()); 898 view.setChipBackground(createChipBackground()); 899 view.setChipHeight(48); 900 String first = (String) mTokenizer.terminateToken("FIRST"); 901 String second = (String) mTokenizer.terminateToken("SECOND"); 902 String third = (String) mTokenizer.terminateToken("THIRD"); 903 mEditable = new SpannableStringBuilder(); 904 mEditable.append(first + second + third); 905 906 // Test replacing the first chip with a new chip. 907 int firstStart = mEditable.toString().indexOf(first); 908 int firstEnd = firstStart + first.trim().length(); 909 int secondStart = mEditable.toString().indexOf(second); 910 int secondEnd = secondStart + second.trim().length(); 911 int thirdStart = mEditable.toString().indexOf(third); 912 int thirdEnd = thirdStart + third.trim().length(); 913 mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0); 914 mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0); 915 mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); 916 assertEquals(view.getLastChip(), mMockRecips[mMockRecips.length - 1]); 917 mEditable.append("extra"); 918 assertEquals(view.getLastChip(), mMockRecips[mMockRecips.length - 1]); 919 } 920 921 private Drawable createChipBackground() { 922 Bitmap drawable = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 923 return new BitmapDrawable(getContext().getResources(), drawable); 924 } 925 926 private TextView createTestMoreItem() { 927 TextView view = new TextView(getContext()); 928 view.setText("<xliff:g id='count'>%1$s</xliff:g> more..."); 929 return view; 930 } 931 932 private void populateMocks(int size) { 933 mMockEntries = new RecipientEntry[size]; 934 for (int i = 0; i < size; i++) { 935 mMockEntries[i] = RecipientEntry.constructGeneratedEntry("user", 936 "user (at) username.com", true); 937 } 938 mMockRecips = new DrawableRecipientChip[size]; 939 for (int i = 0; i < size; i++) { 940 mMockRecips[i] = new VisibleRecipientChip(null, mMockEntries[i]); 941 } 942 } 943 944 /** 945 * <p> 946 * Ensure the original text is always accurate, regardless of the type of email. The original 947 * text is used to determine where to display the chip span. If this test fails, it means some 948 * text that should be turned into one whole chip may behave unexpectedly. 949 * </p> 950 * <p> 951 * For example, a bug was seen where 952 * 953 * <pre> 954 * "Android User" <android (at) example.com> 955 * </pre> 956 * 957 * was converted to 958 * 959 * <pre> 960 * Android User [android (at) example.com] 961 * </pre> 962 * 963 * where text inside [] is a chip. 964 * </p> 965 */ 966 public void testCreateReplacementChipOriginalText() { 967 // Name in quotes + email address 968 testCreateReplacementChipOriginalText("\"Android User\" <android (at) example.com>,"); 969 // Name in quotes + email address without brackets 970 testCreateReplacementChipOriginalText("\"Android User\" android (at) example.com,"); 971 // Name in quotes 972 testCreateReplacementChipOriginalText("\"Android User\","); 973 // Name without quotes + email address 974 testCreateReplacementChipOriginalText("Android User <android (at) example.com>,"); 975 // Name without quotes 976 testCreateReplacementChipOriginalText("Android User,"); 977 // Email address 978 testCreateReplacementChipOriginalText("<android (at) example.com>,"); 979 // Email address without brackets 980 testCreateReplacementChipOriginalText("android (at) example.com,"); 981 } 982 983 private void testCreateReplacementChipOriginalText(final String email) { 984 // No trailing space 985 attemptCreateReplacementChipOriginalText(email.trim()); 986 // Trailing space 987 attemptCreateReplacementChipOriginalText(email.trim() + " "); 988 } 989 990 private void attemptCreateReplacementChipOriginalText(final String email) { 991 final RecipientEditTextView view = new RecipientEditTextView(getContext(), null); 992 993 view.setText(email); 994 view.mPendingChips.add(email); 995 996 view.createReplacementChip(0, email.length(), view.getText(), true); 997 // The "original text" should be the email without the comma or space(s) 998 assertEquals(email.replaceAll(",\\s*$", ""), 999 view.mTemporaryRecipients.get(0).getOriginalText().toString().trim()); 1000 } 1001 1002 public void testCreateTokenizedEntryForPhone() { 1003 final String phonePattern = "[^\\d]*888[^\\d]*555[^\\d]*1234[^\\d]*"; 1004 final String phone1 = "8885551234"; 1005 final String phone2 = "888-555-1234"; 1006 final String phone3 = "(888) 555-1234"; 1007 1008 final RecipientEditTextView view = new RecipientEditTextView(getContext(), null); 1009 final BaseRecipientAdapter adapter = new TestBaseRecipientAdapter(getContext(), 10, 1010 BaseRecipientAdapter.QUERY_TYPE_PHONE); 1011 view.setAdapter(adapter); 1012 1013 final RecipientEntry entry1 = view.createTokenizedEntry(phone1); 1014 final String destination1 = entry1.getDestination(); 1015 assertTrue(phone1 + " failed with " + destination1, 1016 Pattern.matches(phonePattern, destination1)); 1017 1018 final RecipientEntry entry2 = view.createTokenizedEntry(phone2); 1019 final String destination2 = entry2.getDestination(); 1020 assertTrue(phone2 + " failed with " + destination2, 1021 Pattern.matches(phonePattern, destination2)); 1022 1023 final RecipientEntry entry3 = view.createTokenizedEntry(phone3); 1024 final String destination3 = entry3.getDestination(); 1025 assertTrue(phone3 + " failed with " + destination3, 1026 Pattern.matches(phonePattern, destination3)); 1027 } 1028 } 1029