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