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