ChipsTest.java revision 878b7cea14b1f8583611a7df58a4406b3eca34b6
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        public Drawable getChipBackground(RecipientEntry contact) {
73            return createChipBackground();
74        }
75    }
76
77    private MockRecipientEditTextView createViewForTesting() {
78        mEditable = new SpannableStringBuilder();
79        MockRecipientEditTextView view = new MockRecipientEditTextView(getContext());
80        return view;
81    }
82
83    public void testCreateDisplayText() {
84        RecipientEditTextView view = createViewForTesting();
85        RecipientEntry entry = RecipientEntry.constructGeneratedEntry("User Name, Jr",
86                "user@username.com");
87        String test = view.createDisplayText(entry);
88        assertEquals("Expected a properly formatted RFC email address",
89                "\"User Name, Jr\" <user@username.com>, ", test);
90
91        RecipientEntry alreadyFormatted = RecipientEntry.constructFakeEntry("user@username.com, ");
92        test = view.createDisplayText(alreadyFormatted);
93        assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ",
94                test);
95
96        RecipientEntry alreadyFormattedNoSpace = RecipientEntry
97                .constructFakeEntry("user@username.com,");
98        test = view.createDisplayText(alreadyFormattedNoSpace);
99        assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ",
100                test);
101
102        RecipientEntry alreadyNamed = RecipientEntry.constructGeneratedEntry("User Name",
103                "\"User Name, Jr\" <user@username.com>");
104        test = view.createDisplayText(alreadyNamed);
105        assertEquals(
106                "Expected address that used the name not the excess address name",
107                "User Name <user@username.com>, ", test);
108    }
109
110    public void testSanitizeBetween() {
111        // First, add 2 chips and then make sure we remove
112        // the extra content between them correctly.
113        populateMocks(2);
114        MockRecipientEditTextView view = createViewForTesting();
115        String first = (String) mTokenizer.terminateToken("FIRST");
116        String second = (String) mTokenizer.terminateToken("SECOND");
117        String extra = "EXTRA";
118        mEditable = new SpannableStringBuilder();
119        mEditable.append(first + extra + second);
120        int firstStart = mEditable.toString().indexOf(first);
121        int firstEnd = firstStart + first.length();
122        int secondStart = mEditable.toString().indexOf(second);
123        int secondEnd = secondStart + second.length();
124        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], firstStart, firstEnd, 0);
125        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], secondStart, secondEnd, 0);
126        view.sanitizeBetween();
127        String editableString = mEditable.toString();
128        assertEquals(editableString.indexOf(extra), -1);
129        assertEquals(editableString.indexOf(first), firstStart);
130        assertEquals(editableString.indexOf(second), secondStart - extra.length());
131        assertEquals(editableString, (first + second));
132
133        // Add 1 chip and make sure that we remove the extra stuff before it correctly.
134        mEditable = new SpannableStringBuilder();
135        populateMocks(1);
136        mEditable.append(extra);
137        mEditable.append(first);
138        firstStart = mEditable.toString().indexOf(first);
139        firstEnd = firstStart + first.length();
140        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], firstStart, firstEnd, 0);
141        view.sanitizeBetween();
142        assertEquals(mEditable.toString(), first);
143        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), firstStart
144                - extra.length());
145    }
146
147    public void testMoreChip() {
148        // Add 3 chips: this is the trigger point at which the more chip will be created.
149        // Test that adding the chips and then creating and removing the more chip, as if
150        // the user were focusing/ removing focus from the chips field.
151        populateMocks(3);
152        MockRecipientEditTextView view = createViewForTesting();
153        view.setMoreItem(createTestMoreItem());
154        String first = (String) mTokenizer.terminateToken("FIRST");
155        String second = (String) mTokenizer.terminateToken("SECOND");
156        String third = (String) mTokenizer.terminateToken("THIRD");
157        mEditable = new SpannableStringBuilder();
158        mEditable.append(first+second+third);
159
160        int firstStart = mEditable.toString().indexOf(first);
161        int firstEnd = firstStart + first.length();
162        int secondStart = mEditable.toString().indexOf(second);
163        int secondEnd = secondStart + second.length();
164        int thirdStart = mEditable.toString().indexOf(third);
165        int thirdEnd = thirdStart + third.length();
166        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
167        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
168        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
169
170        view.createMoreChip();
171        assertEquals(mEditable.toString(), first+second+third);
172        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
173        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
174        // Find the more chip.
175        ImageSpan moreChip = view.getMoreChip();
176        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
177        assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd);
178
179        view.removeMoreChip();
180        assertEquals(mEditable.toString(), first+second+third);
181        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
182        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
183        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
184        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart);
185        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd);
186        moreChip = view.getMoreChip();
187        assertEquals(mEditable.getSpanStart(moreChip), -1);
188
189        // Rinse and repeat, just in case!
190        view.createMoreChip();
191        assertEquals(mEditable.toString(), first+second+third);
192        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
193        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
194        // Find the more chip.
195        moreChip = view.getMoreChip();
196        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
197        assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd);
198
199        view.removeMoreChip();
200        assertEquals(mEditable.toString(), first+second+third);
201        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
202        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
203        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
204        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart);
205        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd);
206        moreChip = view.getMoreChip();
207        assertEquals(mEditable.getSpanStart(moreChip), -1);
208    }
209
210    public void testMoreChipLotsOfUsers() {
211        // Test adding and removing the more chip in the case where we have a lot of users.
212        populateMocks(10);
213        MockRecipientEditTextView view = createViewForTesting();
214        view.setMoreItem(createTestMoreItem());
215        String first = (String) mTokenizer.terminateToken("FIRST");
216        String second = (String) mTokenizer.terminateToken("SECOND");
217        String third = (String) mTokenizer.terminateToken("THIRD");
218        String fourth = (String) mTokenizer.terminateToken("FOURTH");
219        String fifth = (String) mTokenizer.terminateToken("FIFTH");
220        String sixth = (String) mTokenizer.terminateToken("SIXTH");
221        String seventh = (String) mTokenizer.terminateToken("SEVENTH");
222        String eigth = (String) mTokenizer.terminateToken("EIGHTH");
223        String ninth = (String) mTokenizer.terminateToken("NINTH");
224        String tenth = (String) mTokenizer.terminateToken("TENTH");
225        mEditable = new SpannableStringBuilder();
226        mEditable.append(first+second+third+fourth+fifth+sixth+seventh+eigth+ninth+tenth);
227
228        int firstStart = mEditable.toString().indexOf(first);
229        int firstEnd = firstStart + first.length();
230        int secondStart = mEditable.toString().indexOf(second);
231        int secondEnd = secondStart + second.length();
232        int thirdStart = mEditable.toString().indexOf(third);
233        int thirdEnd = thirdStart + third.length();
234        int fourthStart = mEditable.toString().indexOf(fourth);
235        int fourthEnd = fourthStart + fourth.length();
236        int fifthStart = mEditable.toString().indexOf(fifth);
237        int fifthEnd = fifthStart + fifth.length();
238        int sixthStart = mEditable.toString().indexOf(sixth);
239        int sixthEnd = sixthStart + sixth.length();
240        int seventhStart = mEditable.toString().indexOf(seventh);
241        int seventhEnd = seventhStart + seventh.length();
242        int eighthStart = mEditable.toString().indexOf(eigth);
243        int eighthEnd = eighthStart + eigth.length();
244        int ninthStart = mEditable.toString().indexOf(ninth);
245        int ninthEnd = ninthStart + ninth.length();
246        int tenthStart = mEditable.toString().indexOf(tenth);
247        int tenthEnd = tenthStart + tenth.length();
248        mEditable.setSpan(mMockRecips[mMockRecips.length - 10], firstStart, firstEnd, 0);
249        mEditable.setSpan(mMockRecips[mMockRecips.length - 9], secondStart, secondEnd, 0);
250        mEditable.setSpan(mMockRecips[mMockRecips.length - 8], thirdStart, thirdEnd, 0);
251        mEditable.setSpan(mMockRecips[mMockRecips.length - 7], fourthStart, fourthEnd, 0);
252        mEditable.setSpan(mMockRecips[mMockRecips.length - 6], fifthStart, fifthEnd, 0);
253        mEditable.setSpan(mMockRecips[mMockRecips.length - 5], sixthStart, sixthEnd, 0);
254        mEditable.setSpan(mMockRecips[mMockRecips.length - 4], seventhStart, seventhEnd, 0);
255        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], eighthStart, eighthEnd, 0);
256        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], ninthStart, ninthEnd, 0);
257        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], tenthStart, tenthEnd, 0);
258
259        view.createMoreChip();
260        assertEquals(mEditable.toString(), first + second + third + fourth + fifth + sixth
261                + seventh + eigth + ninth + tenth);
262        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 10]), firstStart);
263        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 9]), secondStart);
264        // Find the more chip.
265        ImageSpan moreChip = view.getMoreChip();
266        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
267        assertEquals(mEditable.getSpanEnd(moreChip), tenthEnd);
268
269        view.removeMoreChip();
270        assertEquals(mEditable.toString(), first + second + third + fourth + fifth + sixth
271                + seventh + eigth + ninth + tenth);
272        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 10]), firstStart);
273        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 9]), secondStart);
274
275        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 8]), thirdStart);
276        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 7]), fourthStart);
277        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 6]), fifthStart);
278        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 5]), sixthStart);
279        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), seventhStart);
280        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), eighthStart);
281        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), ninthStart);
282        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), tenthStart);
283        moreChip = view.getMoreChip();
284        assertEquals(mEditable.getSpanStart(moreChip), -1);
285
286    }
287
288    public void testMoreChipSpecialChars() {
289        // Make sure the more chip correctly handles extra tokenizer characters in the middle
290        // of chip text.
291        populateMocks(3);
292        MockRecipientEditTextView view = createViewForTesting();
293        view.setMoreItem(createTestMoreItem());
294        String first = (String) mTokenizer.terminateToken("FI,RST");
295        String second = (String) mTokenizer.terminateToken("SE,COND");
296        String third = (String) mTokenizer.terminateToken("THI,RD");
297        mEditable = new SpannableStringBuilder();
298        mEditable.append(first+second+third);
299
300        int firstStart = mEditable.toString().indexOf(first);
301        int firstEnd = firstStart + first.length();
302        int secondStart = mEditable.toString().indexOf(second);
303        int secondEnd = secondStart + second.length();
304        int thirdStart = mEditable.toString().indexOf(third);
305        int thirdEnd = thirdStart + third.length();
306        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
307        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
308        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
309
310        view.createMoreChip();
311        assertEquals(mEditable.toString(), first+second+third);
312        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
313        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
314        // Find the more chip.
315        ImageSpan moreChip = view.getMoreChip();
316        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
317        assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd);
318
319        view.removeMoreChip();
320        assertEquals(mEditable.toString(), first+second+third);
321        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
322        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
323        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
324        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart);
325        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd);
326        moreChip = view.getMoreChip();
327        assertEquals(mEditable.getSpanStart(moreChip), -1);
328    }
329
330    public void testMoreChipDupes() {
331        // Make sure the more chip is correctly added and removed when we have duplicate chips.
332        populateMocks(4);
333        MockRecipientEditTextView view = createViewForTesting();
334        view.setMoreItem(createTestMoreItem());
335        String first = (String) mTokenizer.terminateToken("FIRST");
336        String second = (String) mTokenizer.terminateToken("SECOND");
337        String third = (String) mTokenizer.terminateToken("THIRD");
338        mEditable = new SpannableStringBuilder();
339        mEditable.append(first+second+third+third);
340
341        int firstStart = mEditable.toString().indexOf(first);
342        int firstEnd = firstStart + first.length();
343        int secondStart = mEditable.toString().indexOf(second);
344        int secondEnd = secondStart + second.length();
345        int thirdStart = mEditable.toString().indexOf(third);
346        int thirdEnd = thirdStart + third.length();
347        int thirdNextStart = mEditable.toString().indexOf(third, thirdEnd);
348        int thirdNextEnd = thirdNextStart + third.length();
349        mEditable.setSpan(mMockRecips[mMockRecips.length - 4], firstStart, firstEnd, 0);
350        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], secondStart, secondEnd, 0);
351        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], thirdStart, thirdEnd, 0);
352        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdNextStart, thirdNextEnd, 0);
353
354        view.createMoreChip();
355        assertEquals(mEditable.toString(), first+second+third+third);
356        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), firstStart);
357        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), secondStart);
358        // Find the more chip.
359        ImageSpan moreChip = view.getMoreChip();
360        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
361        assertEquals(mEditable.getSpanEnd(moreChip), thirdNextEnd);
362
363        view.removeMoreChip();
364        assertEquals(mEditable.toString(), first+second+third+third);
365        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), firstStart);
366        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 4]), firstEnd);
367        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), secondStart);
368        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), thirdStart);
369        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), thirdEnd);
370        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdNextStart);
371        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdNextEnd);
372        moreChip = view.getMoreChip();
373        assertEquals(mEditable.getSpanStart(moreChip), -1);
374    }
375
376    public void testMatchesChip() {
377        // Test the logic for checking if we have found the chip
378        // that matches a particular offset in chips field.
379        populateMocks(3);
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);
387
388        int firstStart = mEditable.toString().indexOf(first);
389        int firstEnd = firstStart + first.length();
390        int secondStart = mEditable.toString().indexOf(second);
391        int secondEnd = secondStart + second.length();
392        int thirdStart = mEditable.toString().indexOf(third);
393        int thirdEnd = thirdStart + third.length();
394        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
395        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
396        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
397        assertFalse(view.matchesChip(mMockRecips[mMockRecips.length - 3], -1));
398        assertFalse(view.matchesChip(mMockRecips[mMockRecips.length - 1], mEditable.length() + 1));
399        assertTrue(view.matchesChip(mMockRecips[mMockRecips.length - 3], firstStart));
400        assertTrue(view.matchesChip(mMockRecips[mMockRecips.length - 3], firstEnd));
401        assertTrue(view.matchesChip(mMockRecips[mMockRecips.length - 3], firstEnd - 1));
402    }
403
404    public void testRemoveChip() {
405        // Create 3 chips to start and test removing chips in various postions.
406        populateMocks(3);
407        MockRecipientEditTextView view = createViewForTesting();
408        view.setMoreItem(createTestMoreItem());
409        String first = (String) mTokenizer.terminateToken("FIRST");
410        String second = (String) mTokenizer.terminateToken("SECOND");
411        String third = (String) mTokenizer.terminateToken("THIRD");
412        mEditable = new SpannableStringBuilder();
413        mEditable.append(first + second + third);
414
415        int firstStart = mEditable.toString().indexOf(first);
416        int firstEnd = firstStart + first.length();
417        int secondStart = mEditable.toString().indexOf(second);
418        int secondEnd = secondStart + second.length();
419        int thirdStart = mEditable.toString().indexOf(third);
420        int thirdEnd = thirdStart + third.length();
421        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
422        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
423        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
424        assertEquals(mEditable.toString(), first + second + third);
425        // Test removing the middle chip.
426        view.removeChip(mMockRecips[mMockRecips.length - 2]);
427        assertEquals(mEditable.toString(), first + third);
428        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
429        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
430        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), -1);
431        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), -1);
432        int newThirdStart = mEditable.toString().indexOf(third);
433        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), newThirdStart);
434        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), newThirdStart
435                + third.length());
436
437        // Test removing the first chip.
438        populateMocks(3);
439        view = createViewForTesting();
440        view.setMoreItem(createTestMoreItem());
441        mEditable = new SpannableStringBuilder();
442        mEditable.append(first + second + third);
443
444        firstStart = mEditable.toString().indexOf(first);
445        firstEnd = firstStart + first.length();
446        secondStart = mEditable.toString().indexOf(second);
447        secondEnd = secondStart + second.length();
448        thirdStart = mEditable.toString().indexOf(third);
449        thirdEnd = thirdStart + third.length();
450        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
451        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
452        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
453        assertEquals(mEditable.toString(), first + second + third);
454        view.removeChip(mMockRecips[mMockRecips.length - 3]);
455        assertEquals(mEditable.toString(), second + third);
456        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), -1);
457        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), -1);
458        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), 0);
459        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), second.length());
460        newThirdStart = mEditable.toString().indexOf(third);
461        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), newThirdStart);
462        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), newThirdStart
463                + third.length());
464
465        // Test removing the last chip.
466        populateMocks(3);
467        view = createViewForTesting();
468        view.setMoreItem(createTestMoreItem());
469        mEditable = new SpannableStringBuilder();
470        mEditable.append(first + second + third);
471
472        firstStart = mEditable.toString().indexOf(first);
473        firstEnd = firstStart + first.length();
474        secondStart = mEditable.toString().indexOf(second);
475        secondEnd = secondStart + second.length();
476        thirdStart = mEditable.toString().indexOf(third);
477        thirdEnd = thirdStart + third.length();
478        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
479        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
480        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
481        assertEquals(mEditable.toString(), first + second + third);
482        view.removeChip(mMockRecips[mMockRecips.length - 1]);
483        assertEquals(mEditable.toString(), first + second);
484        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
485        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
486        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
487        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), secondEnd);
488        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), -1);
489        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), -1);
490    }
491
492    public void testReplaceChip() {
493        populateMocks(3);
494        MockRecipientEditTextView view = createViewForTesting();
495        view.setMoreItem(createTestMoreItem());
496        view.setChipBackground(createChipBackground());
497        view.setChipHeight(48);
498        String first = (String) mTokenizer.terminateToken("FIRST");
499        String second = (String) mTokenizer.terminateToken("SECOND");
500        String third = (String) mTokenizer.terminateToken("THIRD");
501        mEditable = new SpannableStringBuilder();
502        mEditable.append(first + second + third);
503
504        // Test replacing the first chip with a new chip.
505        int firstStart = mEditable.toString().indexOf(first);
506        int firstEnd = firstStart + first.length();
507        int secondStart = mEditable.toString().indexOf(second);
508        int secondEnd = secondStart + second.length();
509        int thirdStart = mEditable.toString().indexOf(third);
510        int thirdEnd = thirdStart + third.length();
511        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
512        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
513        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
514        assertEquals(mEditable.toString(), first + second + third);
515        view.replaceChip(mMockRecips[mMockRecips.length - 3], RecipientEntry
516                .constructGeneratedEntry("replacement", "replacement@replacement.com"));
517        assertEquals(mEditable.toString(), mTokenizer
518                .terminateToken("replacement <replacement@replacement.com>")
519                + second + third);
520        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), -1);
521        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), -1);
522        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), mEditable
523                .toString().indexOf(second));
524        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), mEditable
525                .toString().indexOf(second)
526                + second.length());
527        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), mEditable
528                .toString().indexOf(third));
529        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable
530                .toString().indexOf(third)
531                + third.length());
532        RecipientChip[] spans = mEditable.getSpans(0, mEditable.length(), RecipientChip.class);
533        assertEquals(spans.length, 3);
534        spans = mEditable
535                .getSpans(0, mEditable.toString().indexOf(second) - 1, RecipientChip.class);
536        assertEquals((String) spans[0].getDisplay(), "replacement");
537
538
539        // Test replacing the middle chip with a new chip.
540        mEditable = new SpannableStringBuilder();
541        mEditable.append(first + second + third);
542        firstStart = mEditable.toString().indexOf(first);
543        firstEnd = firstStart + first.length();
544        secondStart = mEditable.toString().indexOf(second);
545        secondEnd = secondStart + second.length();
546        thirdStart = mEditable.toString().indexOf(third);
547        thirdEnd = thirdStart + third.length();
548        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
549        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
550        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
551        assertEquals(mEditable.toString(), first + second + third);
552        view.replaceChip(mMockRecips[mMockRecips.length - 2], RecipientEntry
553                .constructGeneratedEntry("replacement", "replacement@replacement.com"));
554        assertEquals(mEditable.toString(), mTokenizer
555                .terminateToken("replacement <replacement@replacement.com>")
556                + second + third);
557        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
558        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
559        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), -1);
560        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), -1);
561        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), mEditable
562                .toString().indexOf(third));
563        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable
564                .toString().indexOf(third)
565                + third.length());
566        spans = mEditable.getSpans(0, mEditable.length(), RecipientChip.class);
567        assertEquals(spans.length, 3);
568        spans = mEditable
569                .getSpans(firstEnd, mEditable.toString().indexOf(third) - 1, RecipientChip.class);
570        assertEquals((String) spans[0].getDisplay(), "replacement");
571
572
573        // Test replacing the last chip with a new chip.
574        mEditable = new SpannableStringBuilder();
575        mEditable.append(first + second + third);
576        firstStart = mEditable.toString().indexOf(first);
577        firstEnd = firstStart + first.length();
578        secondStart = mEditable.toString().indexOf(second);
579        secondEnd = secondStart + second.length();
580        thirdStart = mEditable.toString().indexOf(third);
581        thirdEnd = thirdStart + third.length();
582        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
583        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
584        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
585        assertEquals(mEditable.toString(), first + second + third);
586        view.replaceChip(mMockRecips[mMockRecips.length - 1], RecipientEntry
587                .constructGeneratedEntry("replacement", "replacement@replacement.com"));
588        assertEquals(mEditable.toString(), mTokenizer
589                .terminateToken("replacement <replacement@replacement.com>")
590                + second + third);
591        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
592        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
593        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
594        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), secondEnd);
595        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), -1);
596        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), -1);
597        spans = mEditable.getSpans(0, mEditable.length(), RecipientChip.class);
598        assertEquals(spans.length, 3);
599        spans = mEditable
600                .getSpans(secondEnd, mEditable.length(), RecipientChip.class);
601        assertEquals((String) spans[0].getDisplay(), "replacement");
602    }
603
604    private Drawable createChipBackground() {
605        Bitmap drawable = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
606        return new BitmapDrawable(getContext().getResources(), drawable);
607    }
608
609    private TextView createTestMoreItem() {
610        TextView view = new TextView(getContext());
611        view.setText("<xliff:g id='count'>%1$s</xliff:g> more...");
612        return view;
613    }
614
615    private void populateMocks(int size) {
616        mMockEntries = new RecipientEntry[size];
617        for (int i = 0; i < size; i++) {
618            mMockEntries[i] = RecipientEntry.constructGeneratedEntry("user",
619                    "user@username.com");
620        }
621        mMockRecips = new RecipientChip[size];
622        for (int i = 0; i < size; i++) {
623            mMockRecips[i] = new RecipientChip(null, mMockEntries[i], i);
624        }
625    }
626}
627