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