RecipientEditTextView.java revision b5ebb8916a1bb178062a0ccb59e80de27ec1bb2d
12d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira/* 22d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * Copyright (C) 2011 The Android Open Source Project 32d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * 42d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License"); 52d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * you may not use this file except in compliance with the License. 62d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * You may obtain a copy of the License at 72d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * 82d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * http://www.apache.org/licenses/LICENSE-2.0 92d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * 102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * Unless required by applicable law or agreed to in writing, software 112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS, 122d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * See the License for the specific language governing permissions and 142d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * limitations under the License. 152d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira */ 162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 172d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereirapackage com.android.ex.chips; 182d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.content.Context; 202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Bitmap; 212d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Canvas; 222d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Paint; 232d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Rect; 242d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.drawable.BitmapDrawable; 252d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.drawable.Drawable; 262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.os.Handler; 272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Editable; 282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Layout; 292d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Spannable; 302d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.SpannableString; 312d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Spanned; 322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.TextPaint; 332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.TextUtils; 342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.method.QwertyKeyListener; 352d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.style.ImageSpan; 362d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.util.AttributeSet; 37078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereiraimport android.util.Log; 382d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.view.KeyEvent; 392d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.view.MotionEvent; 402d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.view.View; 412d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.AdapterView; 422d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.AdapterView.OnItemClickListener; 43b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereiraimport android.widget.Filter.FilterListener; 442d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.ListPopupWindow; 452d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.MultiAutoCompleteTextView; 46b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawaimport android.widget.PopupWindow.OnDismissListener; 47b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa 48b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawaimport java.util.Collection; 49b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawaimport java.util.HashSet; 50b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawaimport java.util.List; 51b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawaimport java.util.Set; 522d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 530f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereiraimport java.util.ArrayList; 540f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira 552d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira/** 562d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * RecipientEditTextView is an auto complete text view for use with applications 572d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * that use the new Chips UI for addressing a message to recipients. 582d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira */ 592d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereirapublic class RecipientEditTextView extends MultiAutoCompleteTextView 602d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira implements OnItemClickListener { 612d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 62078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira private static final String TAG = "RecipientEditTextView"; 632d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 64078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira private Drawable mChipBackground = null; 652d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 6643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private Drawable mChipDelete = null; 6743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 68078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira private int mChipPadding; 692d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private Tokenizer mTokenizer; 712d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private final Handler mHandler; 732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private Runnable mDelayedSelectionMode = new Runnable() { 752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 762d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void run() { 772d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira setSelection(getText().length()); 782d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 792d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira }; 802d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 8143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private Drawable mChipBackgroundPressed; 8243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 8343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private RecipientChip mSelectedChip; 8443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 8543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private int mChipDeleteWidth; 8643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 870f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira private ArrayList<RecipientChip> mRecipients; 880f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira 892d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public RecipientEditTextView(Context context, AttributeSet attrs) { 902d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira super(context, attrs); 912d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mHandler = new Handler(); 922d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira setOnItemClickListener(this); 930f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira mRecipients = new ArrayList<RecipientChip>(); 942d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 952d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 96f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira public RecipientChip constructChipSpan(RecipientEntry contact, int offset, boolean pressed) 97078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira throws NullPointerException { 98078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira if (mChipBackground == null) { 99078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira throw new NullPointerException 100078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira ("Unable to render any chips as setChipDimensions was not called."); 101078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 102f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira String text = contact.getDisplayName(); 1032d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Layout layout = getLayout(); 1042d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int line = layout.getLineForOffset(offset); 1052d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int lineTop = layout.getLineTop(line); 1062d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1072d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira TextPaint paint = getPaint(); 1082d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira float defaultSize = paint.getTextSize(); 1092d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Reduce the size of the text slightly so that we can get the "look" of 1112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // padding. 1122d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira paint.setTextSize((float) (paint.getTextSize() * .9)); 1132d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1142d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Ellipsize the text so that it takes AT MOST the entire width of the 1152d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // autocomplete text entry area. Make sure to leave space for padding 1162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // on the sides. 11743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira CharSequence ellipsizedText = TextUtils.ellipsize(text, paint, 11843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira calculateAvailableWidth(pressed), TextUtils.TruncateAt.END); 1192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int height = getLineHeight(); 1212d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int width = (int) Math.floor(paint.measureText(ellipsizedText, 0, ellipsizedText.length())) 122078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira + (mChipPadding * 2); 12343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (pressed) { 12443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira width += mChipDeleteWidth; 12543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 1262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Create the background of the chip. 1282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Bitmap tmpBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 1292d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Canvas canvas = new Canvas(tmpBitmap); 13043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (pressed) { 13143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (mChipBackgroundPressed != null) { 13243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipBackgroundPressed.setBounds(0, 0, width, height); 13343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipBackgroundPressed.draw(canvas); 13443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 13543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira // Align the display text with where the user enters text. 13643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), mChipPadding, height 13743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira - layout.getLineDescent(line), paint); 13843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipDelete.setBounds(width - mChipDeleteWidth, 0, width, height); 13943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipDelete.draw(canvas); 14043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } else { 14143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Log.w(TAG, 14243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira "Unable to draw a background for the chips as it was never set"); 14343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 1442d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } else { 14543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (mChipBackground != null) { 14643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipBackground.setBounds(0, 0, width, height); 14743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipBackground.draw(canvas); 14843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 14943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira // Align the display text with where the user enters text. 15043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), mChipPadding, height 15143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira - layout.getLineDescent(line), paint); 15243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } else { 15343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Log.w(TAG, 15443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira "Unable to draw a background for the chips as it was never set"); 15543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 1562d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 1572d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1582d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1592d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Get the location of the widget so we can properly offset 1602d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // the anchor for each chip. 1612d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int[] xy = new int[2]; 1622d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira getLocationOnScreen(xy); 1632d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Pass the full text, un-ellipsized, to the chip. 1642d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Drawable result = new BitmapDrawable(getResources(), tmpBitmap); 1652d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira result.setBounds(0, 0, width, height); 166f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira Rect bounds = new Rect(xy[0] + offset, xy[1] + lineTop, xy[0] + width, 167f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira calculateLineBottom(xy[1], line)); 168f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira RecipientChip recipientChip = new RecipientChip(result, contact, offset, bounds); 1692d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Return text to the original size. 1712d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira paint.setTextSize(defaultSize); 1722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 1732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return recipientChip; 1742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 1752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 176f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira // The bottom of the line the chip will be located on is calculated by 4 factors: 177f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira // 1) which line the chip appears on 178f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira // 2) the height of a line in the autocomplete view 179f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira // 3) padding built into the edit text view will move the bottom position 180f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira // 4) the position of the autocomplete view on the screen, taking into account 181f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira // that any top padding will move this down visually 182f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira private int calculateLineBottom(int yOffset, int line) { 183f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira int bottomPadding = 0; 184f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira if (line == getLineCount() - 1) { 185f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira bottomPadding += getPaddingBottom(); 186f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira } 187f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira return ((line + 1) * getLineHeight()) + (yOffset + getPaddingTop()) + bottomPadding; 188f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira } 189f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira 1902d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Get the max amount of space a chip can take up. The formula takes into 1912d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // account the width of the EditTextView, any view padding, and padding 1922d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // that will be added to the chip. 19343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private float calculateAvailableWidth(boolean pressed) { 19443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira int paddingRight = 0; 19543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (pressed) { 19643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira paddingRight = mChipDeleteWidth; 19743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 19843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return getWidth() - getPaddingLeft() - getPaddingRight() - (mChipPadding * 2) 19943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira - paddingRight; 2002d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2012d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 202078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira /** 203078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira * Set all chip dimensions and resources. This has to be done from the application 204078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira * as this is a static library. 205078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira * @param chipBackground drawable 206078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira * @param padding Padding around the text in a chip 207078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira * @param offset Offset between the chip and the dropdown of alternates 208078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira */ 20943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira public void setChipDimensions(Drawable chipBackground, Drawable chipBackgroundPressed, 21043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Drawable chipDelete, float padding) { 211078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira mChipBackground = chipBackground; 21243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipBackgroundPressed = chipBackgroundPressed; 21343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipDelete = chipDelete; 21443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mChipDeleteWidth = chipDelete.getIntrinsicWidth(); 215078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira mChipPadding = (int) padding; 2162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2172d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2182d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 2192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void setTokenizer(Tokenizer tokenizer) { 2202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mTokenizer = tokenizer; 2212d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira super.setTokenizer(mTokenizer); 2222d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2232d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2242d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // We want to handle replacing text in the onItemClickListener 2252d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // so we can get all the associated contact information including 2262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // display text, address, and id. 2272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 2282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira protected void replaceText(CharSequence text) { 2292d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return; 2302d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2312d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 2332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public boolean onKeyUp(int keyCode, KeyEvent event) { 2342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira switch (keyCode) { 2352d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira case KeyEvent.KEYCODE_ENTER: 2362d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira case KeyEvent.KEYCODE_DPAD_CENTER: 2372d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira case KeyEvent.KEYCODE_TAB: 2382d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira if (event.hasNoModifiers()) { 239078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira if (isPopupShowing()) { 240078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira // choose the first entry. 241078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira submitItemAtPosition(0); 242078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira dismissDropDown(); 2432d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return true; 2442d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } else { 2452d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int end = getSelectionEnd(); 2462d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int start = mTokenizer.findTokenStart(getText(), end); 2472d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira String text = getText().toString().substring(start, end); 2482d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira clearComposingText(); 2492d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2502d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Editable editable = getText(); 251f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira RecipientEntry entry = RecipientEntry.constructFakeEntry(text); 2522d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 253f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira editable.replace(start, end, createChip(entry)); 2542d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira dismissDropDown(); 2552d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2562d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2572d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2582d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return super.onKeyUp(keyCode, event); 2592d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2602d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2612d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void onChipChanged() { 2622d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // Must be posted so that the previous span 2632d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // is correctly replaced with the previous selection points. 2642d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mHandler.post(mDelayedSelectionMode); 2652d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2662d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2672d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 2682d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public boolean onKeyDown(int keyCode, KeyEvent event) { 2692d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int start = getSelectionStart(); 2702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int end = getSelectionEnd(); 2712d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Spannable span = getSpannable(); 2722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira RecipientChip[] chips = span.getSpans(start, end, RecipientChip.class); 2742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira if (chips != null) { 2752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira for (RecipientChip chip : chips) { 2762d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira chip.onKeyDown(keyCode, event); 2772d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2782d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2792d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2802d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira if (keyCode == KeyEvent.KEYCODE_ENTER && event.hasNoModifiers()) { 2812d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return true; 2822d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2832d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 2842d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return super.onKeyDown(keyCode, event); 2852d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2862d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 287078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira private Spannable getSpannable() { 2882d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return (Spannable) getText(); 2892d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 2902d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 291078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 292078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira @Override 293078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira public boolean onTouchEvent(MotionEvent event) { 294078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira int action = event.getAction(); 295b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira boolean handled = super.onTouchEvent(event); 29643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira boolean chipWasSelected = false; 2970f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira 298078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { 299078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira Spannable span = getSpannable(); 300078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira int offset = getOffsetForPosition(event.getX(), event.getY()); 30143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira int end = span.nextSpanTransition(offset, span.length(), RecipientChip.class); 30243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira int start = mTokenizer.findTokenStart(span, end); 303078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira RecipientChip[] chips = span.getSpans(start, end, RecipientChip.class); 304078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira if (chips != null && chips.length > 0) { 305078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira // Get the first chip that matched. 306078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira final RecipientChip currentChip = chips[0]; 307078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 308078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira if (action == MotionEvent.ACTION_UP) { 30943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (mSelectedChip != null && mSelectedChip != currentChip) { 31043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelectedChip.unselectChip(); 31143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelectedChip = currentChip.selectChip(); 31243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } else if (mSelectedChip == null) { 31343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelectedChip = currentChip.selectChip(); 31443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } else { 31543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelectedChip.onClick(this, event.getX(), event.getY()); 31643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 317078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 31843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira chipWasSelected = true; 31943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 32043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 32143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (!chipWasSelected) { 32243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (mSelectedChip != null) { 32343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelectedChip.unselectChip(); 32443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelectedChip = null; 325078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 326078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 327b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira return handled; 328078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 329078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 330f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira private CharSequence createChip(RecipientEntry entry) { 331078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira // We want to override the tokenizer behavior with our own ending 332078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira // token, space. 333f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira SpannableString chipText = new SpannableString(mTokenizer.terminateToken(entry 334f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira .getDisplayName())); 335078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira int end = getSelectionEnd(); 336078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira int start = mTokenizer.findTokenStart(getText(), end); 337078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira try { 338f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira chipText.setSpan(constructChipSpan(entry, start, false), 0, entry.getDisplayName() 339f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira .length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 340078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } catch (NullPointerException e) { 34143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Log.e(TAG, e.getMessage(), e); 342078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira return null; 343078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 344078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 345078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira return chipText; 346078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 347078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 348078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira @Override 349078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 350078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira submitItemAtPosition(position); 351078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 352078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 353078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira private void submitItemAtPosition(int position) { 354f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira RecipientEntry entry = (RecipientEntry) getAdapter().getItem(position); 355078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira clearComposingText(); 356078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 357078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira int end = getSelectionEnd(); 358078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira int start = mTokenizer.findTokenStart(getText(), end); 359078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 360078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira Editable editable = getText(); 361f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira editable.replace(start, end, createChip(entry)); 362078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 363078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 364078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira 3650f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira public Editable getRecipients() { 3660f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira StringBuilder plainText = new StringBuilder(); 3670f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira int size = mRecipients.size(); 3680f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira for (int i = 0; i < size; i++) { 3690f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira plainText.append(mRecipients.get(i).getValue()); 3700f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira if (i != size-1) { 3710f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira plainText.append(','); 3720f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira } 3730f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira } 3740f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira return Editable.Factory.getInstance().newEditable(plainText); 3750f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira } 3760f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira 377b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa /** Returns a collection of contact Id for each chip inside this View. */ 378b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa /* package */ Collection<Integer> getContactIds() { 379b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa final Set<Integer> result = new HashSet<Integer>(); 380b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa for (RecipientChip chip : mRecipients) { 381b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa result.add(chip.getContactId()); 382b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa } 383b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa return result; 384b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa } 385b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa 386b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa /** Returns a collection of data Id for each chip inside this View. May be null. */ 387b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa /* package */ Collection<Integer> getDataIds() { 388b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa final Set<Integer> result = new HashSet<Integer>(); 389b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa for (RecipientChip chip : mRecipients) { 390b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa result.add(chip.getDataId()); 391b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa } 392b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa return result; 393b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa } 394b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa 3952d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira /** 3962d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * RecipientChip defines an ImageSpan that contains information relevant to 3972d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * a particular recipient. 3982d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira */ 399b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira public class RecipientChip extends ImageSpan implements OnItemClickListener, OnDismissListener, 400b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira FilterListener { 4012d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private final CharSequence mDisplay; 4022d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4032d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private final CharSequence mValue; 4042d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4052d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private final int mOffset; 4062d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4072d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private ListPopupWindow mPopup; 4082d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4092d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private View mAnchorView; 4102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private int mLeft; 4122d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 413b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa private final int mContactId; 414b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa 415b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa private final int mDataId; 4162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 41743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private RecipientEntry mEntry; 41843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 41943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private boolean mSelected = false; 42043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 42143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private Rect mBounds; 422f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira public RecipientChip(Drawable drawable, RecipientEntry entry, int offset, Rect bounds) { 4232d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira super(drawable); 424f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira mDisplay = entry.getDisplayName(); 425f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira mValue = entry.getDestination(); 426b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa mContactId = entry.getContactId(); 427b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa mDataId = entry.getDataId(); 4282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mOffset = offset; 42943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mEntry = entry; 43043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mBounds = bounds; 431f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira 4322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mAnchorView = new View(getContext()); 4332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mAnchorView.setLeft(bounds.left); 4342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mAnchorView.setRight(bounds.left); 435f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira mAnchorView.setTop(bounds.bottom); 436f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira mAnchorView.setBottom(bounds.bottom); 4372d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mAnchorView.setVisibility(View.GONE); 4380f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira mRecipients.add(this); 4392d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4402d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 44143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira public void unselectChip() { 44243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (getChipStart() == -1 || getChipEnd() == -1) { 44343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelectedChip = null; 44443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return; 44543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 44643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira clearComposingText(); 44743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira RecipientChip newChipSpan = null; 44843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira try { 44943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira newChipSpan = constructChipSpan(mEntry, mOffset, false); 45043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } catch (NullPointerException e) { 45143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Log.e(TAG, e.getMessage(), e); 45243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return; 45343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 45443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira replace(newChipSpan); 45543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (mPopup != null && mPopup.isShowing()) { 45643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup.dismiss(); 45743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 45843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return; 45943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 46043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 4612d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void onKeyDown(int keyCode, KeyEvent event) { 4622d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira if (keyCode == KeyEvent.KEYCODE_DEL) { 4632d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira if (mPopup != null && mPopup.isShowing()) { 4642d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mPopup.dismiss(); 4652d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4662d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira removeChip(); 4672d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4682d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4692d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public boolean isCompletedContact() { 471b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa return mContactId != -1; 4722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira private void replace(RecipientChip newChip) { 4752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Spannable spannable = getSpannable(); 4762d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int spanStart = getChipStart(); 4772d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int spanEnd = getChipEnd(); 4782d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira QwertyKeyListener.markAsReplaced(getText(), spanStart, spanEnd, ""); 4792d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira spannable.removeSpan(this); 4800f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira mRecipients.remove(this); 4812d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira spannable.setSpan(newChip, spanStart, spanEnd, 0); 4822d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4832d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4842d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void removeChip() { 4852d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira Spannable spannable = getSpannable(); 4862d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int spanStart = getChipStart(); 4872d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int spanEnd = getChipEnd(); 48843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 4892d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira QwertyKeyListener.markAsReplaced(getText(), spanStart, spanEnd, ""); 4902d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira spannable.removeSpan(this); 4910f93fba1c7da8cb2d209e5e37831600c3b86852cMindy Pereira mRecipients.remove(this); 49243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira spannable.setSpan(null, spanStart, spanEnd, 0); 493b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira getText().delete(spanStart, spanEnd); 4942d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4952d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 4962d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public int getChipStart() { 4972d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return getSpannable().getSpanStart(this); 4982d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 4992d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 5002d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public int getChipEnd() { 5012d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return getSpannable().getSpanEnd(this); 5022d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5032d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 504b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira public void replaceChip(RecipientEntry entry) { 5052d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira clearComposingText(); 5062d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 507078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira RecipientChip newChipSpan = null; 508078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira try { 509b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira newChipSpan = constructChipSpan(entry, mOffset, false); 510078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } catch (NullPointerException e) { 51143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Log.e(TAG, e.getMessage(), e); 512078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira return; 513078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira } 5142d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira replace(newChipSpan); 5152d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira if (mPopup != null && mPopup.isShowing()) { 5162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mPopup.dismiss(); 5172d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5182d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira onChipChanged(); 5192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 52143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira public RecipientChip selectChip() { 52243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira clearComposingText(); 52343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira RecipientChip newChipSpan = null; 52443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (isCompletedContact()) { 52543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira try { 52643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira newChipSpan = constructChipSpan(mEntry, mOffset, true); 52743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira newChipSpan.setSelected(true); 52843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } catch (NullPointerException e) { 52943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Log.e(TAG, e.getMessage(), e); 53043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return newChipSpan; 53143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 53243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira replace(newChipSpan); 53343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (mPopup != null && mPopup.isShowing()) { 53443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup.dismiss(); 53543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 53643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelected = true; 53743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira // Make sure we call edit on the new chip span. 53843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira newChipSpan.showAlternates(); 53943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } else { 54043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira CharSequence text = getValue(); 54143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira removeChip(); 54243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira Editable editable = getText(); 54343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira setSelection(editable.length()); 54443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira editable.append(text); 54543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 54643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return newChipSpan; 54743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 54843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 54943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private void showAlternates() { 55043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup = new ListPopupWindow(RecipientEditTextView.this.getContext()); 55143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 55243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (!mPopup.isShowing()) { 55343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mAnchorView.setLeft(mLeft); 55443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mAnchorView.setRight(mLeft); 55543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup.setAnchorView(mAnchorView); 55643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira BaseRecipientAdapter adapter = (BaseRecipientAdapter) getAdapter(); 55743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira adapter.getFilter().filter(getValue(), this); 55843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup.setAdapter(adapter); 55943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira // TODO: get width from dimen.xml. 56043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup.setWidth(getWidth()); 56143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup.setOnItemClickListener(this); 56243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mPopup.setOnDismissListener(this); 56343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 56443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 56543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 56643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private void setSelected(boolean selected) { 56743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira mSelected = selected; 56843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 56943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 5702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public CharSequence getDisplay() { 5712d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return mDisplay; 5722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 5742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public CharSequence getValue() { 5752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira return mValue; 5762d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5772d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 57843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira private boolean isInDelete(float x, float y) { 57943876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira // Figure out the bounds of this chip and whether or not 58043876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira // the user clicked in the X portion. 58143876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return x > (mBounds.right - mChipDeleteWidth) && x < mBounds.right; 58243876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira } 58343876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira 58443876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira public void onClick(View widget, float x, float y) { 58543876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (mSelected) { 58643876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira if (isInDelete(x, y)) { 58743876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira removeChip(); 58843876560a507b0672cd2732c74bdbdec84ed3893Mindy Pereira return; 589f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira } 5902d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5912d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5922d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 5932d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 5942d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, 5952d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira int y, int bottom, Paint paint) { 5962d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mLeft = (int) x; 5972d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira super.draw(canvas, text, start, end, x, top, y, bottom, paint); 5982d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 5992d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 6002d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 6012d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) { 6022d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mPopup.dismiss(); 6032d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira clearComposingText(); 604b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira replaceChip((RecipientEntry) adapterView.getItemAtPosition(position)); 6052d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 6062d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira 6072d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira // When the popup dialog is dismissed, return the cursor to the end. 6082d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira @Override 6092d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira public void onDismiss() { 6102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira mHandler.post(mDelayedSelectionMode); 6112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 612b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira 613b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira @Override 614b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira public void onFilterComplete(int count) { 615b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira if (count > 0 && mPopup != null) { 616b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira mPopup.show(); 617b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira } 618b5dd668995d80a9bc9a6c5ed9f24a9d32a269634Mindy Pereira } 619b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa 620b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa public int getContactId() { 621b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa return mContactId; 622b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa } 623b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa 624b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa public int getDataId() { 625b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa return mDataId; 626b5ebb8916a1bb178062a0ccb59e80de27ec1bb2dDaisuke Miyakawa } 6272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira } 6282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira} 629