Setup a really cheesy delete icon.

This allows us to:
1) set a pressed state
2) get taps on the "delete icon" (the world remains thankful that I am
not a visual designer)
3) get the correctly tapped chip regardless of presses near the end
Change-Id: I286de87b05ac46aca1511a2c7108c6778438386e
diff --git a/res/drawable-hdpi/delete.png b/res/drawable-hdpi/delete.png
new file mode 100644
index 0000000..200eae4
--- /dev/null
+++ b/res/drawable-hdpi/delete.png
Binary files differ
diff --git a/res/drawable-mdpi/delete.png b/res/drawable-mdpi/delete.png
new file mode 100644
index 0000000..200eae4
--- /dev/null
+++ b/res/drawable-mdpi/delete.png
Binary files differ
diff --git a/res/drawable/chip_background_selected.xml b/res/drawable/chip_background_selected.xml
index e6c127c..ea97abe 100644
--- a/res/drawable/chip_background_selected.xml
+++ b/res/drawable/chip_background_selected.xml
@@ -15,7 +15,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
-    <solid android:color="#DC2E1A"/>
+    <solid android:color="#97def8"/>
     <corners android:radius="4.0dp" />
     <padding android:left="6dp" android:right="6dp" android:top="2dp"
     android:bottom="2dp"/>
diff --git a/src/com/android/ex/chips/RecipientEditTextView.java b/src/com/android/ex/chips/RecipientEditTextView.java
index e425d67..8fc953c 100644
--- a/src/com/android/ex/chips/RecipientEditTextView.java
+++ b/src/com/android/ex/chips/RecipientEditTextView.java
@@ -56,6 +56,8 @@
 
     private Drawable mChipBackground = null;
 
+    private Drawable mChipDelete = null;
+
     private int mChipPadding;
 
     private Tokenizer mTokenizer;
@@ -69,6 +71,12 @@
         }
     };
 
+    private Drawable mChipBackgroundPressed;
+
+    private RecipientChip mSelectedChip;
+
+    private int mChipDeleteWidth;
+
     public RecipientEditTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mHandler = new Handler();
@@ -96,27 +104,47 @@
         // Ellipsize the text so that it takes AT MOST the entire width of the
         // autocomplete text entry area. Make sure to leave space for padding
         // on the sides.
-        CharSequence ellipsizedText = TextUtils.ellipsize(text, paint, calculateAvailableWidth(),
-                TextUtils.TruncateAt.END);
+        CharSequence ellipsizedText = TextUtils.ellipsize(text, paint,
+                calculateAvailableWidth(pressed), TextUtils.TruncateAt.END);
 
         int height = getLineHeight();
         int width = (int) Math.floor(paint.measureText(ellipsizedText, 0, ellipsizedText.length()))
                 + (mChipPadding * 2);
+        if (pressed) {
+            width += mChipDeleteWidth;
+        }
 
         // Create the background of the chip.
         Bitmap tmpBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         Canvas canvas = new Canvas(tmpBitmap);
-        if (mChipBackground != null) {
-            mChipBackground.setBounds(0, 0, width, height);
-            mChipBackground.draw(canvas);
+        if (pressed) {
+            if (mChipBackgroundPressed != null) {
+                mChipBackgroundPressed.setBounds(0, 0, width, height);
+                mChipBackgroundPressed.draw(canvas);
+
+                // Align the display text with where the user enters text.
+                canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), mChipPadding, height
+                        - layout.getLineDescent(line), paint);
+                mChipDelete.setBounds(width - mChipDeleteWidth, 0, width, height);
+                mChipDelete.draw(canvas);
+            } else {
+                Log.w(TAG,
+                        "Unable to draw a background for the chips as it was never set");
+            }
         } else {
-            Log.w(TAG,
-                    "Unable to draw a background for the chips as it was never set");
+            if (mChipBackground != null) {
+                mChipBackground.setBounds(0, 0, width, height);
+                mChipBackground.draw(canvas);
+
+                // Align the display text with where the user enters text.
+                canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), mChipPadding, height
+                        - layout.getLineDescent(line), paint);
+            } else {
+                Log.w(TAG,
+                        "Unable to draw a background for the chips as it was never set");
+            }
         }
 
-        // Align the display text with where the user enters text.
-        canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), mChipPadding, height
-                - layout.getLineDescent(line), paint);
 
         // Get the location of the widget so we can properly offset
         // the anchor for each chip.
@@ -152,8 +180,13 @@
     // Get the max amount of space a chip can take up. The formula takes into
     // account the width of the EditTextView, any view padding, and padding
     // that will be added to the chip.
-    private float calculateAvailableWidth() {
-        return getWidth() - getPaddingLeft() - getPaddingRight() - (mChipPadding * 2);
+    private float calculateAvailableWidth(boolean pressed) {
+        int paddingRight = 0;
+        if (pressed) {
+            paddingRight = mChipDeleteWidth;
+        }
+        return getWidth() - getPaddingLeft() - getPaddingRight() - (mChipPadding * 2)
+                - paddingRight;
     }
 
     /**
@@ -163,8 +196,12 @@
      * @param padding Padding around the text in a chip
      * @param offset Offset between the chip and the dropdown of alternates
      */
-    public void setChipDimensions(Drawable chipBackground, float padding) {
+    public void setChipDimensions(Drawable chipBackground, Drawable chipBackgroundPressed,
+            Drawable chipDelete, float padding) {
         mChipBackground = chipBackground;
+        mChipBackgroundPressed = chipBackgroundPressed;
+        mChipDelete = chipDelete;
+        mChipDeleteWidth = chipDelete.getIntrinsicWidth();
         mChipPadding = (int) padding;
     }
 
@@ -246,21 +283,34 @@
     public boolean onTouchEvent(MotionEvent event) {
         int action = event.getAction();
         boolean handled = super.onTouchEvent(event);
+        boolean chipWasSelected = false;
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
             Spannable span = getSpannable();
             int offset = getOffsetForPosition(event.getX(), event.getY());
-            int start = offset;
-            int end = span.length();
+            int end = span.nextSpanTransition(offset, span.length(), RecipientChip.class);
+            int start = mTokenizer.findTokenStart(span, end);
             RecipientChip[] chips = span.getSpans(start, end, RecipientChip.class);
             if (chips != null && chips.length > 0) {
                 // Get the first chip that matched.
                 final RecipientChip currentChip = chips[0];
 
                 if (action == MotionEvent.ACTION_UP) {
-                    currentChip.onClick(this);
-                } else if (action == MotionEvent.ACTION_DOWN) {
-
+                    if (mSelectedChip != null && mSelectedChip != currentChip) {
+                        mSelectedChip.unselectChip();
+                        mSelectedChip = currentChip.selectChip();
+                    } else if (mSelectedChip == null) {
+                        mSelectedChip = currentChip.selectChip();
+                    } else {
+                        mSelectedChip.onClick(this, event.getX(), event.getY());
+                    }
                 }
+                chipWasSelected = true;
+            }
+        }
+        if (!chipWasSelected) {
+            if (mSelectedChip != null) {
+                mSelectedChip.unselectChip();
+                mSelectedChip = null;
             }
         }
         return handled;
@@ -277,7 +327,7 @@
             chipText.setSpan(constructChipSpan(entry, start, false), 0, entry.getDisplayName()
                     .length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
         } catch (NullPointerException e) {
-            Log.e(TAG, e.getMessage());
+            Log.e(TAG, e.getMessage(), e);
             return null;
         }
 
@@ -321,12 +371,19 @@
 
         private int mId = -1;
 
+        private RecipientEntry mEntry;
+
+        private boolean mSelected = false;
+
+        private Rect mBounds;
         public RecipientChip(Drawable drawable, RecipientEntry entry, int offset, Rect bounds) {
             super(drawable);
             mDisplay = entry.getDisplayName();
             mValue = entry.getDestination();
             mId = entry.getContactId();
             mOffset = offset;
+            mEntry = entry;
+            mBounds = bounds;
 
             mAnchorView = new View(getContext());
             mAnchorView.setLeft(bounds.left);
@@ -336,6 +393,26 @@
             mAnchorView.setVisibility(View.GONE);
         }
 
+        public void unselectChip() {
+            if (getChipStart() == -1 || getChipEnd() == -1) {
+                mSelectedChip = null;
+                return;
+            }
+            clearComposingText();
+            RecipientChip newChipSpan = null;
+            try {
+                newChipSpan = constructChipSpan(mEntry, mOffset, false);
+            } catch (NullPointerException e) {
+                Log.e(TAG, e.getMessage(), e);
+                return;
+            }
+            replace(newChipSpan);
+            if (mPopup != null && mPopup.isShowing()) {
+                mPopup.dismiss();
+            }
+            return;
+        }
+
         public void onKeyDown(int keyCode, KeyEvent event) {
             if (keyCode == KeyEvent.KEYCODE_DEL) {
                 if (mPopup != null && mPopup.isShowing()) {
@@ -362,8 +439,10 @@
             Spannable spannable = getSpannable();
             int spanStart = getChipStart();
             int spanEnd = getChipEnd();
+
             QwertyKeyListener.markAsReplaced(getText(), spanStart, spanEnd, "");
             spannable.removeSpan(this);
+            spannable.setSpan(null, spanStart, spanEnd, 0);
             getText().delete(spanStart, spanEnd);
         }
 
@@ -382,7 +461,7 @@
             try {
                 newChipSpan = constructChipSpan(entry, mOffset, false);
             } catch (NullPointerException e) {
-                Log.e(TAG, e.getMessage());
+                Log.e(TAG, e.getMessage(), e);
                 return;
             }
             replace(newChipSpan);
@@ -392,6 +471,55 @@
             onChipChanged();
         }
 
+        public RecipientChip selectChip() {
+            clearComposingText();
+            RecipientChip newChipSpan = null;
+            if (isCompletedContact()) {
+                try {
+                    newChipSpan = constructChipSpan(mEntry, mOffset, true);
+                    newChipSpan.setSelected(true);
+                } catch (NullPointerException e) {
+                    Log.e(TAG, e.getMessage(), e);
+                    return newChipSpan;
+                }
+                replace(newChipSpan);
+                if (mPopup != null && mPopup.isShowing()) {
+                    mPopup.dismiss();
+                }
+                mSelected = true;
+                // Make sure we call edit on the new chip span.
+                newChipSpan.showAlternates();
+            } else {
+                CharSequence text = getValue();
+                removeChip();
+                Editable editable = getText();
+                setSelection(editable.length());
+                editable.append(text);
+            }
+            return newChipSpan;
+        }
+
+        private void showAlternates() {
+            mPopup = new ListPopupWindow(RecipientEditTextView.this.getContext());
+
+            if (!mPopup.isShowing()) {
+                mAnchorView.setLeft(mLeft);
+                mAnchorView.setRight(mLeft);
+                mPopup.setAnchorView(mAnchorView);
+                BaseRecipientAdapter adapter = (BaseRecipientAdapter) getAdapter();
+                adapter.getFilter().filter(getValue(), this);
+                mPopup.setAdapter(adapter);
+                // TODO: get width from dimen.xml.
+                mPopup.setWidth(getWidth());
+                mPopup.setOnItemClickListener(this);
+                mPopup.setOnDismissListener(this);
+            }
+        }
+
+        private void setSelected(boolean selected) {
+            mSelected = selected;
+        }
+
         public CharSequence getDisplay() {
             return mDisplay;
         }
@@ -400,28 +528,18 @@
             return mValue;
         }
 
-        public void onClick(View widget) {
-            if (isCompletedContact()) {
-                mPopup = new ListPopupWindow(widget.getContext());
+        private boolean isInDelete(float x, float y) {
+            // Figure out the bounds of this chip and whether or not
+            // the user clicked in the X portion.
+            return x > (mBounds.right - mChipDeleteWidth) && x < mBounds.right;
+        }
 
-                if (!mPopup.isShowing()) {
-                    mAnchorView.setLeft(mLeft);
-                    mAnchorView.setRight(mLeft);
-                    mPopup.setAnchorView(mAnchorView);
-                    BaseRecipientAdapter adapter = (BaseRecipientAdapter)getAdapter();
-                    adapter.getFilter().filter(getValue(), this);
-                    mPopup.setAdapter(adapter);
-                    // TODO: get width from dimen.xml.
-                    mPopup.setWidth(getWidth());
-                    mPopup.setOnItemClickListener(this);
-                    mPopup.setOnDismissListener(this);
+        public void onClick(View widget, float x, float y) {
+            if (mSelected) {
+                if (isInDelete(x, y)) {
+                    removeChip();
+                    return;
                 }
-            } else {
-                CharSequence text = getValue();
-                removeChip();
-                Editable editable = getText();
-                setSelection(editable.length());
-                editable.append(text);
             }
         }