General cleanup of the chips view.

1) remove extra code
2) make sure we use convenience methods where possible
3) always make sure that we use sorted recipient chips when looking
for the position of a chip
4) test the convenience methods
5) rempove unused code
6) make methods package private that don't have to be public
Change-Id: I586eac795a548456ec29bd384abd2e7611a24a03
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d00e36a..c837b28 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -16,7 +16,7 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <style name="RecipientEditTextView" parent="@android:attr/autoCompleteTextViewStyle">
         <item name="android:inputType">textEmailAddress|textMultiLine</item>
-        <item name="android:imeOptions">actionNext|flagNoExtractUi</item>
+        <item name="android:imeOptions">actionNext|flagNoFullscreen</item>
         <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
         <item name="android:background">@null</item>
         <item name="android:layout_height">wrap_content</item>
diff --git a/src/com/android/ex/chips/RecipientEditTextView.java b/src/com/android/ex/chips/RecipientEditTextView.java
index 9ca2c38..6ae6efa 100644
--- a/src/com/android/ex/chips/RecipientEditTextView.java
+++ b/src/com/android/ex/chips/RecipientEditTextView.java
@@ -64,7 +64,6 @@
 import android.view.ViewParent;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Filterable;
 import android.widget.ListAdapter;
 import android.widget.ListPopupWindow;
 import android.widget.ListView;
@@ -91,20 +90,41 @@
         GestureDetector.OnGestureListener, OnDismissListener, OnClickListener,
         PopupWindow.OnDismissListener {
 
+    private static final char COMMIT_CHAR_COMMA = ',';
+
+    private static final char COMMIT_CHAR_SEMICOLON = ';';
+
+    private static final char COMMIT_CHAR_SPACE = ' ';
+
     private static final String TAG = "RecipientEditTextView";
 
+    private static int DISMISS = "dismiss".hashCode();
+
+    private static final long DISMISS_DELAY = 300;
+
     // TODO: get correct number/ algorithm from with UX.
     private static final int CHIP_LIMIT = 2;
 
+    private static int sSelectedTextColor = -1;
+
+    // Resources for displaying chips.
     private Drawable mChipBackground = null;
 
     private Drawable mChipDelete = null;
 
+    private Drawable mInvalidChipBackground;
+
+    private Drawable mChipBackgroundPressed;
+
+    private float mChipHeight;
+
+    private float mChipFontSize;
+
     private int mChipPadding;
 
     private Tokenizer mTokenizer;
 
-    private Drawable mChipBackgroundPressed;
+    private Validator mValidator;
 
     private RecipientChip mSelectedChip;
 
@@ -118,30 +138,10 @@
 
     private final ArrayList<String> mPendingChips = new ArrayList<String>();
 
-    private float mChipHeight;
-
-    private float mChipFontSize;
-
-    private Validator mValidator;
-
-    private Drawable mInvalidChipBackground;
-
     private Handler mHandler;
 
-    private static int DISMISS = "dismiss".hashCode();
-
-    private static final long DISMISS_DELAY = 300;
-
     private int mPendingChipsCount = 0;
 
-    private static int sSelectedTextColor = -1;
-
-    private static final char COMMIT_CHAR_COMMA = ',';
-
-    private static final char COMMIT_CHAR_SEMICOLON = ';';
-
-    private static final char COMMIT_CHAR_SPACE = ' ';
-
     private ListPopupWindow mAlternatesPopup;
 
     private ListPopupWindow mAddressPopup;
@@ -168,11 +168,14 @@
     private OnItemClickListener mAlternatesListener;
 
     private int mCheckedItem;
+
     private TextWatcher mTextWatcher;
 
+    // Obtain the enclosing scroll view, if it exists, so that the view can be
+    // scrolled to show the last line of chips content.
     private ScrollView mScrollView;
 
-    private boolean mTried;
+    private boolean mTriedGettingScrollView;
 
     private final Runnable mAddTextWatcher = new Runnable() {
         @Override
@@ -246,26 +249,23 @@
         mGestureDetector = new GestureDetector(context, this);
     }
 
-    @Override
-    public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
-        super.setAdapter(adapter);
-        if (adapter == null) {
-            return;
+    /*package*/ RecipientChip getLastChip() {
+        RecipientChip last = null;
+        RecipientChip[] chips = getSortedRecipients();
+        if (chips != null && chips.length > 0) {
+            last = chips[chips.length - 1];
         }
+        return last;
     }
 
     @Override
     public void onSelectionChanged(int start, int end) {
         // When selection changes, see if it is inside the chips area.
         // If so, move the cursor back after the chips again.
-        Spannable span = getSpannable();
-        int textLength = getText().length();
-        RecipientChip[] chips = span.getSpans(start, textLength, RecipientChip.class);
-        if (chips != null && chips.length > 0) {
-            if (chips != null && chips.length > 0) {
-                // Grab the last chip and set the cursor to after it.
-                setSelection(Math.min(span.getSpanEnd(chips[chips.length - 1]) + 1, textLength));
-            }
+        RecipientChip last = getLastChip();
+        if (last != null) {
+            // Grab the last chip and set the cursor to after it.
+            setSelection(Math.min(getSpannable().getSpanEnd(last) + 1, getText().length()));
         }
         super.onSelectionChanged(start, end);
     }
@@ -279,7 +279,7 @@
         }
     }
 
-
+    @Override
     public Parcelable onSaveInstanceState() {
         // If the user changes orientation while they are editing, just roll back the selection.
         clearSelectedChip();
@@ -405,7 +405,7 @@
         // on the sides.
         int height = (int) mChipHeight;
         int deleteWidth = height;
-        CharSequence ellipsizedText = ellipsizeText(contact.getDisplayName(), paint,
+        CharSequence ellipsizedText = ellipsizeText(createChipDisplayText(contact), paint,
                 calculateAvailableWidth(true) - deleteWidth);
 
         // Make sure there is a minimum chip width so the user can ALWAYS
@@ -438,13 +438,6 @@
         return tmpBitmap;
     }
 
-    /**
-     * Get the background drawable for a RecipientChip.
-     */
-    public Drawable getChipBackground(RecipientEntry contact) {
-        return (mValidator != null && mValidator.isValid(contact.getDestination())) ?
-                mChipBackground : mInvalidChipBackground;
-    }
 
     private Bitmap createUnselectedChip(RecipientEntry contact, TextPaint paint, Layout layout) {
         // Ellipsize the text so that it takes AT MOST the entire width of the
@@ -452,10 +445,7 @@
         // on the sides.
         int height = (int) mChipHeight;
         int iconWidth = height;
-        String displayText =
-            !TextUtils.isEmpty(contact.getDisplayName()) ? contact.getDisplayName() :
-            !TextUtils.isEmpty(contact.getDestination()) ? contact.getDestination() : "";
-        CharSequence ellipsizedText = ellipsizeText(displayText, paint,
+        CharSequence ellipsizedText = ellipsizeText(createChipDisplayText(contact), paint,
                 calculateAvailableWidth(false) - iconWidth);
         // Make sure there is a minimum chip width so the user can ALWAYS
         // tap a chip without difficulty.
@@ -517,6 +507,15 @@
         return tmpBitmap;
     }
 
+    /**
+     * Get the background drawable for a RecipientChip.
+     */
+    // Visible for testing.
+    /*package*/ Drawable getChipBackground(RecipientEntry contact) {
+        return (mValidator != null && mValidator.isValid(contact.getDestination())) ?
+                mChipBackground : mInvalidChipBackground;
+    }
+
     private float getTextYOffset(String text, TextPaint paint, int height) {
         Rect bounds = new Rect();
         paint.getTextBounds((String)text, 0, text.length(), bounds);
@@ -524,7 +523,7 @@
         return height - ((height - textHeight) / 2);
     }
 
-    public RecipientChip constructChipSpan(RecipientEntry contact, int offset, boolean pressed)
+    private RecipientChip constructChipSpan(RecipientEntry contact, int offset, boolean pressed)
             throws NullPointerException {
         if (mChipBackground == null) {
             throw new NullPointerException(
@@ -646,7 +645,7 @@
             }
         }
         // Try to find the scroll view parent, if it exists.
-        if (mScrollView == null && !mTried) {
+        if (mScrollView == null && !mTriedGettingScrollView) {
             ViewParent parent = getParent();
             while (parent != null && !(parent instanceof ScrollView)) {
                 parent = parent.getParent();
@@ -654,7 +653,7 @@
             if (parent != null) {
                 mScrollView = (ScrollView) parent;
             }
-            mTried = true;
+            mTriedGettingScrollView = true;
         }
     }
 
@@ -708,7 +707,7 @@
                 }
                 mPendingChipsCount--;
             }
-            sanitizeSpannable();
+            sanitizeEnd();
             if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0
                     && mTemporaryRecipients.size() <= RecipientAlternatesAdapter.MAX_LOOKUPS) {
                 if (hasFocus() || mTemporaryRecipients.size() < CHIP_LIMIT) {
@@ -736,9 +735,10 @@
     /**
      * Remove any characters after the last valid chip.
      */
-    private void sanitizeSpannable() {
+    // Visible for testing.
+    /*package*/ void sanitizeEnd() {
         // Find the last chip; eliminate any commit characters after it.
-        RecipientChip[] chips = getRecipients();
+        RecipientChip[] chips = getSortedRecipients();
         if (chips != null && chips.length > 0) {
             int end;
             ImageSpan lastSpan;
@@ -746,7 +746,7 @@
             if (mMoreChip != null) {
                 lastSpan = mMoreChip;
             } else {
-                lastSpan = chips[chips.length - 1];
+                lastSpan = getLastChip();
             }
             end = getSpannable().getSpanEnd(lastSpan);
             Editable editable = getText();
@@ -779,7 +779,7 @@
         }
         RecipientEntry entry = createTokenizedEntry(token);
         if (entry != null) {
-            String destText = createDisplayText(entry);
+            String destText = createAddressText(entry);
             // Always leave a blank space at the end of a chip.
             int textLength = destText.length() - 1;
             SpannableString chipText = new SpannableString(destText);
@@ -1324,7 +1324,8 @@
     }
 
     // Visible for testing.
-    /* package */ String createDisplayText(RecipientEntry entry) {
+    // Use this method to generate text to add to the list of addresses.
+    /*package*/ String createAddressText(RecipientEntry entry) {
         String display = entry.getDisplayName();
         String address = entry.getDestination();
         if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) {
@@ -1339,15 +1340,39 @@
             }
         }
         Rfc822Token token = new Rfc822Token(display, address, null);
-        String displayText = token.toString();
-        String trimmedDisplayText = displayText.trim();
+        String trimmedDisplayText = token.toString().trim();
         int index = trimmedDisplayText.indexOf(",");
         return index < trimmedDisplayText.length() - 1 ? (String) mTokenizer
-                .terminateToken(displayText) : displayText;
+                .terminateToken(trimmedDisplayText) : trimmedDisplayText;
+    }
+
+    // Visible for testing.
+    // Use this method to generate text to display in a chip.
+    /*package*/ String createChipDisplayText(RecipientEntry entry) {
+        String display = entry.getDisplayName();
+        String address = entry.getDestination();
+        if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) {
+            display = null;
+        }
+        if (address != null) {
+            // Tokenize out the address in case the address already
+            // contained the username as well.
+            Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(address);
+            if (tokenized != null && tokenized.length > 0) {
+                address = tokenized[0].getAddress();
+            }
+        }
+        if (!TextUtils.isEmpty(display)) {
+            return display;
+        } else if (!TextUtils.isEmpty(address)){
+            return address;
+        } else {
+            return new Rfc822Token(display, address, null).toString();
+        }
     }
 
     private CharSequence createChip(RecipientEntry entry, boolean pressed) {
-        String displayText = createDisplayText(entry);
+        String displayText = createAddressText(entry);
         if (TextUtils.isEmpty(displayText)) {
             return null;
         }
@@ -1420,7 +1445,7 @@
     /** Returns a collection of contact Id for each chip inside this View. */
     /* package */ Collection<Long> getContactIds() {
         final Set<Long> result = new HashSet<Long>();
-        RecipientChip[] chips = getRecipients();
+        RecipientChip[] chips = getSortedRecipients();
         if (chips != null) {
             for (RecipientChip chip : chips) {
                 result.add(chip.getContactId());
@@ -1429,14 +1454,25 @@
         return result;
     }
 
-    private RecipientChip[] getRecipients() {
-        return getSpannable().getSpans(0, getText().length(), RecipientChip.class);
+
+    /** Returns a collection of data Id for each chip inside this View. May be null. */
+    /* package */ Collection<Long> getDataIds() {
+        final Set<Long> result = new HashSet<Long>();
+        RecipientChip [] chips = getSortedRecipients();
+        if (chips != null) {
+            for (RecipientChip chip : chips) {
+                result.add(chip.getDataId());
+            }
+        }
+        return result;
     }
 
     // Visible for testing.
-    /* package */ RecipientChip[] getSortedRecipients() {
+    /* package */RecipientChip[] getSortedRecipients() {
+        RecipientChip[] recips = getSpannable()
+                .getSpans(0, getText().length(), RecipientChip.class);
         ArrayList<RecipientChip> recipientsList = new ArrayList<RecipientChip>(Arrays
-                .asList(getRecipients()));
+                .asList(recips));
         final Spannable spannable = getSpannable();
         Collections.sort(recipientsList, new Comparator<RecipientChip>() {
 
@@ -1456,19 +1492,6 @@
         return recipientsList.toArray(new RecipientChip[recipientsList.size()]);
     }
 
-    /** Returns a collection of data Id for each chip inside this View. May be null. */
-    /* package */ Collection<Long> getDataIds() {
-        final Set<Long> result = new HashSet<Long>();
-        RecipientChip [] chips = getRecipients();
-        if (chips != null) {
-            for (RecipientChip chip : chips) {
-                result.add(chip.getDataId());
-            }
-        }
-        return result;
-    }
-
-
     @Override
     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
         return false;
@@ -1630,7 +1653,7 @@
      * @return A RecipientChip in the selected state or null if the chip
      * just contained an email address.
      */
-    public RecipientChip selectChip(RecipientChip currentChip) {
+    private RecipientChip selectChip(RecipientChip currentChip) {
         if (currentChip.getContactId() == RecipientEntry.INVALID_CONTACT) {
             CharSequence text = currentChip.getValue();
             Editable editable = getText();
@@ -1722,7 +1745,7 @@
      * the chip without a delete icon and with an unfocused background. This
      * is called when the RecipientChip no longer has focus.
      */
-    public void unselectChip(RecipientChip chip) {
+    private void unselectChip(RecipientChip chip) {
         int start = getChipStart(chip);
         int end = getChipEnd(chip);
         Editable editable = getText();
@@ -1751,20 +1774,6 @@
     }
 
     /**
-     * Return whether this chip contains the position passed in.
-     */
-    public boolean matchesChip(RecipientChip chip, int offset) {
-        int start = getChipStart(chip);
-        int end = getChipEnd(chip);
-
-        if (start == -1 || end == -1) {
-            return false;
-        }
-        return (offset >= start && offset <= end);
-    }
-
-
-    /**
      * Return whether a touch event was inside the delete target of
      * a selected chip. It is in the delete target if:
      * 1) the x and y points of the event are within the
@@ -1809,7 +1818,8 @@
      * Replace this currently selected chip with a new chip
      * that uses the contact data provided.
      */
-    public void replaceChip(RecipientChip chip, RecipientEntry entry) {
+    // Visible for testing.
+    /*package*/ void replaceChip(RecipientChip chip, RecipientEntry entry) {
         boolean wasSelected = chip == mSelectedChip;
         if (wasSelected) {
             mSelectedChip = null;
@@ -2051,7 +2061,7 @@
             }
             String[] addresses = new String[originalRecipients.size()];
             for (int i = 0; i < originalRecipients.size(); i++) {
-                addresses[i] = createDisplayText(originalRecipients.get(i).getEntry());
+                addresses[i] = createAddressText(originalRecipients.get(i).getEntry());
             }
             HashMap<String, RecipientEntry> entries = RecipientAlternatesAdapter
                     .getMatchingRecipients(getContext(), addresses);
@@ -2112,7 +2122,7 @@
                 (ArrayList<RecipientChip>) params[0];
             String[] addresses = new String[originalRecipients.size()];
             for (int i = 0; i < originalRecipients.size(); i++) {
-                addresses[i] = createDisplayText(originalRecipients.get(i).getEntry());
+                addresses[i] = createAddressText(originalRecipients.get(i).getEntry());
             }
             HashMap<String, RecipientEntry> entries = RecipientAlternatesAdapter
                     .getMatchingRecipients(getContext(), addresses);
diff --git a/tests/src/com/android/ex/chips/ChipsTest.java b/tests/src/com/android/ex/chips/ChipsTest.java
index 41b9eab..e56de61 100644
--- a/tests/src/com/android/ex/chips/ChipsTest.java
+++ b/tests/src/com/android/ex/chips/ChipsTest.java
@@ -70,7 +70,7 @@
         }
 
         @Override
-        public Drawable getChipBackground(RecipientEntry contact) {
+        Drawable getChipBackground(RecipientEntry contact) {
             return createChipBackground();
         }
 
@@ -95,27 +95,34 @@
         RecipientEditTextView view = createViewForTesting();
         RecipientEntry entry = RecipientEntry.constructGeneratedEntry("User Name, Jr",
                 "user@username.com");
-        String test = view.createDisplayText(entry);
+        String testAddress = view.createAddressText(entry);
+        String testDisplay = view.createChipDisplayText(entry);
         assertEquals("Expected a properly formatted RFC email address",
-                "\"User Name, Jr\" <user@username.com>, ", test);
+                "\"User Name, Jr\" <user@username.com>, ", testAddress);
+        assertEquals("Expected a displayable name", "User Name, Jr", testDisplay);
+
 
         RecipientEntry alreadyFormatted = RecipientEntry.constructFakeEntry("user@username.com, ");
-        test = view.createDisplayText(alreadyFormatted);
+        testAddress = view.createAddressText(alreadyFormatted);
+        testDisplay = view.createChipDisplayText(alreadyFormatted);
         assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ",
-                test);
+                testAddress);
+        assertEquals("Expected a displayable name", "user@username.com", testDisplay);
 
         RecipientEntry alreadyFormattedNoSpace = RecipientEntry
                 .constructFakeEntry("user@username.com,");
-        test = view.createDisplayText(alreadyFormattedNoSpace);
+        testAddress = view.createAddressText(alreadyFormattedNoSpace);
         assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ",
-                test);
+                testAddress);
 
         RecipientEntry alreadyNamed = RecipientEntry.constructGeneratedEntry("User Name",
                 "\"User Name, Jr\" <user@username.com>");
-        test = view.createDisplayText(alreadyNamed);
+        testAddress = view.createAddressText(alreadyNamed);
+        testDisplay = view.createChipDisplayText(alreadyNamed);
         assertEquals(
                 "Expected address that used the name not the excess address name",
-                "User Name <user@username.com>, ", test);
+                "User Name <user@username.com>, ", testAddress);
+        assertEquals("Expected a displayable name", "User Name", testDisplay);
     }
 
     public void testSanitizeBetween() {
@@ -155,6 +162,35 @@
                 - extra.length());
     }
 
+    public void testSanitizeEnd() {
+        // First, add 2 chips and then make sure we remove
+        // the extra content between them correctly.
+        populateMocks(2);
+        MockRecipientEditTextView view = createViewForTesting();
+        String first = (String) mTokenizer.terminateToken("FIRST");
+        String second = (String) mTokenizer.terminateToken("SECOND");
+        String extra = "EXTRA";
+        mEditable = new SpannableStringBuilder();
+        mEditable.append(first + second);
+        int firstStart = mEditable.toString().indexOf(first);
+        int firstEnd = firstStart + first.trim().length();
+        int secondStart = mEditable.toString().indexOf(second);
+        int secondEnd = secondStart + second.trim().length();
+        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], firstStart, firstEnd, 0);
+        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], secondStart, secondEnd, 0);
+        view.sanitizeEnd();
+        String editableString = mEditable.toString();
+        assertEquals(editableString.indexOf(extra), -1);
+        assertEquals(editableString.indexOf(first), firstStart);
+        assertEquals(editableString.indexOf(second), secondStart);
+        assertEquals(editableString, (first + second));
+        mEditable.append(extra);
+        editableString = mEditable.toString();
+        assertEquals(mEditable.toString(), (first + second + extra));
+        view.sanitizeEnd();
+        assertEquals(mEditable.toString(), (first + second));
+    }
+
     public void testMoreChip() {
         // Add 3 chips: this is the trigger point at which the more chip will be created.
         // Test that adding the chips and then creating and removing the more chip, as if
@@ -384,34 +420,6 @@
         assertEquals(mEditable.getSpanStart(moreChip), -1);
     }
 
-    public void testMatchesChip() {
-        // Test the logic for checking if we have found the chip
-        // that matches a particular offset in chips field.
-        populateMocks(3);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first+second+third);
-
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertFalse(view.matchesChip(mMockRecips[mMockRecips.length - 3], -1));
-        assertFalse(view.matchesChip(mMockRecips[mMockRecips.length - 1], mEditable.length() + 1));
-        assertTrue(view.matchesChip(mMockRecips[mMockRecips.length - 3], firstStart));
-        assertTrue(view.matchesChip(mMockRecips[mMockRecips.length - 3], firstEnd));
-        assertTrue(view.matchesChip(mMockRecips[mMockRecips.length - 3], firstEnd - 1));
-    }
-
     public void testRemoveChip() {
         // Create 3 chips to start and test removing chips in various postions.
         populateMocks(3);
@@ -784,6 +792,33 @@
         assertFalse(view.isCompletedToken("test "));
     }
 
+    public void testGetLastChip() {
+        populateMocks(3);
+        MockRecipientEditTextView view = createViewForTesting();
+        view.setMoreItem(createTestMoreItem());
+        view.setChipBackground(createChipBackground());
+        view.setChipHeight(48);
+        String first = (String) mTokenizer.terminateToken("FIRST");
+        String second = (String) mTokenizer.terminateToken("SECOND");
+        String third = (String) mTokenizer.terminateToken("THIRD");
+        mEditable = new SpannableStringBuilder();
+        mEditable.append(first + second + third);
+
+        // Test replacing the first chip with a new chip.
+        int firstStart = mEditable.toString().indexOf(first);
+        int firstEnd = firstStart + first.trim().length();
+        int secondStart = mEditable.toString().indexOf(second);
+        int secondEnd = secondStart + second.trim().length();
+        int thirdStart = mEditable.toString().indexOf(third);
+        int thirdEnd = thirdStart + third.trim().length();
+        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
+        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
+        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
+        assertEquals(view.getLastChip(), mMockRecips[mMockRecips.length - 1]);
+        mEditable.append("extra");
+        assertEquals(view.getLastChip(), mMockRecips[mMockRecips.length - 1]);
+    }
+
     private Drawable createChipBackground() {
         Bitmap drawable = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         return new BitmapDrawable(getContext().getResources(), drawable);