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