RecipientEditTextView.java revision 9b398e3019d1e12c661647a277a338975a50d952
19159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira/* 2194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy 39159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * Copyright (C) 2011 The Android Open Source Project 49159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * 59159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * Licensed under the Apache License, Version 2.0 (the "License"); 69159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * you may not use this file except in compliance with the License. 79159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * You may obtain a copy of the License at 89159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * 99159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * http://www.apache.org/licenses/LICENSE-2.0 109159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * 119159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * Unless required by applicable law or agreed to in writing, software 129159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * distributed under the License is distributed on an "AS IS" BASIS, 139159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 149159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * See the License for the specific language governing permissions and 159159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * limitations under the License. 169159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira */ 179159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira 189159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereirapackage com.android.ex.chips; 199159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira 201d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.app.Dialog; 211d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.content.ClipData; 2220c9d620e79ae28994856541761a951074551518Mindy Pereiraimport android.content.ClipDescription; 231d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.content.ClipboardManager; 249159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.content.Context; 251d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.content.DialogInterface; 261d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.content.DialogInterface.OnDismissListener; 2752c441e2c03e0f48572348953b985a4bf989c057Mindy Pereiraimport android.content.res.Resources; 2822faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereiraimport android.content.res.TypedArray; 29cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.Bitmap; 302bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereiraimport android.graphics.BitmapFactory; 31cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.Canvas; 322bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereiraimport android.graphics.Matrix; 33b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport android.graphics.Paint; 34e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Phamimport android.graphics.Point; 35cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.Rect; 362bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereiraimport android.graphics.RectF; 37cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.drawable.BitmapDrawable; 389159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.graphics.drawable.Drawable; 391852931de1e24e77cb708f4ba010eaa269426657Mindy Pereiraimport android.os.AsyncTask; 401650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedyimport android.os.Build; 41007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereiraimport android.os.Handler; 4278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedyimport android.os.Looper; 43007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereiraimport android.os.Message; 44dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereiraimport android.os.Parcelable; 459159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.text.Editable; 4661b48ccefc655549802556947eb8cf3959c6ddadGilles Debunneimport android.text.InputType; 47cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.Layout; 48cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.Spannable; 49cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.SpannableString; 500e128968f242866568bba0f833bb17ffda127f07Scott Kennedyimport android.text.SpannableStringBuilder; 51cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.Spanned; 52cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.TextPaint; 53cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.TextUtils; 54b88ee450829eb4ac24fb47c377b9ec3aab0782daMindy Pereiraimport android.text.TextWatcher; 55cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.method.QwertyKeyListener; 56cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.style.ImageSpan; 573bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereiraimport android.text.util.Rfc822Token; 583bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereiraimport android.text.util.Rfc822Tokenizer; 599159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.util.AttributeSet; 60cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.util.Log; 61093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindypimport android.util.TypedValue; 62fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereiraimport android.view.ActionMode; 63aa2afffe7aba707c2406f2e4503fa6037c4cd196Andy Stadlerimport android.view.ActionMode.Callback; 64e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Phamimport android.view.DragEvent; 651d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.view.GestureDetector; 66cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.view.KeyEvent; 67750e6e52d9e560d5fbf687f15bf388a947e98eb2Mindy Pereiraimport android.view.LayoutInflater; 68fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereiraimport android.view.Menu; 69fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereiraimport android.view.MenuItem; 70cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.view.MotionEvent; 71cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.view.View; 7261b48ccefc655549802556947eb8cf3959c6ddadGilles Debunneimport android.view.View.OnClickListener; 73c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereiraimport android.view.ViewParent; 7493364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereiraimport android.view.inputmethod.EditorInfo; 7593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereiraimport android.view.inputmethod.InputConnection; 76cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.widget.AdapterView; 77cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.widget.AdapterView.OnItemClickListener; 7880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylorimport android.widget.Button; 79093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindypimport android.widget.Filterable; 8095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereiraimport android.widget.ListAdapter; 81cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.widget.ListPopupWindow; 82007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereiraimport android.widget.ListView; 839159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.widget.MultiAutoCompleteTextView; 84c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereiraimport android.widget.ScrollView; 85750e6e52d9e560d5fbf687f15bf388a947e98eb2Mindy Pereiraimport android.widget.TextView; 867537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa 8716923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport com.android.ex.chips.RecipientAlternatesAdapter.RecipientMatchCallback; 88194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedyimport com.android.ex.chips.recipientchip.DrawableRecipientChip; 89194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedyimport com.android.ex.chips.recipientchip.InvisibleRecipientChip; 90194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedyimport com.android.ex.chips.recipientchip.VisibleRecipientChip; 91cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 9276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.ArrayList; 9376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Arrays; 9476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Collection; 9576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Collections; 9676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Comparator; 9776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.HashSet; 9876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.List; 9976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Map; 10076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Set; 10176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.regex.Matcher; 10276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.regex.Pattern; 10376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert 1049159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira/** 1059159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * RecipientEditTextView is an auto complete text view for use with applications 1069159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * that use the new Chips UI for addressing a message to recipients. 1079159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira */ 10895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereirapublic class RecipientEditTextView extends MultiAutoCompleteTextView implements 1091d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira OnItemClickListener, Callback, RecipientAlternatesAdapter.OnCheckedItemChangedListener, 110ecee50cc64d17d3cf7553a492dbf22f99f08aa56Mindy Pereira GestureDetector.OnGestureListener, OnDismissListener, OnClickListener, 1116a6bc7010f0f634ad32312d9802c50ed749f12bbMindy Pereira TextView.OnEditorActionListener { 112cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 113aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private static final char COMMIT_CHAR_COMMA = ','; 114aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 115aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private static final char COMMIT_CHAR_SEMICOLON = ';'; 116aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 117aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private static final char COMMIT_CHAR_SPACE = ' '; 118aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 11903e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy private static final String SEPARATOR = String.valueOf(COMMIT_CHAR_COMMA) 12003e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy + String.valueOf(COMMIT_CHAR_SPACE); 12103e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy 122cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private static final String TAG = "RecipientEditTextView"; 123cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 124aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private static int DISMISS = "dismiss".hashCode(); 125aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 126aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private static final long DISMISS_DELAY = 300; 127aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 12812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira // TODO: get correct number/ algorithm from with UX. 129f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // Visible for testing. 130f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira /*package*/ static final int CHIP_LIMIT = 2; 131f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 132f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira private static final int MAX_CHIPS_PARSED = 50; 13312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 134aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private static int sSelectedTextColor = -1; 135aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 136aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Resources for displaying chips. 137cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private Drawable mChipBackground = null; 138cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 139cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private Drawable mChipDelete = null; 140cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 141aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private Drawable mInvalidChipBackground; 142aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 143aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private Drawable mChipBackgroundPressed; 144aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 145aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private float mChipHeight; 146aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 147aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private float mChipFontSize; 148aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 149ffd270a55d80db66bd6a1d7b786443fdc00af372Mindy Pereira private float mLineSpacingExtra; 150ffd270a55d80db66bd6a1d7b786443fdc00af372Mindy Pereira 151cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private int mChipPadding; 152cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 153b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin /** 154b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * Enumerator for avatar position. See attr.xml for more details. 155c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein * 0 for end, 1 for start. 156b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin */ 157b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin private int mAvatarPosition; 158b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 159c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein private static final int AVATAR_POSITION_END = 0; 160b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 161c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein private static final int AVATAR_POSITION_START = 1; 162b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 1639b398e3019d1e12c661647a277a338975a50d952Kevin Lin /** 1649b398e3019d1e12c661647a277a338975a50d952Kevin Lin * Enumerator for image span alignment. See attr.xml for more details. 1659b398e3019d1e12c661647a277a338975a50d952Kevin Lin * 0 for bottom, 1 for baseline. 1669b398e3019d1e12c661647a277a338975a50d952Kevin Lin */ 1679b398e3019d1e12c661647a277a338975a50d952Kevin Lin private int mImageSpanAlignment; 1689b398e3019d1e12c661647a277a338975a50d952Kevin Lin 1699b398e3019d1e12c661647a277a338975a50d952Kevin Lin private static final int IMAGE_SPAN_ALIGNMENT_BOTTOM = 0; 1709b398e3019d1e12c661647a277a338975a50d952Kevin Lin 1719b398e3019d1e12c661647a277a338975a50d952Kevin Lin private static final int IMAGE_SPAN_ALIGNMENT_BASELINE = 1; 1729b398e3019d1e12c661647a277a338975a50d952Kevin Lin 1739b398e3019d1e12c661647a277a338975a50d952Kevin Lin 174b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin private boolean mDisableDelete; 175b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 176cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private Tokenizer mTokenizer; 177cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 178aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private Validator mValidator; 179cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 180194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private DrawableRecipientChip mSelectedChip; 181cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1822bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira private Bitmap mDefaultContactPhoto; 1832bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira 18412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira private ImageSpan mMoreChip; 18512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 186750e6e52d9e560d5fbf687f15bf388a947e98eb2Mindy Pereira private TextView mMoreItem; 18712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 18820c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy // VisibleForTesting 18920c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy final ArrayList<String> mPendingChips = new ArrayList<String>(); 190f97eb41a7946f2c3013ac74f2451b78070531125Mindy Pereira 191007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira private Handler mHandler; 192007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira 19302a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira private int mPendingChipsCount = 0; 19402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira 195f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira private boolean mNoChips = false; 196f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 19795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira private ListPopupWindow mAlternatesPopup; 19895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira 19901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira private ListPopupWindow mAddressPopup; 20001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira 20120c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy // VisibleForTesting 202194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy ArrayList<DrawableRecipientChip> mTemporaryRecipients; 2031852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira 204194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private ArrayList<DrawableRecipientChip> mRemovedSpans; 2053bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira 206076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira private boolean mShouldShrink = true; 207076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira 2081d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira // Chip copy fields. 2091d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira private GestureDetector mGestureDetector; 2101d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 2111d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira private Dialog mCopyDialog; 2121d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 2131d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira private String mCopyAddress; 2141d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 21595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira /** 216aa2afffe7aba707c2406f2e4503fa6037c4cd196Andy Stadler * Used with {@link #mAlternatesPopup}. Handles clicks to alternate addresses for a 217aa2afffe7aba707c2406f2e4503fa6037c4cd196Andy Stadler * selected chip. 21895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira */ 21995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira private OnItemClickListener mAlternatesListener; 22095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira 221c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira private int mCheckedItem; 222aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 22379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira private TextWatcher mTextWatcher; 22479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira 225aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Obtain the enclosing scroll view, if it exists, so that the view can be 226aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // scrolled to show the last line of chips content. 227c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira private ScrollView mScrollView; 228c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira 229aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira private boolean mTriedGettingScrollView; 230c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira 231e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham private boolean mDragEnabled = false; 232e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2334491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor // This pattern comes from android.util.Patterns. It has been tweaked to handle a "1" before 2344491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor // parens, so numbers such as "1 (425) 222-2342" match. 2354491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor private static final Pattern PHONE_PATTERN 2364491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor = Pattern.compile( // sdd = space, dot, or dash 2374491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor "(\\+[0-9]+[\\- \\.]*)?" // +<digits><sdd>* 2384491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor + "(1?[ ]*\\([0-9]+\\)[\\- \\.]*)?" // 1(<digits>)<sdd>* 2394491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit> 2404491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor 24179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira private final Runnable mAddTextWatcher = new Runnable() { 24279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira @Override 24379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira public void run() { 24479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira if (mTextWatcher == null) { 24579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira mTextWatcher = new RecipientTextWatcher(); 24679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira addTextChangedListener(mTextWatcher); 24779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 24879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 24979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira }; 25079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira 2511852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira private IndividualReplacementTask mIndividualReplacements; 2521852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira 2532cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira private Runnable mHandlePendingChips = new Runnable() { 2542cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira 2552cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira @Override 2562cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira public void run() { 2572cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira handlePendingChips(); 2582cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } 2592cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira 2602cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira }; 2612cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira 262d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira private Runnable mDelayedShrink = new Runnable() { 263d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira 264d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira @Override 265d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira public void run() { 266d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira shrink(); 267d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira } 268d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira 269d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira }; 270d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira 2715e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp private int mMaxLines; 2725e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp 273093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp private static int sExcessTopPadding = -1; 274093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp 275093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp private int mActionBarHeight; 276093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp 277fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler private boolean mAttachedToWindow; 278fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler 279b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin private DropdownChipLayouter mDropdownChipLayouter; 280b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 2819159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira public RecipientEditTextView(Context context, AttributeSet attrs) { 2829159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira super(context, attrs); 28322faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira setChipDimensions(context, attrs); 284e50b0a1f168322390b63f435f222766cdae6ba7dMindy Pereira if (sSelectedTextColor == -1) { 285e50b0a1f168322390b63f435f222766cdae6ba7dMindy Pereira sSelectedTextColor = context.getResources().getColor(android.R.color.white); 286e50b0a1f168322390b63f435f222766cdae6ba7dMindy Pereira } 28795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mAlternatesPopup = new ListPopupWindow(context); 28801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira mAddressPopup = new ListPopupWindow(context); 289bfedc1e199e57dcda494389fdca0750e1f165135Mindy Pereira mCopyDialog = new Dialog(context); 29095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mAlternatesListener = new OnItemClickListener() { 29195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira @Override 29295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira public void onItemClick(AdapterView<?> adapterView,View view, int position, 29395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira long rowId) { 29421cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira mAlternatesPopup.setOnItemClickListener(null); 29595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira replaceChip(mSelectedChip, ((RecipientAlternatesAdapter) adapterView.getAdapter()) 29695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira .getRecipientEntry(position)); 29795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Message delayed = Message.obtain(mHandler, DISMISS); 29821cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira delayed.obj = mAlternatesPopup; 29995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mHandler.sendMessageDelayed(delayed, DISMISS_DELAY); 30095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira clearComposingText(); 30195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 30295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira }; 303e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 304b88ee450829eb4ac24fb47c377b9ec3aab0782daMindy Pereira setOnItemClickListener(this); 305fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira setCustomSelectionActionModeCallback(this); 306007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira mHandler = new Handler() { 307007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira @Override 308007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira public void handleMessage(Message msg) { 309007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira if (msg.what == DISMISS) { 31095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira ((ListPopupWindow) msg.obj).dismiss(); 311007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira return; 312007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira } 313007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira super.handleMessage(msg); 314007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira } 315007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira }; 3164e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira mTextWatcher = new RecipientTextWatcher(); 3174e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira addTextChangedListener(mTextWatcher); 3181d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mGestureDetector = new GestureDetector(context, this); 31993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira setOnEditorActionListener(this); 320b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 321b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin setDropdownChipLayouter(new DropdownChipLayouter(LayoutInflater.from(context), context)); 322b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 323b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 324b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin protected void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) { 325b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin mDropdownChipLayouter = dropdownChipLayouter; 32693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } 32793364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira 32893364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira @Override 329fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler protected void onDetachedFromWindow() { 330fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler mAttachedToWindow = false; 331fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler } 332fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler 333fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler @Override 334fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler protected void onAttachedToWindow() { 335fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler mAttachedToWindow = true; 336fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler } 337fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler 338fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler @Override 33993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira public boolean onEditorAction(TextView view, int action, KeyEvent keyEvent) { 34093364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira if (action == EditorInfo.IME_ACTION_DONE) { 34193364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira if (commitDefault()) { 34293364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira return true; 34393364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } 34493364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira if (mSelectedChip != null) { 34593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira clearSelectedChip(); 34693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira return true; 34793364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } else if (focusNext()) { 34893364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira return true; 34993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } 35093364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } 35193364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira return false; 35293364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } 35393364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira 35493364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira @Override 35593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 35693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira InputConnection connection = super.onCreateInputConnection(outAttrs); 35793364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira int imeActions = outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION; 35893364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira if ((imeActions&EditorInfo.IME_ACTION_DONE) != 0) { 35993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira // clear the existing action 36093364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira outAttrs.imeOptions ^= imeActions; 36193364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira // set the DONE action 36293364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE; 36393364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } 36493364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { 36593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION; 36693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira } 3671af243fdc3b87979252bddc06097c5056ef93894Scott Kennedy 3681af243fdc3b87979252bddc06097c5056ef93894Scott Kennedy outAttrs.actionId = EditorInfo.IME_ACTION_DONE; 369a98b8e49be8e1ff1938ab204d86372e292c78194Mindy Pereira outAttrs.actionLabel = getContext().getString(R.string.done); 37093364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira return connection; 371e33555f13a9b05d835cb860e2c30ef40af3c8502Erik } 3727afe160db4a48f66c964ced89e29e0b63b23c7c1Mindy Pereira 373194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy /*package*/ DrawableRecipientChip getLastChip() { 374194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip last = null; 375194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = getSortedRecipients(); 376aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira if (chips != null && chips.length > 0) { 377aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira last = chips[chips.length - 1]; 378e33555f13a9b05d835cb860e2c30ef40af3c8502Erik } 379aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira return last; 380fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira } 381fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira 382fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira @Override 383fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira public void onSelectionChanged(int start, int end) { 384fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira // When selection changes, see if it is inside the chips area. 385fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira // If so, move the cursor back after the chips again. 386194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip last = getLastChip(); 387f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (last != null && start < getSpannable().getSpanEnd(last)) { 388aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Grab the last chip and set the cursor to after it. 389aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira setSelection(Math.min(getSpannable().getSpanEnd(last) + 1, getText().length())); 39005dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira } 39105dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira super.onSelectionChanged(start, end); 39205dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira } 39305dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira 394dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira @Override 395dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira public void onRestoreInstanceState(Parcelable state) { 396dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira if (!TextUtils.isEmpty(getText())) { 397dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira super.onRestoreInstanceState(null); 398dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira } else { 399dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira super.onRestoreInstanceState(state); 400dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira } 401dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira } 402dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira 403aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira @Override 404daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira public Parcelable onSaveInstanceState() { 405daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira // If the user changes orientation while they are editing, just roll back the selection. 406daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira clearSelectedChip(); 407daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira return super.onSaveInstanceState(); 408daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira } 409daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira 41002a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira /** 41102a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira * Convenience method: Append the specified text slice to the TextView's 41202a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira * display buffer, upgrading it to BufferType.EDITABLE if it was 41302a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira * not already editable. Commas are excluded as they are added automatically 41402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira * by the view. 41502a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira */ 41602a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira @Override 41702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira public void append(CharSequence text, int start, int end) { 418e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira // We don't care about watching text changes while appending. 419e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira if (mTextWatcher != null) { 420e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira removeTextChangedListener(mTextWatcher); 421e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira } 42202a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira super.append(text, start, end); 42302a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira if (!TextUtils.isEmpty(text) && TextUtils.getTrimmedLength(text) > 0) { 4240124468e712b4098ea240b8d45f84e52826b293dmindyp String displayString = text.toString(); 42503e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy 42603e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy if (!displayString.trim().endsWith(String.valueOf(COMMIT_CHAR_COMMA))) { 42703e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy // We have no separator, so we should add it 42803e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy super.append(SEPARATOR, 0, SEPARATOR.length()); 42903e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy displayString += SEPARATOR; 4300124468e712b4098ea240b8d45f84e52826b293dmindyp } 43103e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy 43220d7af71188a4a2d94c8d9edd7eff7879d6df4c4mindyp if (!TextUtils.isEmpty(displayString) 43302a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira && TextUtils.getTrimmedLength(displayString) > 0) { 43402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira mPendingChipsCount++; 43503e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy mPendingChips.add(displayString); 43602a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira } 43702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira } 438abb864d610bdd171d6b0dfd2e83648952155e6ebmindyp // Put a message on the queue to make sure we ALWAYS handle pending 439abb864d610bdd171d6b0dfd2e83648952155e6ebmindyp // chips. 4402cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira if (mPendingChipsCount > 0) { 4412cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira postHandlePendingChips(); 4422cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } 4434e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira mHandler.post(mAddTextWatcher); 44402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira } 44502a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira 44605dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira @Override 44705dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira public void onFocusChanged(boolean hasFocus, int direction, Rect previous) { 448d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira super.onFocusChanged(hasFocus, direction, previous); 44905dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira if (!hasFocus) { 45012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira shrink(); 451fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira } else { 45212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira expand(); 453fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira } 4549159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira } 4559159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira 456093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp private int getExcessTopPadding() { 457093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp if (sExcessTopPadding == -1) { 458e7f7a7d521b6b369035370b6c9241d2c6e313b44mindyp sExcessTopPadding = (int) (mChipHeight + mLineSpacingExtra); 459093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } 460093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp return sExcessTopPadding; 461093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } 462093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp 4636b7110f320c978c368c28bdb06212c6a6df12f1fAlice Yang @Override 464093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp public <T extends ListAdapter & Filterable> void setAdapter(T adapter) { 465093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp super.setAdapter(adapter); 466b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin BaseRecipientAdapter baseAdapter = (BaseRecipientAdapter) adapter; 467b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin baseAdapter.registerUpdateObserver(new BaseRecipientAdapter.EntriesUpdatedObserver() { 468b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin @Override 469b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin public void onChanged(List<RecipientEntry> entries) { 470b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // Scroll the chips field to the top of the screen so 471b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // that the user can see as many results as possible. 472b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (entries != null && entries.size() > 0) { 473b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin scrollBottomIntoView(); 474b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 475b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 476b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin }); 477b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin baseAdapter.setDropdownChipLayouter(mDropdownChipLayouter); 478093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } 479093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp 48053e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin protected void scrollBottomIntoView() { 481093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp if (mScrollView != null && mShouldShrink) { 482093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp int[] location = new int[2]; 483093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp getLocationOnScreen(location); 484093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp int height = getHeight(); 485093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp int currentPos = location[1] + height; 486093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp // Desired position shows at least 1 line of chips below the action 487093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp // bar. We add excess padding to make sure this is always below other 488093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp // content. 489093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp int desiredPos = (int) mChipHeight + mActionBarHeight + getExcessTopPadding(); 490093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp if (currentPos > desiredPos) { 491093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp mScrollView.scrollBy(0, currentPos - desiredPos); 492093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } 493093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } 494093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } 495093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp 49653e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin protected ScrollView getScrollView() { 4977a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy return mScrollView; 49853e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin } 49953e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin 500d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira @Override 501d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira public void performValidation() { 502d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira // Do nothing. Chips handles its own validation. 503d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira } 504d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira 50512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira private void shrink() { 5065c125afa54288595524c85182031421cbad08ac3Mindy Pereira if (mTokenizer == null) { 5075c125afa54288595524c85182031421cbad08ac3Mindy Pereira return; 5085c125afa54288595524c85182031421cbad08ac3Mindy Pereira } 5095e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp long contactId = mSelectedChip != null ? mSelectedChip.getEntry().getContactId() : -1; 5105e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (mSelectedChip != null && contactId != RecipientEntry.INVALID_CONTACT 5115e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp && (!isPhoneQuery() && contactId != RecipientEntry.GENERATED_CONTACT)) { 51212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira clearSelectedChip(); 51312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } else { 514d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira if (getWidth() <= 0) { 515d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // We don't have the width yet which means the view hasn't been drawn yet 516d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // and there is no reason to attempt to commit chips yet. 517d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // This focus lost must be the result of an orientation change 518d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // or an initial rendering. 519d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // Re-post the shrink for later. 520d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira mHandler.removeCallbacks(mDelayedShrink); 521d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira mHandler.post(mDelayedShrink); 522d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira return; 523d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira } 524e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira // Reset any pending chips as they would have been handled 525e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira // when the field lost focus. 526e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira if (mPendingChipsCount > 0) { 5272cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira postHandlePendingChips(); 528e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira } else { 529e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira Editable editable = getText(); 530e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira int end = getSelectionEnd(); 531e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira int start = mTokenizer.findTokenStart(editable, end); 532194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = 533194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy getSpannable().getSpans(start, end, DrawableRecipientChip.class); 534e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira if ((chips == null || chips.length == 0)) { 535d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira Editable text = getText(); 536d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira int whatEnd = mTokenizer.findTokenEnd(text, start); 537d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // This token was already tokenized, so skip past the ending token. 538448e90b97a4df8102d6e1d2039274d9ea188dff9Mindy Pereira if (whatEnd < text.length() && text.charAt(whatEnd) == ',') { 5394f82d888c680a61b95373740ce68bfb48a242617mindyp whatEnd = movePastTerminators(whatEnd); 540d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira } 541e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira // In the middle of chip; treat this as an edit 542e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira // and commit the whole token. 543d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira int selEnd = getSelectionEnd(); 5446337231a660b717cd6f5c40d524f4aabfcc865b0Mindy Pereira if (whatEnd != selEnd) { 545e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira handleEdit(start, whatEnd); 546e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira } else { 547e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira commitChip(start, end, editable); 548e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira } 549e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira } 550a74f40dc1073117349d1d39b7c8396a39d24f57fMindy Pereira } 551e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira mHandler.post(mAddTextWatcher); 55212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 5533bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira createMoreChip(); 55412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 55512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 55612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira private void expand() { 557374ccb2935ea793d2fbee54ec5fe6d71af934d27mindyp if (mShouldShrink) { 558374ccb2935ea793d2fbee54ec5fe6d71af934d27mindyp setMaxLines(Integer.MAX_VALUE); 559374ccb2935ea793d2fbee54ec5fe6d71af934d27mindyp } 56012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira removeMoreChip(); 56112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira setCursorVisible(true); 56212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira Editable text = getText(); 56312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira setSelection(text != null && text.length() > 0 ? text.length() : 0); 5641852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // If there are any temporary chips, try replacing them now that the user 5651852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // has expanded the field. 5661852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0) { 5671852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira new RecipientReplacementTask().execute(); 5681852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira mTemporaryRecipients = null; 5691852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 57012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 57112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 5722bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira private CharSequence ellipsizeText(CharSequence text, TextPaint paint, float maxWidth) { 5736e8e8e8165a797611f80a2c17249147333d55ea7Mindy Pereira paint.setTextSize(mChipFontSize); 57402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira if (maxWidth <= 0 && Log.isLoggable(TAG, Log.DEBUG)) { 57502a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira Log.d(TAG, "Max width is negative: " + maxWidth); 57602a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira } 57702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira return TextUtils.ellipsize(text, paint, maxWidth, 57802a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira TextUtils.TruncateAt.END); 5792bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } 580cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 581b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin /** 582b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * Creates a bitmap of the given contact on a selected chip. 583b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * 584b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * @param contact The recipient entry to pull data from. 585b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * @param paint The paint to use to draw the bitmap. 586b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin */ 587f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy private Bitmap createSelectedChip(RecipientEntry contact, TextPaint paint) { 588b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin paint.setColor(sSelectedTextColor); 589b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin Bitmap photo; 590b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (mDisableDelete) { 591b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // Show the avatar instead if we don't want to delete 592b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin photo = getAvatarIcon(contact); 5932bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } else { 594b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin photo = ((BitmapDrawable) mChipDelete).getBitmap(); 5952bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } 596b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return createChipBitmap(contact, paint, photo, mChipBackgroundPressed); 5972bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } 598cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 599b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin /** 600b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * Creates a bitmap of the given contact on a selected chip. 601b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * 602b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * @param contact The recipient entry to pull data from. 603b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * @param paint The paint to use to draw the bitmap. 604b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin */ 605b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // TODO: Is leaveBlankIconSpacer obsolete now that we have left and right attributes? 606f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy private Bitmap createUnselectedChip(RecipientEntry contact, TextPaint paint, 6075e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp boolean leaveBlankIconSpacer) { 608b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin Drawable background = getChipBackground(contact); 609b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin Bitmap photo = getAvatarIcon(contact); 610b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin paint.setColor(getContext().getResources().getColor(android.R.color.black)); 611b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return createChipBitmap(contact, paint, photo, background); 612b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 613b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 614b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin private Bitmap createChipBitmap(RecipientEntry contact, TextPaint paint, Bitmap icon, 615b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin Drawable background) { 616b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (background == null) { 617b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin Log.w(TAG, "Unable to draw a background for the chips as it was never set"); 618c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein return Bitmap.createBitmap( 619c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein (int) mChipHeight * 2, (int) mChipHeight, Bitmap.Config.ARGB_8888); 620b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 621b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 622b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin Rect backgroundPadding = new Rect(); 623b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin background.getPadding(backgroundPadding); 624b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 625cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Ellipsize the text so that it takes AT MOST the entire width of the 626cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // autocomplete text entry area. Make sure to leave space for padding 627cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // on the sides. 6286e8e8e8165a797611f80a2c17249147333d55ea7Mindy Pereira int height = (int) mChipHeight; 629b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // Since the icon is a square, it's width is equal to the maximum height it can be inside 630b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // the chip. 631b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin int iconWidth = height - backgroundPadding.top - backgroundPadding.bottom; 6326ed7ded9deabbc92ed8341cf922673fd4626ba7eMindy Pereira float[] widths = new float[1]; 6336ed7ded9deabbc92ed8341cf922673fd4626ba7eMindy Pereira paint.getTextWidths(" ", widths); 634aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira CharSequence ellipsizedText = ellipsizeText(createChipDisplayText(contact), paint, 6359b398e3019d1e12c661647a277a338975a50d952Kevin Lin calculateAvailableWidth() - iconWidth - widths[0] - backgroundPadding.left 6369b398e3019d1e12c661647a277a338975a50d952Kevin Lin - backgroundPadding.right);; 637b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin int textWidth = (int) paint.measureText(ellipsizedText, 0, ellipsizedText.length()); 638b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 6399d2a1980bbcad5dae3b0fb03c35208724b377fa8Mindy Pereira // Make sure there is a minimum chip width so the user can ALWAYS 6409d2a1980bbcad5dae3b0fb03c35208724b377fa8Mindy Pereira // tap a chip without difficulty. 641b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin int width = Math.max(iconWidth * 2, textWidth + (mChipPadding * 2) + iconWidth 642b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin + backgroundPadding.left + backgroundPadding.right); 643cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 644cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Create the background of the chip. 645cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira Bitmap tmpBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 646cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira Canvas canvas = new Canvas(tmpBitmap); 6476b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira 648b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // Draw the background drawable 649b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin background.setBounds(0, 0, width, height); 650b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin background.draw(canvas); 651b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // Draw the text vertically aligned 652c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein int textX = shouldPositionAvatarOnRight() ? 653b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin mChipPadding + backgroundPadding.left : 654b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin width - backgroundPadding.right - mChipPadding - textWidth; 655b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), 656b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin textX, getTextYOffset(ellipsizedText.toString(), paint, height), paint); 657b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (icon != null) { 658b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // Draw the icon 659c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein int iconX = shouldPositionAvatarOnRight() ? 660b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin width - backgroundPadding.right - iconWidth : 661b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin backgroundPadding.left; 662b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin RectF src = new RectF(0, 0, icon.getWidth(), icon.getHeight()); 663b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin RectF dst = new RectF(iconX, 664b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 0 + backgroundPadding.top, 665b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin iconX + iconWidth, 666b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin height - backgroundPadding.bottom); 667b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin drawIconOnCanvas(icon, canvas, paint, src, dst); 6682bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } 6692bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira return tmpBitmap; 6702bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } 671cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 672aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira /** 673c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein * Returns true if the avatar should be positioned at the right edge of the chip. 674c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein * Takes into account both the set avatar position (start or end) as well as whether 675c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein * the layout direction is LTR or RTL. 676c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein */ 677c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein private boolean shouldPositionAvatarOnRight() { 678c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein final boolean isRtl = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 ? 679c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein getLayoutDirection() == LAYOUT_DIRECTION_RTL : false; 680c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein final boolean assignedPosition = mAvatarPosition == AVATAR_POSITION_END; 681c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein // If in Rtl mode, the position should be flipped. 682c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein return isRtl ? !assignedPosition : assignedPosition; 683c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein } 684c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein 685c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein /** 686b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * Returns the avatar icon to use for this recipient entry. Returns null if we don't want to 687b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * draw an icon for this recipient. 688b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin */ 689b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin private Bitmap getAvatarIcon(RecipientEntry contact) { 690b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // Don't draw photos for recipients that have been typed in OR generated on the fly. 691b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin long contactId = contact.getContactId(); 692b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin boolean drawPhotos = isPhoneQuery() ? 693b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin contactId != RecipientEntry.INVALID_CONTACT 694b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin : (contactId != RecipientEntry.INVALID_CONTACT 695b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin && (contactId != RecipientEntry.GENERATED_CONTACT && 696b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin !TextUtils.isEmpty(contact.getDisplayName()))); 697b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 698b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (drawPhotos) { 699b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin byte[] photoBytes = contact.getPhotoBytes(); 700b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // There may not be a photo yet if anything but the first contact address 701b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // was selected. 702b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (photoBytes == null && contact.getPhotoThumbnailUri() != null) { 703b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // TODO: cache this in the recipient entry? 704b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin getAdapter().fetchPhoto(contact, contact.getPhotoThumbnailUri()); 705b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin photoBytes = contact.getPhotoBytes(); 706b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 707b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (photoBytes != null) { 708b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return BitmapFactory.decodeByteArray(photoBytes, 0, photoBytes.length); 709b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } else { 710b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin // TODO: can the scaled down default photo be cached? 711b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return mDefaultContactPhoto; 712b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 713b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 714b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 715b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return null; 716b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 717b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 718b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin /** 719aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira * Get the background drawable for a RecipientChip. 720aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira */ 721aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Visible for testing. 722f30a42800318f6790d55421f8f6980eb38db4d3cmindyp /* package */Drawable getChipBackground(RecipientEntry contact) { 723f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return contact.isValid() ? mChipBackground : mInvalidChipBackground; 724aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } 725aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 726b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin /** 727b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * Given a height, returns a Y offset that will draw the text in the middle of the height. 728b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin */ 729b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin protected float getTextYOffset(String text, TextPaint paint, int height) { 7301d37d9775446acd8e4e6bb0637565e88b5360096Mindy Pereira Rect bounds = new Rect(); 731e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham paint.getTextBounds(text, 0, text.length(), bounds); 7328ff03734c31831d3d60b0cb2707c006dbf64d330Mindy Pereira int textHeight = bounds.bottom - bounds.top ; 7338ff03734c31831d3d60b0cb2707c006dbf64d330Mindy Pereira return height - ((height - textHeight) / 2) - (int)paint.descent(); 7341d37d9775446acd8e4e6bb0637565e88b5360096Mindy Pereira } 7351d37d9775446acd8e4e6bb0637565e88b5360096Mindy Pereira 736b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin /** 737b8985b7b595e38518d2a0657b89ff47bd34862abScott Kennedy * Draws the icon onto the canvas given the source rectangle of the bitmap and the destination 738b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin * rectangle of the canvas. 739b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin */ 740b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin protected void drawIconOnCanvas(Bitmap icon, Canvas canvas, Paint paint, RectF src, RectF dst) { 741b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin Matrix matrix = new Matrix(); 742b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin matrix.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); 743b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin canvas.drawBitmap(icon, matrix, paint); 744b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 745b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 746194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private DrawableRecipientChip constructChipSpan(RecipientEntry contact, boolean pressed, 7475e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp boolean leaveIconSpace) throws NullPointerException { 7482bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira if (mChipBackground == null) { 7492bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira throw new NullPointerException( 7502bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira "Unable to render any chips as setChipDimensions was not called."); 751cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 75202a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira 7532bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira TextPaint paint = getPaint(); 7542bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira float defaultSize = paint.getTextSize(); 755e50b0a1f168322390b63f435f222766cdae6ba7dMindy Pereira int defaultColor = paint.getColor(); 7562bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira 7572bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira Bitmap tmpBitmap; 7582bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira if (pressed) { 759f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy tmpBitmap = createSelectedChip(contact, paint); 760cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 7612bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } else { 762f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy tmpBitmap = createUnselectedChip(contact, paint, leaveIconSpace); 7632bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira } 764cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 765cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Pass the full text, un-ellipsized, to the chip. 766cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira Drawable result = new BitmapDrawable(getResources(), tmpBitmap); 7672bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira result.setBounds(0, 0, tmpBitmap.getWidth(), tmpBitmap.getHeight()); 7689b398e3019d1e12c661647a277a338975a50d952Kevin Lin DrawableRecipientChip recipientChip = 7699b398e3019d1e12c661647a277a338975a50d952Kevin Lin new VisibleRecipientChip(result, contact, getImageSpanAlignment()); 770cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Return text to the original size. 771cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira paint.setTextSize(defaultSize); 772e50b0a1f168322390b63f435f222766cdae6ba7dMindy Pereira paint.setColor(defaultColor); 773cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return recipientChip; 7749159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira } 7759159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira 7769b398e3019d1e12c661647a277a338975a50d952Kevin Lin private int getImageSpanAlignment() { 7779b398e3019d1e12c661647a277a338975a50d952Kevin Lin switch (mImageSpanAlignment) { 7789b398e3019d1e12c661647a277a338975a50d952Kevin Lin case IMAGE_SPAN_ALIGNMENT_BASELINE: 7799b398e3019d1e12c661647a277a338975a50d952Kevin Lin return ImageSpan.ALIGN_BASELINE; 7809b398e3019d1e12c661647a277a338975a50d952Kevin Lin case IMAGE_SPAN_ALIGNMENT_BOTTOM: 7819b398e3019d1e12c661647a277a338975a50d952Kevin Lin return ImageSpan.ALIGN_BOTTOM; 7829b398e3019d1e12c661647a277a338975a50d952Kevin Lin default: 7839b398e3019d1e12c661647a277a338975a50d952Kevin Lin return ImageSpan.ALIGN_BOTTOM; 7849b398e3019d1e12c661647a277a338975a50d952Kevin Lin } 7859b398e3019d1e12c661647a277a338975a50d952Kevin Lin } 7869b398e3019d1e12c661647a277a338975a50d952Kevin Lin 787a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 788342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * Calculate the bottom of the line the chip will be located on using: 789342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * 1) which line the chip appears on 79021625f86828b6bbfc5e87796564eaca5127155feMindy Pereira * 2) the height of a chip 79121625f86828b6bbfc5e87796564eaca5127155feMindy Pereira * 3) padding built into the edit text view 792a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 79381fd3d1ed9ea08706e297a227fcab10eac2cf0e3Mindy Pereira private int calculateOffsetFromBottom(int line) { 79421625f86828b6bbfc5e87796564eaca5127155feMindy Pereira // Line offsets start at zero. 795c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira int actualLine = getLineCount() - (line + 1); 7961d37d9775446acd8e4e6bb0637565e88b5360096Mindy Pereira return -((actualLine * ((int) mChipHeight) + getPaddingBottom()) + getPaddingTop()) 7971d37d9775446acd8e4e6bb0637565e88b5360096Mindy Pereira + getDropDownVerticalOffset(); 798f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira } 799f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira 800a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 801a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * Get the max amount of space a chip can take up. The formula takes into 802a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * account the width of the EditTextView, any view padding, and padding 803a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * that will be added to the chip. 804a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 805f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy private float calculateAvailableWidth() { 8062bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira return getWidth() - getPaddingLeft() - getPaddingRight() - (mChipPadding * 2); 8079159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira } 8089159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira 80952c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira 81022faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira private void setChipDimensions(Context context, AttributeSet attrs) { 8115da0234c9a7108d3386039816c7469753b79c307Mindy Pereira TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecipientEditTextView, 0, 8125da0234c9a7108d3386039816c7469753b79c307Mindy Pereira 0); 81352c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira Resources r = getContext().getResources(); 8148c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp 81522faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira mChipBackground = a.getDrawable(R.styleable.RecipientEditTextView_chipBackground); 81622faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira if (mChipBackground == null) { 81722faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira mChipBackground = r.getDrawable(R.drawable.chip_background); 81822faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira } 8195da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipBackgroundPressed = a 8205da0234c9a7108d3386039816c7469753b79c307Mindy Pereira .getDrawable(R.styleable.RecipientEditTextView_chipBackgroundPressed); 8215da0234c9a7108d3386039816c7469753b79c307Mindy Pereira if (mChipBackgroundPressed == null) { 8225da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipBackgroundPressed = r.getDrawable(R.drawable.chip_background_selected); 8235da0234c9a7108d3386039816c7469753b79c307Mindy Pereira } 8245da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipDelete = a.getDrawable(R.styleable.RecipientEditTextView_chipDelete); 8255da0234c9a7108d3386039816c7469753b79c307Mindy Pereira if (mChipDelete == null) { 8265da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipDelete = r.getDrawable(R.drawable.chip_delete); 8275da0234c9a7108d3386039816c7469753b79c307Mindy Pereira } 8285da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipPadding = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipPadding, -1); 8295da0234c9a7108d3386039816c7469753b79c307Mindy Pereira if (mChipPadding == -1) { 8305da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipPadding = (int) r.getDimension(R.dimen.chip_padding); 8315da0234c9a7108d3386039816c7469753b79c307Mindy Pereira } 8325da0234c9a7108d3386039816c7469753b79c307Mindy Pereira 8335da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mDefaultContactPhoto = BitmapFactory.decodeResource(r, R.drawable.ic_contact_picture); 8345da0234c9a7108d3386039816c7469753b79c307Mindy Pereira 83552c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira mMoreItem = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.more_item, null); 8365da0234c9a7108d3386039816c7469753b79c307Mindy Pereira 8375da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipHeight = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipHeight, -1); 8385da0234c9a7108d3386039816c7469753b79c307Mindy Pereira if (mChipHeight == -1) { 8395da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipHeight = r.getDimension(R.dimen.chip_height); 8405da0234c9a7108d3386039816c7469753b79c307Mindy Pereira } 8415da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipFontSize = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipFontSize, -1); 8425da0234c9a7108d3386039816c7469753b79c307Mindy Pereira if (mChipFontSize == -1) { 8435da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mChipFontSize = r.getDimension(R.dimen.chip_text_size); 8445da0234c9a7108d3386039816c7469753b79c307Mindy Pereira } 8455da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mInvalidChipBackground = a 8465da0234c9a7108d3386039816c7469753b79c307Mindy Pereira .getDrawable(R.styleable.RecipientEditTextView_invalidChipBackground); 8475da0234c9a7108d3386039816c7469753b79c307Mindy Pereira if (mInvalidChipBackground == null) { 8485da0234c9a7108d3386039816c7469753b79c307Mindy Pereira mInvalidChipBackground = r.getDrawable(R.drawable.chip_background_invalid); 8495da0234c9a7108d3386039816c7469753b79c307Mindy Pereira } 850b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin mAvatarPosition = a.getInt(R.styleable.RecipientEditTextView_avatarPosition, 0); 8519b398e3019d1e12c661647a277a338975a50d952Kevin Lin mImageSpanAlignment = a.getInt(R.styleable.RecipientEditTextView_imageSpanAlignment, 0); 852b8985b7b595e38518d2a0657b89ff47bd34862abScott Kennedy mDisableDelete = a.getBoolean(R.styleable.RecipientEditTextView_disableDelete, false); 853b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 854f30a42800318f6790d55421f8f6980eb38db4d3cmindyp mLineSpacingExtra = r.getDimension(R.dimen.line_spacing_extra); 855f30a42800318f6790d55421f8f6980eb38db4d3cmindyp mMaxLines = r.getInteger(R.integer.chips_max_lines); 856093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp TypedValue tv = new TypedValue(); 857093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { 858093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources() 859093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp .getDisplayMetrics()); 860093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } 861b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 862ef79800ff65490ec91ce77f55c4ade142d1cc4b2Mindy Pereira a.recycle(); 86352c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira } 86452c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira 865d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // Visible for testing. 866d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira /* package */ void setMoreItem(TextView moreItem) { 867d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira mMoreItem = moreItem; 868d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira } 869d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira 87097cb25912dab282cf732757f68b0405ed005f00bMindy Pereira 87197cb25912dab282cf732757f68b0405ed005f00bMindy Pereira // Visible for testing. 87297cb25912dab282cf732757f68b0405ed005f00bMindy Pereira /* package */ void setChipBackground(Drawable chipBackground) { 87397cb25912dab282cf732757f68b0405ed005f00bMindy Pereira mChipBackground = chipBackground; 87497cb25912dab282cf732757f68b0405ed005f00bMindy Pereira } 87597cb25912dab282cf732757f68b0405ed005f00bMindy Pereira 87697cb25912dab282cf732757f68b0405ed005f00bMindy Pereira // Visible for testing. 87797cb25912dab282cf732757f68b0405ed005f00bMindy Pereira /* package */ void setChipHeight(int height) { 87897cb25912dab282cf732757f68b0405ed005f00bMindy Pereira mChipHeight = height; 87997cb25912dab282cf732757f68b0405ed005f00bMindy Pereira } 88097cb25912dab282cf732757f68b0405ed005f00bMindy Pereira 881b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin public float getChipHeight() { 882b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return mChipHeight; 883b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 884b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 885076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira /** 886076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira * Set whether to shrink the recipients field such that at most 887076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira * one line of recipients chips are shown when the field loses 888076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira * focus. By default, the number of displayed recipients will be 889076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira * limited and a "more" chip will be shown when focus is lost. 890076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira * @param shrink 891076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira */ 892076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira public void setOnFocusListShrinkRecipients(boolean shrink) { 893076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira mShouldShrink = shrink; 894076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira } 895076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira 896cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira @Override 89702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira public void onSizeChanged(int width, int height, int oldw, int oldh) { 89802a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira super.onSizeChanged(width, height, oldw, oldh); 899db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira if (width != 0 && height != 0) { 900db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira if (mPendingChipsCount > 0) { 901db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira postHandlePendingChips(); 902db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira } else { 903db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira checkChipWidths(); 904db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira } 9058005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira } 9061852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // Try to find the scroll view parent, if it exists. 907aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira if (mScrollView == null && !mTriedGettingScrollView) { 908c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira ViewParent parent = getParent(); 909c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira while (parent != null && !(parent instanceof ScrollView)) { 910c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira parent = parent.getParent(); 911c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira } 912c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira if (parent != null) { 913c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira mScrollView = (ScrollView) parent; 914c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira } 915aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira mTriedGettingScrollView = true; 916c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira } 91702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira } 91802a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira 9192cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira private void postHandlePendingChips() { 9202cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mHandler.removeCallbacks(mHandlePendingChips); 9212cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mHandler.post(mHandlePendingChips); 9222cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } 9232cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira 924db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira private void checkChipWidths() { 925db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira // Check the widths of the associated chips. 926194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = getSortedRecipients(); 927db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira if (chips != null) { 928db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira Rect bounds; 929194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (DrawableRecipientChip chip : chips) { 930f30a42800318f6790d55421f8f6980eb38db4d3cmindyp bounds = chip.getBounds(); 931db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira if (getWidth() > 0 && bounds.right - bounds.left > getWidth()) { 932db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira // Need to redraw that chip. 933db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira replaceChip(chip, chip.getEntry()); 934db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira } 935db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira } 9362cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } 937db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira } 938db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira 939f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // Visible for testing. 940f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira /*package*/ void handlePendingChips() { 941f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (getViewWidth() <= 0) { 9428005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira // The widget has not been sized yet. 9438005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira // This will be called as a result of onSizeChanged 9448005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira // at a later point. 9458005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira return; 9468005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira } 947db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira if (mPendingChipsCount <= 0) { 948db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira return; 949db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira } 950db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira 9512cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira synchronized (mPendingChips) { 9522cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira Editable editable = getText(); 9532cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira // Tokenize! 954f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (mPendingChipsCount <= MAX_CHIPS_PARSED) { 955f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira for (int i = 0; i < mPendingChips.size(); i++) { 956f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira String current = mPendingChips.get(i); 957f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int tokenStart = editable.toString().indexOf(current); 958488718e8f464d8554dd1cb00395fdfc7871f0fc8mindyp // Always leave a space at the end between tokens. 959488718e8f464d8554dd1cb00395fdfc7871f0fc8mindyp int tokenEnd = tokenStart + current.length() - 1; 960f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (tokenStart >= 0) { 961f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // When we have a valid token, include it with the token 962f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // to the left. 963f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (tokenEnd < editable.length() - 2 964f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira && editable.charAt(tokenEnd) == COMMIT_CHAR_COMMA) { 965f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira tokenEnd++; 966f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 967f30a42800318f6790d55421f8f6980eb38db4d3cmindyp createReplacementChip(tokenStart, tokenEnd, editable, i < CHIP_LIMIT 968f30a42800318f6790d55421f8f6980eb38db4d3cmindyp || !mShouldShrink); 9692cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } 970f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira mPendingChipsCount--; 9713bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 972f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira sanitizeEnd(); 973f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } else { 974f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira mNoChips = true; 9753bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 976f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 977dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0 9782cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira && mTemporaryRecipients.size() <= RecipientAlternatesAdapter.MAX_LOOKUPS) { 9792cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira if (hasFocus() || mTemporaryRecipients.size() < CHIP_LIMIT) { 9802cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira new RecipientReplacementTask().execute(); 9812cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mTemporaryRecipients = null; 9822cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } else { 9832cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira // Create the "more" chip 9842cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mIndividualReplacements = new IndividualReplacementTask(); 985194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy mIndividualReplacements.execute(new ArrayList<DrawableRecipientChip>( 9862cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mTemporaryRecipients.subList(0, CHIP_LIMIT))); 9875e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (mTemporaryRecipients.size() > CHIP_LIMIT) { 988194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy mTemporaryRecipients = new ArrayList<DrawableRecipientChip>( 9895e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp mTemporaryRecipients.subList(CHIP_LIMIT, 9905e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp mTemporaryRecipients.size())); 9915e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp } else { 9925e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp mTemporaryRecipients = null; 9935e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp } 9942cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira createMoreChip(); 9952cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } 9962cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira } else { 9972cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira // There are too many recipients to look up, so just fall back 998dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira // to showing addresses for all of them. 9992cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mTemporaryRecipients = null; 10001852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira createMoreChip(); 10011852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 10022cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mPendingChipsCount = 0; 10032cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira mPendingChips.clear(); 10043bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10053bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10063bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira 1007f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // Visible for testing. 1008f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira /*package*/ int getViewWidth() { 1009f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return getWidth(); 1010f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1011f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 10123bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira /** 10133bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira * Remove any characters after the last valid chip. 10143bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira */ 1015aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Visible for testing. 1016aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira /*package*/ void sanitizeEnd() { 10173c42baf5b81810bce77e776963f20c960865e85bMindy Pereira // Don't sanitize while we are waiting for pending chips to complete. 10183c42baf5b81810bce77e776963f20c960865e85bMindy Pereira if (mPendingChipsCount > 0) { 10193c42baf5b81810bce77e776963f20c960865e85bMindy Pereira return; 10203c42baf5b81810bce77e776963f20c960865e85bMindy Pereira } 10213bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira // Find the last chip; eliminate any commit characters after it. 1022194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = getSortedRecipients(); 1023f30a42800318f6790d55421f8f6980eb38db4d3cmindyp Spannable spannable = getSpannable(); 10243bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira if (chips != null && chips.length > 0) { 10253bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira int end; 1026dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira mMoreChip = getMoreChip(); 10273bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira if (mMoreChip != null) { 1028f30a42800318f6790d55421f8f6980eb38db4d3cmindyp end = spannable.getSpanEnd(mMoreChip); 10293bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } else { 1030f30a42800318f6790d55421f8f6980eb38db4d3cmindyp end = getSpannable().getSpanEnd(getLastChip()); 10313bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10323bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira Editable editable = getText(); 10333bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira int length = editable.length(); 10343bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira if (length > end) { 10353bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira // See what characters occur after that and eliminate them. 10363bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira if (Log.isLoggable(TAG, Log.DEBUG)) { 10373bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira Log.d(TAG, "There were extra characters after the last tokenizable entry." 10383bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira + editable); 10393bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10403bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira editable.delete(end + 1, length); 10413bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10423bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10433bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10443bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira 10453bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira /** 10463bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira * Create a chip that represents just the email address of a recipient. At some later 10473bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira * point, this chip will be attached to a real contact entry, if one exists. 10483bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira */ 104920c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy // VisibleForTesting 105020c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy void createReplacementChip(int tokenStart, int tokenEnd, Editable editable, 1051f30a42800318f6790d55421f8f6980eb38db4d3cmindyp boolean visible) { 105232366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira if (alreadyHasChip(tokenStart, tokenEnd)) { 105332366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira // There is already a chip present at this location. 105432366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira // Don't recreate it. 105532366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira return; 105632366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira } 10577ebb40ff05dbf28edd9bbed4eba7e57c8c6005aeMindy Pereira String token = editable.toString().substring(tokenStart, tokenEnd); 105820c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy final String trimmedToken = token.trim(); 105920c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy int commitCharIndex = trimmedToken.lastIndexOf(COMMIT_CHAR_COMMA); 106041137c249b5fe547a3631d9c6db57ff980022affTom Taylor if (commitCharIndex != -1 && commitCharIndex == trimmedToken.length() - 1) { 106120c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy token = trimmedToken.substring(0, trimmedToken.length() - 1); 10623bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10633bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira RecipientEntry entry = createTokenizedEntry(token); 1064ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira if (entry != null) { 1065194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip chip = null; 1066ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira try { 1067f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (!mNoChips) { 1068f30a42800318f6790d55421f8f6980eb38db4d3cmindyp /* 1069f30a42800318f6790d55421f8f6980eb38db4d3cmindyp * leave space for the contact icon if this is not just an 1070f30a42800318f6790d55421f8f6980eb38db4d3cmindyp * email address 1071f30a42800318f6790d55421f8f6980eb38db4d3cmindyp */ 1072f30a42800318f6790d55421f8f6980eb38db4d3cmindyp boolean leaveSpace = TextUtils.isEmpty(entry.getDisplayName()) 1073f30a42800318f6790d55421f8f6980eb38db4d3cmindyp || TextUtils.equals(entry.getDisplayName(), 1074f30a42800318f6790d55421f8f6980eb38db4d3cmindyp entry.getDestination()); 1075f30a42800318f6790d55421f8f6980eb38db4d3cmindyp chip = visible ? 1076f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy constructChipSpan(entry, false, leaveSpace) 1077f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy : new InvisibleRecipientChip(entry); 1078f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1079ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira } catch (NullPointerException e) { 1080ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira Log.e(TAG, e.getMessage(), e); 1081ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira } 1082cb76b4d2bcb7b72b9505a620ca71f753506e48b9mindyp editable.setSpan(chip, tokenStart, tokenEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 1083ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira // Add this chip to the list of entries "to replace" 1084ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira if (chip != null) { 1085dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira if (mTemporaryRecipients == null) { 1086194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy mTemporaryRecipients = new ArrayList<DrawableRecipientChip>(); 1087dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira } 10880613ff859a6b44685af62821eac369597cf69b26Scott Kennedy chip.setOriginalText(token); 1089ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira mTemporaryRecipients.add(chip); 1090ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira } 10911852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 10923bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 10933bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira 1094abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor private static boolean isPhoneNumber(String number) { 1095abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor // TODO: replace this function with libphonenumber's isPossibleNumber (see 1096abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor // PhoneNumberUtil). One complication is that it requires the sender's region which 1097abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor // comes from the CurrentCountryIso. For now, let's just do this simple match. 1098abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor if (TextUtils.isEmpty(number)) { 1099abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor return false; 1100abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor } 1101abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor 11024491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor Matcher match = PHONE_PATTERN.matcher(number); 1103abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor return match.matches(); 1104abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor } 1105abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor 1106983c99b9e00913e8d2a5642cd05ccb9a81570be7Scott Kennedy // VisibleForTesting 1107983c99b9e00913e8d2a5642cd05ccb9a81570be7Scott Kennedy RecipientEntry createTokenizedEntry(final String token) { 1108ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira if (TextUtils.isEmpty(token)) { 1109ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira return null; 1110ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira } 11115e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (isPhoneQuery() && isPhoneNumber(token)) { 11120ba9133c904b8c35af8209a54604331cd671bc1fTom Taylor return RecipientEntry.constructFakePhoneEntry(token, true); 11135e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp } 11143bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(token); 111501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira String display = null; 1116f30a42800318f6790d55421f8f6980eb38db4d3cmindyp boolean isValid = isValid(token); 1117f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (isValid && tokens != null && tokens.length > 0) { 1118ee58f4904fe6d992ad4631604b595ba47d08ca6bMindy Pereira // If we can get a name from tokenizing, then generate an entry from 1119ee58f4904fe6d992ad4631604b595ba47d08ca6bMindy Pereira // this. 112001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira display = tokens[0].getName(); 112101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira if (!TextUtils.isEmpty(display)) { 1122f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return RecipientEntry.constructGeneratedEntry(display, tokens[0].getAddress(), 1123f30a42800318f6790d55421f8f6980eb38db4d3cmindyp isValid); 1124401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira } else { 1125401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira display = tokens[0].getAddress(); 1126401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira if (!TextUtils.isEmpty(display)) { 1127f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return RecipientEntry.constructFakeEntry(display, isValid); 1128401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira } 112901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 113001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 11314e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira // Unable to validate the token or to create a valid token from it. 11324e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira // Just create a chip the user can edit. 1133dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira String validatedToken = null; 1134f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (mValidator != null && !isValid) { 11354e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira // Try fixing up the entry using the validator. 1136dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira validatedToken = mValidator.fixText(token).toString(); 1137dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira if (!TextUtils.isEmpty(validatedToken)) { 1138dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira if (validatedToken.contains(token)) { 1139f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // protect against the case of a validator with a null 1140f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // domain, 1141dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira // which doesn't add a domain to the token 1142dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(validatedToken); 1143dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira if (tokenized.length > 0) { 1144dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira validatedToken = tokenized[0].getAddress(); 1145f30a42800318f6790d55421f8f6980eb38db4d3cmindyp isValid = true; 1146dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira } 1147dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira } else { 1148f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // We ran into a case where the token was invalid and 1149f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // removed 1150f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // by the validator. In this case, just use the original 1151f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // token 1152dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira // and let the user sort out the error chip. 1153dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira validatedToken = null; 1154f30a42800318f6790d55421f8f6980eb38db4d3cmindyp isValid = false; 1155184d3773dc096c7a6a83f7aeda042fa8346c2024Erik } 1156ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira } 11574e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira } 1158ee58f4904fe6d992ad4631604b595ba47d08ca6bMindy Pereira // Otherwise, fallback to just creating an editable email address chip. 1159f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return RecipientEntry.constructFakeEntry( 1160f30a42800318f6790d55421f8f6980eb38db4d3cmindyp !TextUtils.isEmpty(validatedToken) ? validatedToken : token, isValid); 116101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 116201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira 11634e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira private boolean isValid(String text) { 11644e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira return mValidator == null ? true : mValidator.isValid(text); 11654e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira } 11664e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira 1167f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy private static String tokenizeAddress(String destination) { 116801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(destination); 116901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira if (tokens != null && tokens.length > 0) { 117001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira return tokens[0].getAddress(); 11713bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 117201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira return destination; 11733bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 11743bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira 117502a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira @Override 1176cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira public void setTokenizer(Tokenizer tokenizer) { 1177cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira mTokenizer = tokenizer; 1178cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira super.setTokenizer(mTokenizer); 1179cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1180cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1181a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira @Override 1182a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira public void setValidator(Validator validator) { 1183a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira mValidator = validator; 1184a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira super.setValidator(validator); 1185a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira } 1186a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira 1187a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1188a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * We cannot use the default mechanism for replaceText. Instead, 1189a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * we override onItemClickListener so we can get all the associated 1190a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * contact information including display text, address, and id. 1191a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 1192cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira @Override 1193cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira protected void replaceText(CharSequence text) { 1194cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return; 1195cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1196cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1197a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1198a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * Dismiss any selected chips when the back key is pressed. 1199a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 1200cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira @Override 12014c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira public boolean onKeyPreIme(int keyCode, KeyEvent event) { 120203cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira if (keyCode == KeyEvent.KEYCODE_BACK && mSelectedChip != null) { 12034c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira clearSelectedChip(); 120403cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira return true; 12054c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira } 12064c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira return super.onKeyPreIme(keyCode, event); 12074c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira } 12084c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira 1209a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1210a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * Monitor key presses in this view to see if the user types 1211a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * any commit keys, which consist of ENTER, TAB, or DPAD_CENTER. 1212a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * If the user has entered text that has contact matches and types 1213a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * a commit key, create a chip from the topmost matching contact. 1214a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * If the user has entered text that has no contact matches and types 1215a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * a commit key, then create a chip from the text they have entered. 1216a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 12174c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira @Override 1218cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira public boolean onKeyUp(int keyCode, KeyEvent event) { 1219cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira switch (keyCode) { 12200f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira case KeyEvent.KEYCODE_TAB: 12210f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira if (event.hasNoModifiers()) { 12220f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira if (mSelectedChip != null) { 12230f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira clearSelectedChip(); 12240f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira } else { 12250f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira commitDefault(); 12260f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira } 12270f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira } 122803cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira break; 1229cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1230cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return super.onKeyUp(keyCode, event); 1231cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1232cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 12330f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira private boolean focusNext() { 12340f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira View next = focusSearch(View.FOCUS_DOWN); 12350f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira if (next != null) { 12360f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira next.requestFocus(); 12370f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira return true; 12380f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira } 12390f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira return false; 12400f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira } 12410f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira 1242342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira /** 1243342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * Create a chip from the default selection. If the popup is showing, the 1244342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * default is the first item in the popup suggestions list. Otherwise, it is 1245342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * whatever the user had typed in. End represents where the the tokenizer 1246342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * should search for a token to turn into a chip. 1247342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * @return If a chip was created from a real contact. 1248342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira */ 1249a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira private boolean commitDefault() { 125060d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira // If there is no tokenizer, don't try to commit. 125160d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira if (mTokenizer == null) { 125260d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira return false; 125360d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira } 125412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira Editable editable = getText(); 1255342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira int end = getSelectionEnd(); 125612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira int start = mTokenizer.findTokenStart(editable, end); 12572b4ffc53a3b51631cb6aabf535986a9344ee6cbbMindy Pereira 125879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira if (shouldCreateChip(start, end)) { 125979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira int whatEnd = mTokenizer.findTokenEnd(getText(), start); 126079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira // In the middle of chip; treat this as an edit 126179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira // and commit the whole token. 12624f82d888c680a61b95373740ce68bfb48a242617mindyp whatEnd = movePastTerminators(whatEnd); 126379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira if (whatEnd != getSelectionEnd()) { 126479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira handleEdit(start, whatEnd); 126579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira return true; 126612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 126779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira return commitChip(start, end , editable); 126879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 126979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira return false; 127079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 127179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira 127279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira private void commitByCharacter() { 127360d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira // We can't possibly commit by character if we can't tokenize. 127460d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira if (mTokenizer == null) { 127560d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira return; 127660d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira } 127779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira Editable editable = getText(); 127879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira int end = getSelectionEnd(); 127979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira int start = mTokenizer.findTokenStart(editable, end); 128079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira if (shouldCreateChip(start, end)) { 128179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira commitChip(start, end, editable); 128212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 1283ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira setSelection(getText().length()); 128479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 128512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 128679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira private boolean commitChip(int start, int end, Editable editable) { 128720c9d620e79ae28994856541761a951074551518Mindy Pereira ListAdapter adapter = getAdapter(); 128820c9d620e79ae28994856541761a951074551518Mindy Pereira if (adapter != null && adapter.getCount() > 0 && enoughToFilter() 1289c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor && end == getSelectionEnd() && !isPhoneQuery()) { 129079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira // choose the first entry. 129179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira submitItemAtPosition(0); 129279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira dismissDropDown(); 129379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira return true; 129479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } else { 129579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira int tokenEnd = mTokenizer.findTokenEnd(editable, start); 129620c9d620e79ae28994856541761a951074551518Mindy Pereira if (editable.length() > tokenEnd + 1) { 129720c9d620e79ae28994856541761a951074551518Mindy Pereira char charAt = editable.charAt(tokenEnd + 1); 129820c9d620e79ae28994856541761a951074551518Mindy Pereira if (charAt == COMMIT_CHAR_COMMA || charAt == COMMIT_CHAR_SEMICOLON) { 129920c9d620e79ae28994856541761a951074551518Mindy Pereira tokenEnd++; 130020c9d620e79ae28994856541761a951074551518Mindy Pereira } 1301d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira } 130262397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira String text = editable.toString().substring(start, tokenEnd).trim(); 130379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira clearComposingText(); 130479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira if (text != null && text.length() > 0 && !text.equals(" ")) { 130501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira RecipientEntry entry = createTokenizedEntry(text); 1306ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira if (entry != null) { 1307ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 1308ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira CharSequence chipText = createChip(entry, false); 1309ee6d83fe1da297b2f9af0fb221be376fdc816830Mindy Pereira if (chipText != null && start > -1 && end > -1) { 1310f177bdab5724635aed964de889febe96ecab6bc0Mindy Pereira editable.replace(start, end, chipText); 1311f177bdab5724635aed964de889febe96ecab6bc0Mindy Pereira } 1312ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira } 131320c9d620e79ae28994856541761a951074551518Mindy Pereira // Only dismiss the dropdown if it is related to the text we 131420c9d620e79ae28994856541761a951074551518Mindy Pereira // just committed. 131520c9d620e79ae28994856541761a951074551518Mindy Pereira // For paste, it may not be as there are possibly multiple 131620c9d620e79ae28994856541761a951074551518Mindy Pereira // tokens being added. 131720c9d620e79ae28994856541761a951074551518Mindy Pereira if (end == getSelectionEnd()) { 131820c9d620e79ae28994856541761a951074551518Mindy Pereira dismissDropDown(); 131920c9d620e79ae28994856541761a951074551518Mindy Pereira } 13205df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira sanitizeBetween(); 132112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira return true; 132205dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira } 132305dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira } 132412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira return false; 132505dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira } 132605dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira 132701162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira // Visible for testing. 132801162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira /* package */ void sanitizeBetween() { 13293c42baf5b81810bce77e776963f20c960865e85bMindy Pereira // Don't sanitize while we are waiting for content to chipify. 13303c42baf5b81810bce77e776963f20c960865e85bMindy Pereira if (mPendingChipsCount > 0) { 13313c42baf5b81810bce77e776963f20c960865e85bMindy Pereira return; 13323c42baf5b81810bce77e776963f20c960865e85bMindy Pereira } 13335df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira // Find the last chip. 1334194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] recips = getSortedRecipients(); 13355df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira if (recips != null && recips.length > 0) { 1336194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip last = recips[recips.length - 1]; 1337194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip beforeLast = null; 13385df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira if (recips.length > 1) { 13395df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira beforeLast = recips[recips.length - 2]; 13405df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira } 13415df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira int startLooking = 0; 13425df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira int end = getSpannable().getSpanStart(last); 13435df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira if (beforeLast != null) { 13445df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira startLooking = getSpannable().getSpanEnd(beforeLast); 1345399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira Editable text = getText(); 134601162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira if (startLooking == -1 || startLooking > text.length() - 1) { 1347399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira // There is nothing after this chip. 1348399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira return; 1349399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira } 1350399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira if (text.charAt(startLooking) == ' ') { 13515df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira startLooking++; 13525df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira } 13535df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira } 1354e13775cc6e9342e42db0b853cc42dbfda6d1365fMindy Pereira if (startLooking >= 0 && end >= 0 && startLooking < end) { 13555df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira getText().delete(startLooking, end); 13565df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira } 13575df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira } 13585df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira } 13595df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira 136079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira private boolean shouldCreateChip(int start, int end) { 1361f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return !mNoChips && hasFocus() && enoughToFilter() && !alreadyHasChip(start, end); 136232366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira } 136332366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira 136432366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira private boolean alreadyHasChip(int start, int end) { 1365f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (mNoChips) { 1366f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return true; 1367f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1368194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = 1369194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy getSpannable().getSpans(start, end, DrawableRecipientChip.class); 137032366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira if ((chips == null || chips.length == 0)) { 137132366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira return false; 137264af2da9970f59eee2dfd0c8dd2a06f09171bad2Mindy Pereira } 137332366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira return true; 137479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 137579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira 137679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira private void handleEdit(int start, int end) { 1377a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira if (start == -1 || end == -1) { 1378a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira // This chip no longer exists in the field. 1379a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira dismissDropDown(); 1380a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira return; 1381a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira } 138279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira // This is in the middle of a chip, so select out the whole chip 138379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira // and commit it. 138479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira Editable editable = getText(); 138579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira setSelection(end); 138679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira String text = getText().toString().substring(start, end); 1387fe52b97e748ec0b9bd44b759780ed42b9dcee7ffMindy Pereira if (!TextUtils.isEmpty(text)) { 1388f30a42800318f6790d55421f8f6980eb38db4d3cmindyp RecipientEntry entry = RecipientEntry.constructFakeEntry(text, isValid(text)); 1389fe52b97e748ec0b9bd44b759780ed42b9dcee7ffMindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 1390fe52b97e748ec0b9bd44b759780ed42b9dcee7ffMindy Pereira CharSequence chipText = createChip(entry, false); 13915cfd6fea275724ce223cb8f4a1821922c8763631Mindy Pereira int selEnd = getSelectionEnd(); 13925cfd6fea275724ce223cb8f4a1821922c8763631Mindy Pereira if (chipText != null && start > -1 && selEnd > -1) { 13935cfd6fea275724ce223cb8f4a1821922c8763631Mindy Pereira editable.replace(start, selEnd, chipText); 13942d7709d276c03e536d37961076107af9f98522f5Mindy Pereira } 1395fe52b97e748ec0b9bd44b759780ed42b9dcee7ffMindy Pereira } 1396ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira dismissDropDown(); 139764af2da9970f59eee2dfd0c8dd2a06f09171bad2Mindy Pereira } 139864af2da9970f59eee2dfd0c8dd2a06f09171bad2Mindy Pereira 1399a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1400a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * If there is a selected chip, delegate the key events 1401a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * to the selected chip. 1402a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 1403cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira @Override 1404cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira public boolean onKeyDown(int keyCode, KeyEvent event) { 140595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (mSelectedChip != null && keyCode == KeyEvent.KEYCODE_DEL) { 140695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (mAlternatesPopup != null && mAlternatesPopup.isShowing()) { 140795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mAlternatesPopup.dismiss(); 140895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 140995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira removeChip(mSelectedChip); 1410cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1411cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 14122fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy switch (keyCode) { 14132fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy case KeyEvent.KEYCODE_ENTER: 14142fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy case KeyEvent.KEYCODE_DPAD_CENTER: 14152fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy if (event.hasNoModifiers()) { 14162fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy if (commitDefault()) { 14172fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy return true; 14182fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy } 14192fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy if (mSelectedChip != null) { 14202fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy clearSelectedChip(); 14212fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy return true; 14222fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy } else if (focusNext()) { 14232fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy return true; 14242fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy } 14252fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy } 14262fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy break; 14272fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy } 14282fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy 1429cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return super.onKeyDown(keyCode, event); 14309159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira } 14319159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira 143201162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira // Visible for testing. 143301162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira /* package */ Spannable getSpannable() { 1434e33555f13a9b05d835cb860e2c30ef40af3c8502Erik return getText(); 1435cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1436cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1437194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private int getChipStart(DrawableRecipientChip chip) { 143895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira return getSpannable().getSpanStart(chip); 143995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 144095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira 1441194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private int getChipEnd(DrawableRecipientChip chip) { 144295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira return getSpannable().getSpanEnd(chip); 144395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 144495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira 1445cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira /** 1446cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira * Instead of filtering on the entire contents of the edit box, 1447cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira * this subclass method filters on the range from 1448cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd} 1449cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira * if the length of that range meets or exceeds {@link #getThreshold} 1450cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira * and makes sure that the range is not already a Chip. 1451cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira */ 1452cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira @Override 1453cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira protected void performFiltering(CharSequence text, int keyCode) { 1454093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp boolean isCompletedToken = isCompletedToken(text); 1455093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp if (enoughToFilter() && !isCompletedToken) { 1456cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira int end = getSelectionEnd(); 1457cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira int start = mTokenizer.findTokenStart(text, end); 1458cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // If this is a RecipientChip, don't filter 1459cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // on its contents. 1460cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira Spannable span = getSpannable(); 1461194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = span.getSpans(start, end, DrawableRecipientChip.class); 1462cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira if (chips != null && chips.length > 0) { 146341e93fbe82cd4d802e3f1fbe265038f7a0521dddKevin Lin dismissDropDown(); 1464cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return; 1465cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1466093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp } else if (isCompletedToken) { 146741e93fbe82cd4d802e3f1fbe265038f7a0521dddKevin Lin dismissDropDown(); 1468093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp return; 1469cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1470cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira super.performFiltering(text, keyCode); 1471cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1472cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 147320c9d620e79ae28994856541761a951074551518Mindy Pereira // Visible for testing. 147420c9d620e79ae28994856541761a951074551518Mindy Pereira /*package*/ boolean isCompletedToken(CharSequence text) { 147520c9d620e79ae28994856541761a951074551518Mindy Pereira if (TextUtils.isEmpty(text)) { 147620c9d620e79ae28994856541761a951074551518Mindy Pereira return false; 147720c9d620e79ae28994856541761a951074551518Mindy Pereira } 147820c9d620e79ae28994856541761a951074551518Mindy Pereira // Check to see if this is a completed token before filtering. 147920c9d620e79ae28994856541761a951074551518Mindy Pereira int end = text.length(); 148020c9d620e79ae28994856541761a951074551518Mindy Pereira int start = mTokenizer.findTokenStart(text, end); 148120c9d620e79ae28994856541761a951074551518Mindy Pereira String token = text.toString().substring(start, end).trim(); 148220c9d620e79ae28994856541761a951074551518Mindy Pereira if (!TextUtils.isEmpty(token)) { 148320c9d620e79ae28994856541761a951074551518Mindy Pereira char atEnd = token.charAt(token.length() - 1); 148420c9d620e79ae28994856541761a951074551518Mindy Pereira return atEnd == COMMIT_CHAR_COMMA || atEnd == COMMIT_CHAR_SEMICOLON; 148520c9d620e79ae28994856541761a951074551518Mindy Pereira } 148620c9d620e79ae28994856541761a951074551518Mindy Pereira return false; 148720c9d620e79ae28994856541761a951074551518Mindy Pereira } 148820c9d620e79ae28994856541761a951074551518Mindy Pereira 1489cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private void clearSelectedChip() { 1490cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira if (mSelectedChip != null) { 149195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira unselectChip(mSelectedChip); 1492cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira mSelectedChip = null; 1493cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1494b86dcd5230ebcc57e5fc7a669c2304aca142dbf5Mindy Pereira setCursorVisible(true); 1495cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1496cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1497a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1498a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * Monitor touch events in the RecipientEditTextView. 1499a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * If the view does not have focus, any tap on the view 1500a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * will just focus the view. If the view has focus, determine 1501a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * if the touch target is a recipient chip. If it is and the chip 1502a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * is not selected, select it and clear any other selected chips. 1503a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * If it isn't, then select that chip. 1504a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 1505cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira @Override 1506cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira public boolean onTouchEvent(MotionEvent event) { 150705dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira if (!isFocused()) { 150805dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira // Ignore any chip taps until this view is focused. 150905dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira return super.onTouchEvent(event); 151005dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira } 1511cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira boolean handled = super.onTouchEvent(event); 151205dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira int action = event.getAction(); 1513cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira boolean chipWasSelected = false; 15142e905906f83fb1285498f09fce4db4a5878efbccMindy Pereira if (mSelectedChip == null) { 15151d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mGestureDetector.onTouchEvent(event); 15161d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 1517bfedc1e199e57dcda494389fdca0750e1f165135Mindy Pereira if (mCopyAddress == null && action == MotionEvent.ACTION_UP) { 1518cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira float x = event.getX(); 1519cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira float y = event.getY(); 15201650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy int offset = putOffsetInRange(x, y); 1521194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip currentChip = findChip(offset); 1522cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira if (currentChip != null) { 1523cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira if (action == MotionEvent.ACTION_UP) { 1524cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira if (mSelectedChip != null && mSelectedChip != currentChip) { 1525cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira clearSelectedChip(); 152695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mSelectedChip = selectChip(currentChip); 1527cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } else if (mSelectedChip == null) { 1528a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira setSelection(getText().length()); 1529a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira commitDefault(); 153095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mSelectedChip = selectChip(currentChip); 1531cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } else { 153295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira onClick(mSelectedChip, offset, x, y); 1533cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1534cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1535cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira chipWasSelected = true; 1536c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira handled = true; 15375e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp } else if (mSelectedChip != null && shouldShowEditableText(mSelectedChip)) { 1538c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira chipWasSelected = true; 1539cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1540cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1541cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira if (action == MotionEvent.ACTION_UP && !chipWasSelected) { 1542cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira clearSelectedChip(); 1543cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1544cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return handled; 1545cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1546cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1547c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira private void scrollLineIntoView(int line) { 1548c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira if (mScrollView != null) { 1549093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp mScrollView.smoothScrollBy(0, calculateOffsetFromBottom(line)); 1550c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira } 1551c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira } 1552c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira 1553272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy private void showAlternates(final DrawableRecipientChip currentChip, 1554272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy final ListPopupWindow alternatesPopup, final int width) { 1555272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy new AsyncTask<Void, Void, ListAdapter>() { 1556272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy @Override 1557272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy protected ListAdapter doInBackground(final Void... params) { 1558272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy return createAlternatesAdapter(currentChip); 1559272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy } 1560272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy 15616b7110f320c978c368c28bdb06212c6a6df12f1fAlice Yang @Override 1562272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy protected void onPostExecute(final ListAdapter result) { 1563fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler if (!mAttachedToWindow) { 1564fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler return; 1565fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler } 1566272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy int line = getLayout().getLineForOffset(getChipStart(currentChip)); 1567272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy int bottom; 1568272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy if (line == getLineCount() -1) { 1569272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy bottom = 0; 1570272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy } else { 1571272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy bottom = -(int) ((mChipHeight + (2 * mLineSpacingExtra)) * (Math 1572272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy .abs(getLineCount() - 1 - line))); 1573272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy } 1574272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy // Align the alternates popup with the left side of the View, 1575272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy // regardless of the position of the chip tapped. 1576272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy alternatesPopup.setWidth(width); 1577272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy alternatesPopup.setAnchorView(RecipientEditTextView.this); 1578272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy alternatesPopup.setVerticalOffset(bottom); 1579272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy alternatesPopup.setAdapter(result); 1580272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy alternatesPopup.setOnItemClickListener(mAlternatesListener); 1581272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy // Clear the checked item. 1582272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy mCheckedItem = -1; 1583272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy alternatesPopup.show(); 1584272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy ListView listView = alternatesPopup.getListView(); 1585272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 1586272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy // Checked item would be -1 if the adapter has not 1587272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy // loaded the view that should be checked yet. The 1588272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy // variable will be set correctly when onCheckedItemChanged 1589272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy // is called in a separate thread. 1590272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy if (mCheckedItem != -1) { 1591272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy listView.setItemChecked(mCheckedItem, true); 1592272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy mCheckedItem = -1; 1593272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy } 1594272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy } 1595272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy }.execute((Void[]) null); 159695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 159795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira 1598194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private ListAdapter createAlternatesAdapter(DrawableRecipientChip chip) { 15997a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy return new RecipientAlternatesAdapter(getContext(), chip.getContactId(), 16007a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy chip.getDirectoryId(), chip.getLookupKey(), chip.getDataId(), 1601b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin getAdapter().getQueryType(), this, mDropdownChipLayouter); 160295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 160395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira 1604194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private ListAdapter createSingleAddressAdapter(DrawableRecipientChip currentChip) { 1605b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return new SingleRecipientArrayAdapter(getContext(), currentChip.getEntry(), 1606b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin mDropdownChipLayouter); 160701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 160801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira 1609aa2afffe7aba707c2406f2e4503fa6037c4cd196Andy Stadler @Override 161095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira public void onCheckedItemChanged(int position) { 161195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira ListView listView = mAlternatesPopup.getListView(); 161295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (listView != null && listView.getCheckedItemCount() == 0) { 161395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira listView.setItemChecked(position, true); 161495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 161598b547f2eeb80259036e3f528636d7cbd823bf6dMindy Pereira mCheckedItem = position; 161695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 161795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira 16181650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy private int putOffsetInRange(final float x, final float y) { 16191650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy final int offset; 16201650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy 16211650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 16221650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy offset = getOffsetForPosition(x, y); 16231650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy } else { 16241650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy offset = supportGetOffsetForPosition(x, y); 16251650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy } 16261650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy 16271650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy return putOffsetInRange(offset); 16281650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy } 16291650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy 1630cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // TODO: This algorithm will need a lot of tweaking after more people have used 1631cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // the chips ui. This attempts to be "forgiving" to fat finger touches by favoring 1632cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // what comes before the finger. 1633cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private int putOffsetInRange(int o) { 1634cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira int offset = o; 1635cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira Editable text = getText(); 1636cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira int length = text.length(); 1637cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Remove whitespace from end to find "real end" 1638cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira int realLength = length; 1639cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira for (int i = length - 1; i >= 0; i--) { 1640cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira if (text.charAt(i) == ' ') { 1641cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira realLength--; 1642cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } else { 1643cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira break; 1644cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1645cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1646cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1647fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira // If the offset is beyond or at the end of the text, 1648fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira // leave it alone. 1649fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira if (offset >= realLength) { 1650cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return offset; 1651cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1652fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira Editable editable = getText(); 1653b88ee450829eb4ac24fb47c377b9ec3aab0782daMindy Pereira while (offset >= 0 && findText(editable, offset) == -1 && findChip(offset) == null) { 1654cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Keep walking backward! 1655cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira offset--; 1656cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1657cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return offset; 1658cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1659cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1660f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy private static int findText(Editable text, int offset) { 1661fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira if (text.charAt(offset) != ' ') { 1662fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira return offset; 1663fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira } 1664fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira return -1; 1665fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira } 1666fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira 1667194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private DrawableRecipientChip findChip(int offset) { 1668194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = 1669194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy getSpannable().getSpans(0, getText().length(), DrawableRecipientChip.class); 1670cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Find the chip that contains this offset. 1671cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira for (int i = 0; i < chips.length; i++) { 1672194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip chip = chips[i]; 167395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int start = getChipStart(chip); 167495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int end = getChipEnd(chip); 167595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (offset >= start && offset <= end) { 1676cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return chip; 1677cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1678cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1679cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return null; 1680cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1681cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 168201162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira // Visible for testing. 1683aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Use this method to generate text to add to the list of addresses. 168432aff5f5bdc7c860169e50eacb7120983b3901a8Mindy Pereira /* package */String createAddressText(RecipientEntry entry) { 16853ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira String display = entry.getDisplayName(); 16863ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira String address = entry.getDestination(); 16873ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) { 16883ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira display = null; 16893ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira } 1690abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor String trimmedDisplayText; 1691c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor if (isPhoneQuery() && isPhoneNumber(address)) { 1692abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor trimmedDisplayText = address.trim(); 1693abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor } else { 1694abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor if (address != null) { 1695abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor // Tokenize out the address in case the address already 1696abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor // contained the username as well. 1697abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(address); 1698abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor if (tokenized != null && tokenized.length > 0) { 1699abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor address = tokenized[0].getAddress(); 1700abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor } 1701ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira } 1702abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor Rfc822Token token = new Rfc822Token(display, address, null); 1703abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor trimmedDisplayText = token.toString().trim(); 1704d8c15c328eaa109946f8f9093f3c2f2773d525ddMindy Pereira } 17053ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira int index = trimmedDisplayText.indexOf(","); 170632aff5f5bdc7c860169e50eacb7120983b3901a8Mindy Pereira return mTokenizer != null && !TextUtils.isEmpty(trimmedDisplayText) 170732aff5f5bdc7c860169e50eacb7120983b3901a8Mindy Pereira && index < trimmedDisplayText.length() - 1 ? (String) mTokenizer 1708aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira .terminateToken(trimmedDisplayText) : trimmedDisplayText; 1709aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } 1710aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 1711aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Visible for testing. 1712aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Use this method to generate text to display in a chip. 1713aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira /*package*/ String createChipDisplayText(RecipientEntry entry) { 1714aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira String display = entry.getDisplayName(); 1715aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira String address = entry.getDestination(); 1716aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) { 1717aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira display = null; 1718aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } 1719aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira if (!TextUtils.isEmpty(display)) { 1720aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira return display; 1721aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } else if (!TextUtils.isEmpty(address)){ 1722aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira return address; 1723aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } else { 1724aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira return new Rfc822Token(display, address, null).toString(); 1725aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } 17263ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira } 17273ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira 17280ab7e735e82c81baf9ab87d028611561ce0592b7Mindy Pereira private CharSequence createChip(RecipientEntry entry, boolean pressed) { 1729aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira String displayText = createAddressText(entry); 17302d7709d276c03e536d37961076107af9f98522f5Mindy Pereira if (TextUtils.isEmpty(displayText)) { 17312d7709d276c03e536d37961076107af9f98522f5Mindy Pereira return null; 17322d7709d276c03e536d37961076107af9f98522f5Mindy Pereira } 1733f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira SpannableString chipText = null; 1734cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira // Always leave a blank space at the end of a chip. 1735f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy int textLength = displayText.length() - 1; 1736f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira chipText = new SpannableString(displayText); 1737f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (!mNoChips) { 1738f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira try { 1739194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip chip = constructChipSpan(entry, pressed, 17405e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp false /* leave space for contact icon */); 1741f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira chipText.setSpan(chip, 0, textLength, 1742f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 1743f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira chip.setOriginalText(chipText.toString()); 1744f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } catch (NullPointerException e) { 1745f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Log.e(TAG, e.getMessage(), e); 1746f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return null; 1747f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1748cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1749cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return chipText; 1750cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1751cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1752a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1753a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * When an item in the suggestions list has been clicked, create a chip from the 1754a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * contact information of the selected item. 1755a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 1756cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira @Override 1757cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1758114c89364bd00a445f0b017ae658928c1dc26c5aTom Taylor if (position < 0) { 1759114c89364bd00a445f0b017ae658928c1dc26c5aTom Taylor return; 1760114c89364bd00a445f0b017ae658928c1dc26c5aTom Taylor } 1761cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira submitItemAtPosition(position); 1762cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1763cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1764cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira private void submitItemAtPosition(int position) { 1765858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy RecipientEntry entry = createValidatedEntry(getAdapter().getItem(position)); 1766f7eaa5fc06e3dd57457f7bf0169374a4c9a1e413Erik if (entry == null) { 1767f7eaa5fc06e3dd57457f7bf0169374a4c9a1e413Erik return; 1768f7eaa5fc06e3dd57457f7bf0169374a4c9a1e413Erik } 1769cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira clearComposingText(); 1770cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1771cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira int end = getSelectionEnd(); 1772cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira int start = mTokenizer.findTokenStart(getText(), end); 1773cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1774cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira Editable editable = getText(); 1775cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 1776e90bfad1a7cc118e721b723aba48e71b952e5c6eMindy Pereira CharSequence chip = createChip(entry, false); 1777005b2e29742d7b9f3ecc505a50b11d91bd44c818Mindy Pereira if (chip != null && start >= 0 && end >= 0) { 1778e90bfad1a7cc118e721b723aba48e71b952e5c6eMindy Pereira editable.replace(start, end, chip); 1779e90bfad1a7cc118e721b723aba48e71b952e5c6eMindy Pereira } 17805df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira sanitizeBetween(); 1781cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1782cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 17833bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira private RecipientEntry createValidatedEntry(RecipientEntry item) { 17843bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira if (item == null) { 17853bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira return null; 17863bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 17873bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira final RecipientEntry entry; 17883bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira // If the display name and the address are the same, or if this is a 17893bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira // valid contact, but the destination is invalid, then make this a fake 17903bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira // recipient that is editable. 17913bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira String destination = item.getDestination(); 17925e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (!isPhoneQuery() && item.getContactId() == RecipientEntry.GENERATED_CONTACT) { 17935e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp entry = RecipientEntry.constructGeneratedEntry(item.getDisplayName(), 1794f30a42800318f6790d55421f8f6980eb38db4d3cmindyp destination, item.isValid()); 17955e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp } else if (RecipientEntry.isCreatedRecipient(item.getContactId()) 1796b4e244af14950aee7d612612d5406981315d3454Mindy Pereira && (TextUtils.isEmpty(item.getDisplayName()) 1797b4e244af14950aee7d612612d5406981315d3454Mindy Pereira || TextUtils.equals(item.getDisplayName(), destination) 1798b4e244af14950aee7d612612d5406981315d3454Mindy Pereira || (mValidator != null && !mValidator.isValid(destination)))) { 1799f30a42800318f6790d55421f8f6980eb38db4d3cmindyp entry = RecipientEntry.constructFakeEntry(destination, item.isValid()); 18003bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } else { 18013bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira entry = item; 18023bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 18033bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira return entry; 18043bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira } 18053bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira 1806cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira /** Returns a collection of contact Id for each chip inside this View. */ 1807cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira /* package */ Collection<Long> getContactIds() { 1808cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira final Set<Long> result = new HashSet<Long>(); 1809194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = getSortedRecipients(); 1810df4457285cf0a54d957f1fad3bbc07112f750818Mindy Pereira if (chips != null) { 1811194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (DrawableRecipientChip chip : chips) { 1812df4457285cf0a54d957f1fad3bbc07112f750818Mindy Pereira result.add(chip.getContactId()); 1813df4457285cf0a54d957f1fad3bbc07112f750818Mindy Pereira } 1814cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1815cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira return result; 1816cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 1817cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 1818aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira 1819aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira /** Returns a collection of data Id for each chip inside this View. May be null. */ 1820aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira /* package */ Collection<Long> getDataIds() { 1821aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira final Set<Long> result = new HashSet<Long>(); 1822194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip [] chips = getSortedRecipients(); 1823aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira if (chips != null) { 1824194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (DrawableRecipientChip chip : chips) { 1825aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira result.add(chip.getDataId()); 1826aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } 1827aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira } 1828aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira return result; 1829d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira } 1830d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira 183101162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira // Visible for testing. 1832194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy /* package */DrawableRecipientChip[] getSortedRecipients() { 1833194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] recips = getSpannable() 1834194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy .getSpans(0, getText().length(), DrawableRecipientChip.class); 1835194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy ArrayList<DrawableRecipientChip> recipientsList = new ArrayList<DrawableRecipientChip>( 1836194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy Arrays.asList(recips)); 1837e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira final Spannable spannable = getSpannable(); 1838194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy Collections.sort(recipientsList, new Comparator<DrawableRecipientChip>() { 1839e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira 1840e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira @Override 1841194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy public int compare(DrawableRecipientChip first, DrawableRecipientChip second) { 1842e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira int firstStart = spannable.getSpanStart(first); 1843e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira int secondStart = spannable.getSpanStart(second); 1844e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira if (firstStart < secondStart) { 1845e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira return -1; 1846e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } else if (firstStart > secondStart) { 1847e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira return 1; 1848e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } else { 1849e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira return 0; 1850e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } 1851e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } 1852e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira }); 1853194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy return recipientsList.toArray(new DrawableRecipientChip[recipientsList.size()]); 1854e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } 1855e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira 185612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira @Override 185712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 185812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira return false; 185912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 186012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 186112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira @Override 186212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira public void onDestroyActionMode(ActionMode mode) { 186312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 186412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 186512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira @Override 186612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 186712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira return false; 186812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 186912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 1870a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1871a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * No chips are selectable. 1872a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 187312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira @Override 187412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira public boolean onCreateActionMode(ActionMode mode, Menu menu) { 187512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira return false; 187612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 187712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 1878d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // Visible for testing. 1879d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira /* package */ImageSpan getMoreChip() { 1880dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira MoreImageSpan[] moreSpans = getSpannable().getSpans(0, getText().length(), 1881dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira MoreImageSpan.class); 1882dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira return moreSpans != null && moreSpans.length > 0 ? moreSpans[0] : null; 1883dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira } 1884dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira 1885f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira private MoreImageSpan createMoreSpan(int count) { 1886f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira String moreText = String.format(mMoreItem.getText().toString(), count); 1887f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira TextPaint morePaint = new TextPaint(getPaint()); 1888f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira morePaint.setTextSize(mMoreItem.getTextSize()); 1889f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira morePaint.setColor(mMoreItem.getCurrentTextColor()); 1890f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int width = (int)morePaint.measureText(moreText) + mMoreItem.getPaddingLeft() 1891f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira + mMoreItem.getPaddingRight(); 1892f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int height = getLineHeight(); 1893f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Bitmap drawable = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 1894f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Canvas canvas = new Canvas(drawable); 1895f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int adjustedHeight = height; 1896f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Layout layout = getLayout(); 1897f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (layout != null) { 1898f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira adjustedHeight -= layout.getLineDescent(0); 1899f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1900f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira canvas.drawText(moreText, 0, moreText.length(), 0, adjustedHeight, morePaint); 1901f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 1902f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Drawable result = new BitmapDrawable(getResources(), drawable); 1903f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira result.setBounds(0, 0, width, height); 1904f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return new MoreImageSpan(result); 1905f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1906f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 1907f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // Visible for testing. 1908f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira /*package*/ void createMoreChipPlainText() { 1909f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // Take the first <= CHIP_LIMIT addresses and get to the end of the second one. 1910f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Editable text = getText(); 1911f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int start = 0; 1912f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int end = start; 1913f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira for (int i = 0; i < CHIP_LIMIT; i++) { 1914f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira end = movePastTerminators(mTokenizer.findTokenEnd(text, start)); 1915f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira start = end; // move to the next token and get its end. 1916f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1917f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // Now, count total addresses. 1918f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira start = 0; 1919f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int tokenCount = countTokens(text); 1920f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira MoreImageSpan moreSpan = createMoreSpan(tokenCount - CHIP_LIMIT); 1921f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira SpannableString chipText = new SpannableString(text.subSequence(end, text.length())); 1922f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira chipText.setSpan(moreSpan, 0, chipText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 1923f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira text.replace(end, text.length(), chipText); 1924f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira mMoreChip = moreSpan; 1925f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1926f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 1927f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira // Visible for testing. 1928f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira /* package */int countTokens(Editable text) { 1929f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int tokenCount = 0; 1930f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira int start = 0; 1931f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira while (start < text.length()) { 1932f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira start = movePastTerminators(mTokenizer.findTokenEnd(text, start)); 1933f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira tokenCount++; 1934f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (start >= text.length()) { 1935f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira break; 1936f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1937f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1938f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return tokenCount; 1939f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1940f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 1941a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 1942342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * Create the more chip. The more chip is text that replaces any chips that 1943342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * do not fit in the pre-defined available space when the 1944342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira * RecipientEditTextView loses focus. 1945a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 1946d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // Visible for testing. 1947d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira /* package */ void createMoreChip() { 1948f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (mNoChips) { 1949f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira createMoreChipPlainText(); 1950f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return; 1951f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 1952f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 1953076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira if (!mShouldShrink) { 1954076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira return; 1955076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira } 1956e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira ImageSpan[] tempMore = getSpannable().getSpans(0, getText().length(), MoreImageSpan.class); 1957e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira if (tempMore.length > 0) { 1958e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira getSpannable().removeSpan(tempMore[0]); 1959e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } 1960194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] recipients = getSortedRecipients(); 1961f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira 1962d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira if (recipients == null || recipients.length <= CHIP_LIMIT) { 19633bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira mMoreChip = null; 19643bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira return; 196512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 1966e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira Spannable spannable = getSpannable(); 1967d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira int numRecipients = recipients.length; 196812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira int overage = numRecipients - CHIP_LIMIT; 1969f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira MoreImageSpan moreSpan = createMoreSpan(overage); 1970194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy mRemovedSpans = new ArrayList<DrawableRecipientChip>(); 197112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira int totalReplaceStart = 0; 197212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira int totalReplaceEnd = 0; 1973e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira Editable text = getText(); 197421cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira for (int i = numRecipients - overage; i < recipients.length; i++) { 197521cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira mRemovedSpans.add(recipients[i]); 1976364e7cf182a4124808d3a699dbea8dd1cc1d7bedMindy Pereira if (i == numRecipients - overage) { 197721cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira totalReplaceStart = spannable.getSpanStart(recipients[i]); 1978364e7cf182a4124808d3a699dbea8dd1cc1d7bedMindy Pereira } 197921cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira if (i == recipients.length - 1) { 198021cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira totalReplaceEnd = spannable.getSpanEnd(recipients[i]); 1981364e7cf182a4124808d3a699dbea8dd1cc1d7bedMindy Pereira } 1982e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira if (mTemporaryRecipients == null || !mTemporaryRecipients.contains(recipients[i])) { 1983e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira int spanStart = spannable.getSpanStart(recipients[i]); 1984e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira int spanEnd = spannable.getSpanEnd(recipients[i]); 1985e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira recipients[i].setOriginalText(text.toString().substring(spanStart, spanEnd)); 19861852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 198721cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira spannable.removeSpan(recipients[i]); 198812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 1989f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (totalReplaceEnd < text.length()) { 1990f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira totalReplaceEnd = text.length(); 1991f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 19921852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira int end = Math.max(totalReplaceStart, totalReplaceEnd); 19931852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira int start = Math.min(totalReplaceStart, totalReplaceEnd); 19941852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira SpannableString chipText = new SpannableString(text.subSequence(start, end)); 199512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira chipText.setSpan(moreSpan, 0, chipText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 19961852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira text.replace(start, end, chipText); 19973bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira mMoreChip = moreSpan; 19985e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp // If adding the +more chip goes over the limit, resize accordingly. 19995e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (!isPhoneQuery() && getLineCount() > mMaxLines) { 20005e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp setMaxLines(getLineCount()); 20015e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp } 200212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 200312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 2004a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira /** 2005a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * Replace the more chip, if it exists, with all of the recipient chips it had 2006a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira * replaced when the RecipientEditTextView gains focus. 2007a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira */ 2008d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira // Visible for testing. 2009d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira /*package*/ void removeMoreChip() { 201012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira if (mMoreChip != null) { 201112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira Spannable span = getSpannable(); 201212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira span.removeSpan(mMoreChip); 201312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira mMoreChip = null; 201412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira // Re-add the spans that were removed. 201512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira if (mRemovedSpans != null && mRemovedSpans.size() > 0) { 201612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira // Recreate each removed span. 2017194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] recipients = getSortedRecipients(); 201854effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira // Start the search for tokens after the last currently visible 201954effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira // chip. 2020ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira if (recipients == null || recipients.length == 0) { 2021ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira return; 2022ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira } 202354effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira int end = span.getSpanEnd(recipients[recipients.length - 1]); 202412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira Editable editable = getText(); 2025194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (DrawableRecipientChip chip : mRemovedSpans) { 2026e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira int chipStart; 20273bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira int chipEnd; 20283bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira String token; 2029e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira // Need to find the location of the chip, again. 2030e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira token = (String) chip.getOriginalText(); 203154effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira // As we find the matching recipient for the remove spans, 203254effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira // reduce the size of the string we need to search. 203354effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira // That way, if there are duplicates, we always find the correct 203454effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira // recipient. 203554effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira chipStart = editable.toString().indexOf(token, end); 203654effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira end = chipEnd = Math.min(editable.length(), chipStart + token.length()); 20370d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira // Only set the span if we found a matching token. 20380d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira if (chipStart != -1) { 20390d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira editable.setSpan(chip, chipStart, chipEnd, 20400d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 20410d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira } 204212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 204312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira mRemovedSpans.clear(); 204412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 204512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 204612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira } 204712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira 2048cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira /** 204995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * Show specified chip as selected. If the RecipientChip is just an email address, 205095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * selecting the chip will take the contents of the chip and place it at 205195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * the end of the RecipientEditTextView for inline editing. If the 205295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * RecipientChip is a complete contact, then selecting the chip 205395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * will change the background color of the chip, show the delete icon, 205495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * and a popup window with the address in use highlighted and any other 205595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * alternate addresses for the contact. 205695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * @param currentChip Chip to select. 205795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * @return A RecipientChip in the selected state or null if the chip 205895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * just contained an email address. 2059cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira */ 2060194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private DrawableRecipientChip selectChip(DrawableRecipientChip currentChip) { 20615e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (shouldShowEditableText(currentChip)) { 206201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira CharSequence text = currentChip.getValue(); 206301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira Editable editable = getText(); 2064f30a42800318f6790d55421f8f6980eb38db4d3cmindyp Spannable spannable = getSpannable(); 2065f30a42800318f6790d55421f8f6980eb38db4d3cmindyp int spanStart = spannable.getSpanStart(currentChip); 2066f30a42800318f6790d55421f8f6980eb38db4d3cmindyp int spanEnd = spannable.getSpanEnd(currentChip); 2067f30a42800318f6790d55421f8f6980eb38db4d3cmindyp spannable.removeSpan(currentChip); 2068f30a42800318f6790d55421f8f6980eb38db4d3cmindyp editable.delete(spanStart, spanEnd); 206901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira setCursorVisible(true); 207001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira setSelection(editable.length()); 2071f30a42800318f6790d55421f8f6980eb38db4d3cmindyp editable.append(text); 2072f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return constructChipSpan( 2073f30a42800318f6790d55421f8f6980eb38db4d3cmindyp RecipientEntry.constructFakeEntry((String) text, isValid(text.toString())), 2074f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy true, false); 20757a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy } else if (currentChip.getContactId() == RecipientEntry.GENERATED_CONTACT) { 207695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int start = getChipStart(currentChip); 207795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int end = getChipEnd(currentChip); 207895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira getSpannable().removeSpan(currentChip); 2079194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip newChip; 208095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira try { 2081f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (mNoChips) { 2082f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return null; 2083f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 2084f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy newChip = constructChipSpan(currentChip.getEntry(), true, false); 208595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } catch (NullPointerException e) { 208695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Log.e(TAG, e.getMessage(), e); 208795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira return null; 208895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 20890ab7e735e82c81baf9ab87d028611561ce0592b7Mindy Pereira Editable editable = getText(); 209095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 2091d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira if (start == -1 || end == -1) { 209295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Log.d(TAG, "The chip being selected no longer exists but should."); 2093d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira } else { 209404da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira editable.setSpan(newChip, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 2095d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira } 209695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira newChip.setSelected(true); 20975e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (shouldShowEditableText(newChip)) { 209801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira scrollLineIntoView(getLayout().getLineForOffset(getChipStart(newChip))); 209981fd3d1ed9ea08706e297a227fcab10eac2cf0e3Mindy Pereira } 2100f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy showAddress(newChip, mAddressPopup, getWidth()); 21014e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira setCursorVisible(false); 210295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira return newChip; 210395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } else { 210401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira int start = getChipStart(currentChip); 210501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira int end = getChipEnd(currentChip); 210601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira getSpannable().removeSpan(currentChip); 2107194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip newChip; 210801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira try { 2109f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy newChip = constructChipSpan(currentChip.getEntry(), true, false); 211001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } catch (NullPointerException e) { 211101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira Log.e(TAG, e.getMessage(), e); 211201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira return null; 211301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 211495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Editable editable = getText(); 211501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 211601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira if (start == -1 || end == -1) { 211701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira Log.d(TAG, "The chip being selected no longer exists but should."); 211801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } else { 211901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira editable.setSpan(newChip, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 212001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 212101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira newChip.setSelected(true); 21225e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp if (shouldShowEditableText(newChip)) { 212301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira scrollLineIntoView(getLayout().getLineForOffset(getChipStart(newChip))); 212401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 2125f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy showAlternates(newChip, mAlternatesPopup, getWidth()); 21264e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira setCursorVisible(false); 212701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira return newChip; 21280ab7e735e82c81baf9ab87d028611561ce0592b7Mindy Pereira } 212995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 21300ab7e735e82c81baf9ab87d028611561ce0592b7Mindy Pereira 2131194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private boolean shouldShowEditableText(DrawableRecipientChip currentChip) { 21325e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp long contactId = currentChip.getContactId(); 21335e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp return contactId == RecipientEntry.INVALID_CONTACT 21345e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT); 21355e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp } 2136cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 2137194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private void showAddress(final DrawableRecipientChip currentChip, final ListPopupWindow popup, 2138f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy int width) { 2139fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler if (!mAttachedToWindow) { 2140fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler return; 2141fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler } 214201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira int line = getLayout().getLineForOffset(getChipStart(currentChip)); 214301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira int bottom = calculateOffsetFromBottom(line); 214401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira // Align the alternates popup with the left side of the View, 214501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira // regardless of the position of the chip tapped. 214601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira popup.setWidth(width); 214701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira popup.setAnchorView(this); 214801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira popup.setVerticalOffset(bottom); 214901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira popup.setAdapter(createSingleAddressAdapter(currentChip)); 215001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira popup.setOnItemClickListener(new OnItemClickListener() { 215101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira @Override 215201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 215301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira unselectChip(currentChip); 215401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira popup.dismiss(); 215501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 215601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira }); 215701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira popup.show(); 215801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira ListView listView = popup.getListView(); 215901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 216001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira listView.setItemChecked(0, true); 216101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira } 216201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira 216395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira /** 216495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * Remove selection from this chip. Unselecting a RecipientChip will render 2165f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira * the chip without a delete icon and with an unfocused background. This is 2166f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira * called when the RecipientChip no longer has focus. 216795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira */ 2168194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private void unselectChip(DrawableRecipientChip chip) { 216995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int start = getChipStart(chip); 217095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int end = getChipEnd(chip); 217195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Editable editable = getText(); 2172e82e61f026fe8edfc12743fb038ddac9af5cf1efMindy Pereira mSelectedChip = null; 217395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (start == -1 || end == -1) { 2174f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira Log.w(TAG, "The chip doesn't exist or may be a chip a user was editing"); 2175c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira setSelection(editable.length()); 2176c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira commitDefault(); 217795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } else { 2178e82e61f026fe8edfc12743fb038ddac9af5cf1efMindy Pereira getSpannable().removeSpan(chip); 217995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira QwertyKeyListener.markAsReplaced(editable, start, end, ""); 218004da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira editable.removeSpan(chip); 218104da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira try { 2182f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (!mNoChips) { 2183f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy editable.setSpan(constructChipSpan(chip.getEntry(), false, false), 21845e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 2185f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 218604da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira } catch (NullPointerException e) { 218704da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira Log.e(TAG, e.getMessage(), e); 218804da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira } 2189cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 219095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira setCursorVisible(true); 219195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira setSelection(editable.length()); 219295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (mAlternatesPopup != null && mAlternatesPopup.isShowing()) { 219395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mAlternatesPopup.dismiss(); 2194cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 219595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 2196cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 219795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira /** 219895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * Return whether a touch event was inside the delete target of 219995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * a selected chip. It is in the delete target if: 220095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * 1) the x and y points of the event are within the 220195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * delete assset. 220295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * 2) the point tapped would have caused a cursor to appear 220395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * right after the selected chip. 220495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * @return boolean 220595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira */ 2206194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private boolean isInDelete(DrawableRecipientChip chip, int offset, float x, float y) { 220795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira // Figure out the bounds of this chip and whether or not 220895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira // the user clicked in the X portion. 2209f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy // TODO: Should x and y be used, or removed? 2210b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin if (mDisableDelete) { 2211b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return false; 2212b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin } 2213b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin 2214b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin return chip.isSelected() && 2215c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein ((mAvatarPosition == AVATAR_POSITION_END && offset == getChipEnd(chip)) || 2216c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein (mAvatarPosition != AVATAR_POSITION_END && offset == getChipStart(chip))); 221795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 22185086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira 221995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira /** 222095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * Remove the chip and any text associated with it from the RecipientEditTextView. 222195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira */ 222297cb25912dab282cf732757f68b0405ed005f00bMindy Pereira // Visible for testing. 2223b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin /* package */void removeChip(DrawableRecipientChip chip) { 222495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Spannable spannable = getSpannable(); 222595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int spanStart = spannable.getSpanStart(chip); 222695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int spanEnd = spannable.getSpanEnd(chip); 222795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Editable text = getText(); 222862397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira int toDelete = spanEnd; 222995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira boolean wasSelected = chip == mSelectedChip; 223095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira // Clear that there is a selected chip before updating any text. 223195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (wasSelected) { 223295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mSelectedChip = null; 2233cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 223462397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira // Always remove trailing spaces when removing a chip. 223562397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira while (toDelete >= 0 && toDelete < text.length() && text.charAt(toDelete) == ' ') { 223662397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira toDelete++; 223762397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira } 223895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira spannable.removeSpan(chip); 2239f6d4bfce7cae3cc385396963dc8ea3eda90b917eMindy Pereira if (spanStart >= 0 && toDelete > 0) { 2240f6d4bfce7cae3cc385396963dc8ea3eda90b917eMindy Pereira text.delete(spanStart, toDelete); 2241f6d4bfce7cae3cc385396963dc8ea3eda90b917eMindy Pereira } 224295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (wasSelected) { 224395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira clearSelectedChip(); 2244cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 224595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 2246cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 224795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira /** 224895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * Replace this currently selected chip with a new chip 224995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * that uses the contact data provided. 225095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira */ 2251aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira // Visible for testing. 2252194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy /*package*/ void replaceChip(DrawableRecipientChip chip, RecipientEntry entry) { 225395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira boolean wasSelected = chip == mSelectedChip; 225495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (wasSelected) { 225595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira mSelectedChip = null; 2256cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 225795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int start = getChipStart(chip); 225895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira int end = getChipEnd(chip); 225995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira getSpannable().removeSpan(chip); 226095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira Editable editable = getText(); 226195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira CharSequence chipText = createChip(entry, false); 22622d7709d276c03e536d37961076107af9f98522f5Mindy Pereira if (chipText != null) { 22632d7709d276c03e536d37961076107af9f98522f5Mindy Pereira if (start == -1 || end == -1) { 22642d7709d276c03e536d37961076107af9f98522f5Mindy Pereira Log.e(TAG, "The chip to replace does not exist but should."); 22652d7709d276c03e536d37961076107af9f98522f5Mindy Pereira editable.insert(0, chipText); 22662d7709d276c03e536d37961076107af9f98522f5Mindy Pereira } else { 22672d7709d276c03e536d37961076107af9f98522f5Mindy Pereira if (!TextUtils.isEmpty(chipText)) { 22682d7709d276c03e536d37961076107af9f98522f5Mindy Pereira // There may be a space to replace with this chip's new 2269b5afbc70a27f7534fd6dfa7440b3a3450bbc52cfmindyp // associated space. Check for it 22702d7709d276c03e536d37961076107af9f98522f5Mindy Pereira int toReplace = end; 22712d7709d276c03e536d37961076107af9f98522f5Mindy Pereira while (toReplace >= 0 && toReplace < editable.length() 22722d7709d276c03e536d37961076107af9f98522f5Mindy Pereira && editable.charAt(toReplace) == ' ') { 22732d7709d276c03e536d37961076107af9f98522f5Mindy Pereira toReplace++; 22742d7709d276c03e536d37961076107af9f98522f5Mindy Pereira } 22752d7709d276c03e536d37961076107af9f98522f5Mindy Pereira editable.replace(start, toReplace, chipText); 227662397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira } 22777ebb40ff05dbf28edd9bbed4eba7e57c8c6005aeMindy Pereira } 2278cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 227995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira setCursorVisible(true); 228095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (wasSelected) { 228195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira clearSelectedChip(); 2282cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 228395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 2284cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 228595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira /** 228695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * Handle click events for a chip. When a selected chip receives a click 228795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * event, see if that event was in the delete icon. If so, delete it. 228895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira * Otherwise, unselect the chip. 228995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira */ 2290194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy public void onClick(DrawableRecipientChip chip, int offset, float x, float y) { 229195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (chip.isSelected()) { 229295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira if (isInDelete(chip, offset, x, y)) { 229395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira removeChip(chip); 229495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } else { 229595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira clearSelectedChip(); 2296cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 2297cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira } 229895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira } 2299cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira 230021cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira private boolean chipsPending() { 230121cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira return mPendingChipsCount > 0 || (mRemovedSpans != null && mRemovedSpans.size() > 0); 230221cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira } 230321cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira 2304883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira @Override 2305883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira public void removeTextChangedListener(TextWatcher watcher) { 2306883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira mTextWatcher = null; 2307883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira super.removeTextChangedListener(watcher); 2308883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira } 2309883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira 231079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira private class RecipientTextWatcher implements TextWatcher { 231172a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira 231279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira @Override 231379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira public void afterTextChanged(Editable s) { 231432366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira // If the text has been set to null or empty, make sure we remove 231532366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira // all the spans we applied. 231632366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira if (TextUtils.isEmpty(s)) { 231732366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira // Remove all the chips spans. 231832366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira Spannable spannable = getSpannable(); 2319194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] chips = spannable.getSpans(0, getText().length(), 2320194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip.class); 2321194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (DrawableRecipientChip chip : chips) { 232232366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira spannable.removeSpan(chip); 232332366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira } 232432366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira if (mMoreChip != null) { 232532366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira spannable.removeSpan(mMoreChip); 232632366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira } 232741e93fbe82cd4d802e3f1fbe265038f7a0521dddKevin Lin clearSelectedChip(); 232832366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira return; 232932366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira } 233001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira // Get whether there are any recipients pending addition to the 233101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira // view. If there are, don't do anything in the text watcher. 233221cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira if (chipsPending()) { 233379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira return; 233479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 2335c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira // If the user is editing a chip, don't clear it. 2336f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (mSelectedChip != null) { 2337f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (!isGeneratedContact(mSelectedChip)) { 2338f30a42800318f6790d55421f8f6980eb38db4d3cmindyp setCursorVisible(true); 2339f30a42800318f6790d55421f8f6980eb38db4d3cmindyp setSelection(getText().length()); 2340f30a42800318f6790d55421f8f6980eb38db4d3cmindyp clearSelectedChip(); 2341f30a42800318f6790d55421f8f6980eb38db4d3cmindyp } else { 2342f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return; 2343f30a42800318f6790d55421f8f6980eb38db4d3cmindyp } 234479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 234579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira int length = s.length(); 234679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira // Make sure there is content there to parse and that it is 2347ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira // not just the commit character. 234879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira if (length > 1) { 2349f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (lastCharacterIsCommitCharacter(s)) { 2350f30a42800318f6790d55421f8f6980eb38db4d3cmindyp commitByCharacter(); 2351f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return; 2352f30a42800318f6790d55421f8f6980eb38db4d3cmindyp } 2353ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira char last; 235411a2adb53f764ffcadd262678ff782e6b4992decMindy Pereira int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1; 2355ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira int len = length() - 1; 2356ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira if (end != len) { 2357ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira last = s.charAt(end); 2358ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira } else { 2359ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira last = s.charAt(len); 2360ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira } 2361f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (last == COMMIT_CHAR_SPACE) { 23624afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor if (!isPhoneQuery()) { 23634afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor // Check if this is a valid email address. If it is, 23644afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor // commit it. 23654afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor String text = getText().toString(); 23664afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor int tokenStart = mTokenizer.findTokenStart(text, getSelectionEnd()); 23674afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor String sub = text.substring(tokenStart, mTokenizer.findTokenEnd(text, 23684afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor tokenStart)); 23694afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor if (!TextUtils.isEmpty(sub) && mValidator != null && 23704afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor mValidator.isValid(sub)) { 23714afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor commitByCharacter(); 23724afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor } 237379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 237479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 237579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 237679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 237779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira 237879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira @Override 237979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira public void onTextChanged(CharSequence s, int start, int before, int count) { 2380f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // The user deleted some text OR some text was replaced; check to 2381f30a42800318f6790d55421f8f6980eb38db4d3cmindyp // see if the insertion point is on a space 238272a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira // following a chip. 2383f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (before - count == 1) { 238472a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira // If the item deleted is a space, and the thing before the 238572a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira // space is a chip, delete the entire span. 238672a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira int selStart = getSelectionStart(); 2387194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] repl = getSpannable().getSpans(selStart, selStart, 2388194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip.class); 238972a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira if (repl.length > 0) { 239072a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira // There is a chip there! Just remove it. 239172a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira Editable editable = getText(); 239272a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira // Add the separator token. 239372a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira int tokenStart = mTokenizer.findTokenStart(editable, selStart); 239472a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira int tokenEnd = mTokenizer.findTokenEnd(editable, tokenStart); 239562fce9316c48a188d89c31d7e616f755bd7cf14cMindy Pereira tokenEnd = tokenEnd + 1; 239662fce9316c48a188d89c31d7e616f755bd7cf14cMindy Pereira if (tokenEnd > editable.length()) { 239762fce9316c48a188d89c31d7e616f755bd7cf14cMindy Pereira tokenEnd = editable.length(); 239862fce9316c48a188d89c31d7e616f755bd7cf14cMindy Pereira } 239962fce9316c48a188d89c31d7e616f755bd7cf14cMindy Pereira editable.delete(tokenStart, tokenEnd); 240072a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira getSpannable().removeSpan(repl[0]); 240172a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira } 24024e7d20f7b74ddce5a104138714c9683c8bd2300dMindy Pereira } else if (count > before) { 24037e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp if (mSelectedChip != null 24047e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp && isGeneratedContact(mSelectedChip)) { 24057e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp if (lastCharacterIsCommitCharacter(s)) { 24067e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp commitByCharacter(); 24077e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp return; 24087e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp } 24097e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp } 241072a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira } 241179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 241279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira 241379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira @Override 241479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira public void beforeTextChanged(CharSequence s, int start, int count, int after) { 2415dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira // Do nothing. 241679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 241779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira } 24181852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira 2419c1b5258fe0d244be2d4d8c4d2969c1b8859c6fa8Alice Yang public boolean lastCharacterIsCommitCharacter(CharSequence s) { 2420f30a42800318f6790d55421f8f6980eb38db4d3cmindyp char last; 2421f30a42800318f6790d55421f8f6980eb38db4d3cmindyp int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1; 2422f30a42800318f6790d55421f8f6980eb38db4d3cmindyp int len = length() - 1; 2423f30a42800318f6790d55421f8f6980eb38db4d3cmindyp if (end != len) { 2424f30a42800318f6790d55421f8f6980eb38db4d3cmindyp last = s.charAt(end); 2425f30a42800318f6790d55421f8f6980eb38db4d3cmindyp } else { 2426f30a42800318f6790d55421f8f6980eb38db4d3cmindyp last = s.charAt(len); 2427f30a42800318f6790d55421f8f6980eb38db4d3cmindyp } 2428f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return last == COMMIT_CHAR_COMMA || last == COMMIT_CHAR_SEMICOLON; 2429f30a42800318f6790d55421f8f6980eb38db4d3cmindyp } 2430f30a42800318f6790d55421f8f6980eb38db4d3cmindyp 2431194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy public boolean isGeneratedContact(DrawableRecipientChip chip) { 2432f30a42800318f6790d55421f8f6980eb38db4d3cmindyp long contactId = chip.getContactId(); 2433f30a42800318f6790d55421f8f6980eb38db4d3cmindyp return contactId == RecipientEntry.INVALID_CONTACT 2434f30a42800318f6790d55421f8f6980eb38db4d3cmindyp || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT); 2435f30a42800318f6790d55421f8f6980eb38db4d3cmindyp } 2436f30a42800318f6790d55421f8f6980eb38db4d3cmindyp 2437e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham /** 2438e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham * Handles pasting a {@link ClipData} to this {@link RecipientEditTextView}. 2439e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham */ 2440e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham private void handlePasteClip(ClipData clip) { 2441e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham removeTextChangedListener(mTextWatcher); 2442e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2443e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham if (clip != null && clip.getDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)){ 2444e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham for (int i = 0; i < clip.getItemCount(); i++) { 2445e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham CharSequence paste = clip.getItemAt(i).getText(); 2446e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham if (paste != null) { 2447e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham int start = getSelectionStart(); 2448e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham int end = getSelectionEnd(); 2449e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham Editable editable = getText(); 2450e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham if (start >= 0 && end >= 0 && start != end) { 2451e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham editable.append(paste, start, end); 2452e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } else { 2453e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham editable.insert(end, paste); 2454e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2455e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham handlePasteAndReplace(); 2456e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2457e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2458e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2459e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2460e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham mHandler.post(mAddTextWatcher); 2461e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2462e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 246320c9d620e79ae28994856541761a951074551518Mindy Pereira @Override 246420c9d620e79ae28994856541761a951074551518Mindy Pereira public boolean onTextContextMenuItem(int id) { 246520c9d620e79ae28994856541761a951074551518Mindy Pereira if (id == android.R.id.paste) { 246620c9d620e79ae28994856541761a951074551518Mindy Pereira ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService( 246720c9d620e79ae28994856541761a951074551518Mindy Pereira Context.CLIPBOARD_SERVICE); 2468e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham handlePasteClip(clipboard.getPrimaryClip()); 246920c9d620e79ae28994856541761a951074551518Mindy Pereira return true; 247020c9d620e79ae28994856541761a951074551518Mindy Pereira } 247120c9d620e79ae28994856541761a951074551518Mindy Pereira return super.onTextContextMenuItem(id); 247220c9d620e79ae28994856541761a951074551518Mindy Pereira } 247320c9d620e79ae28994856541761a951074551518Mindy Pereira 2474b4e244af14950aee7d612612d5406981315d3454Mindy Pereira private void handlePasteAndReplace() { 2475194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy ArrayList<DrawableRecipientChip> created = handlePaste(); 2476b4e244af14950aee7d612612d5406981315d3454Mindy Pereira if (created != null && created.size() > 0) { 2477b4e244af14950aee7d612612d5406981315d3454Mindy Pereira // Perform reverse lookups on the pasted contacts. 2478b4e244af14950aee7d612612d5406981315d3454Mindy Pereira IndividualReplacementTask replace = new IndividualReplacementTask(); 2479b4e244af14950aee7d612612d5406981315d3454Mindy Pereira replace.execute(created); 2480b4e244af14950aee7d612612d5406981315d3454Mindy Pereira } 2481b4e244af14950aee7d612612d5406981315d3454Mindy Pereira } 2482b4e244af14950aee7d612612d5406981315d3454Mindy Pereira 248320c9d620e79ae28994856541761a951074551518Mindy Pereira // Visible for testing. 2484194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy /* package */ArrayList<DrawableRecipientChip> handlePaste() { 248520c9d620e79ae28994856541761a951074551518Mindy Pereira String text = getText().toString(); 248620c9d620e79ae28994856541761a951074551518Mindy Pereira int originalTokenStart = mTokenizer.findTokenStart(text, getSelectionEnd()); 248720c9d620e79ae28994856541761a951074551518Mindy Pereira String lastAddress = text.substring(originalTokenStart); 248820c9d620e79ae28994856541761a951074551518Mindy Pereira int tokenStart = originalTokenStart; 24890cae85010120a2a8490baf3488ed0bd2293e1d4djli int prevTokenStart = 0; 2490194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip findChip = null; 2491194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy ArrayList<DrawableRecipientChip> created = new ArrayList<DrawableRecipientChip>(); 249220c9d620e79ae28994856541761a951074551518Mindy Pereira if (tokenStart != 0) { 249320c9d620e79ae28994856541761a951074551518Mindy Pereira // There are things before this! 24940cae85010120a2a8490baf3488ed0bd2293e1d4djli while (tokenStart != 0 && findChip == null && tokenStart != prevTokenStart) { 249520c9d620e79ae28994856541761a951074551518Mindy Pereira prevTokenStart = tokenStart; 249620c9d620e79ae28994856541761a951074551518Mindy Pereira tokenStart = mTokenizer.findTokenStart(text, tokenStart); 249720c9d620e79ae28994856541761a951074551518Mindy Pereira findChip = findChip(tokenStart); 2498fda0170c661e237c202a8519baba1ce3294c6afeTom Taylor if (tokenStart == originalTokenStart && findChip == null) { 2499fda0170c661e237c202a8519baba1ce3294c6afeTom Taylor break; 2500fda0170c661e237c202a8519baba1ce3294c6afeTom Taylor } 250120c9d620e79ae28994856541761a951074551518Mindy Pereira } 250220c9d620e79ae28994856541761a951074551518Mindy Pereira if (tokenStart != originalTokenStart) { 250320c9d620e79ae28994856541761a951074551518Mindy Pereira if (findChip != null) { 250420c9d620e79ae28994856541761a951074551518Mindy Pereira tokenStart = prevTokenStart; 250520c9d620e79ae28994856541761a951074551518Mindy Pereira } 250620c9d620e79ae28994856541761a951074551518Mindy Pereira int tokenEnd; 2507194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip createdChip; 250820c9d620e79ae28994856541761a951074551518Mindy Pereira while (tokenStart < originalTokenStart) { 2509b5afbc70a27f7534fd6dfa7440b3a3450bbc52cfmindyp tokenEnd = movePastTerminators(mTokenizer.findTokenEnd(getText().toString(), 2510b5afbc70a27f7534fd6dfa7440b3a3450bbc52cfmindyp tokenStart)); 251120c9d620e79ae28994856541761a951074551518Mindy Pereira commitChip(tokenStart, tokenEnd, getText()); 251220c9d620e79ae28994856541761a951074551518Mindy Pereira createdChip = findChip(tokenStart); 2513c52ea8e900948bf42ed63b2d3d6c9afbd70d9df0Mindy Pereira if (createdChip == null) { 2514c52ea8e900948bf42ed63b2d3d6c9afbd70d9df0Mindy Pereira break; 2515c52ea8e900948bf42ed63b2d3d6c9afbd70d9df0Mindy Pereira } 251620c9d620e79ae28994856541761a951074551518Mindy Pereira // +1 for the space at the end. 251720c9d620e79ae28994856541761a951074551518Mindy Pereira tokenStart = getSpannable().getSpanEnd(createdChip) + 1; 2518b4e244af14950aee7d612612d5406981315d3454Mindy Pereira created.add(createdChip); 251920c9d620e79ae28994856541761a951074551518Mindy Pereira } 252020c9d620e79ae28994856541761a951074551518Mindy Pereira } 252120c9d620e79ae28994856541761a951074551518Mindy Pereira } 252220c9d620e79ae28994856541761a951074551518Mindy Pereira // Take a look at the last token. If the token has been completed with a 252320c9d620e79ae28994856541761a951074551518Mindy Pereira // commit character, create a chip. 252420c9d620e79ae28994856541761a951074551518Mindy Pereira if (isCompletedToken(lastAddress)) { 252520c9d620e79ae28994856541761a951074551518Mindy Pereira Editable editable = getText(); 2526b4e244af14950aee7d612612d5406981315d3454Mindy Pereira tokenStart = editable.toString().indexOf(lastAddress, originalTokenStart); 2527b4e244af14950aee7d612612d5406981315d3454Mindy Pereira commitChip(tokenStart, editable.length(), editable); 2528b4e244af14950aee7d612612d5406981315d3454Mindy Pereira created.add(findChip(tokenStart)); 252920c9d620e79ae28994856541761a951074551518Mindy Pereira } 2530b4e244af14950aee7d612612d5406981315d3454Mindy Pereira return created; 253120c9d620e79ae28994856541761a951074551518Mindy Pereira } 253220c9d620e79ae28994856541761a951074551518Mindy Pereira 253320c9d620e79ae28994856541761a951074551518Mindy Pereira // Visible for testing. 253420c9d620e79ae28994856541761a951074551518Mindy Pereira /* package */int movePastTerminators(int tokenEnd) { 253520c9d620e79ae28994856541761a951074551518Mindy Pereira if (tokenEnd >= length()) { 253620c9d620e79ae28994856541761a951074551518Mindy Pereira return tokenEnd; 253720c9d620e79ae28994856541761a951074551518Mindy Pereira } 253820c9d620e79ae28994856541761a951074551518Mindy Pereira char atEnd = getText().toString().charAt(tokenEnd); 253920c9d620e79ae28994856541761a951074551518Mindy Pereira if (atEnd == COMMIT_CHAR_COMMA || atEnd == COMMIT_CHAR_SEMICOLON) { 254020c9d620e79ae28994856541761a951074551518Mindy Pereira tokenEnd++; 254120c9d620e79ae28994856541761a951074551518Mindy Pereira } 254220c9d620e79ae28994856541761a951074551518Mindy Pereira // This token had not only an end token character, but also a space 254320c9d620e79ae28994856541761a951074551518Mindy Pereira // separating it from the next token. 254420c9d620e79ae28994856541761a951074551518Mindy Pereira if (tokenEnd < length() && getText().toString().charAt(tokenEnd) == ' ') { 254520c9d620e79ae28994856541761a951074551518Mindy Pereira tokenEnd++; 254620c9d620e79ae28994856541761a951074551518Mindy Pereira } 254720c9d620e79ae28994856541761a951074551518Mindy Pereira return tokenEnd; 254820c9d620e79ae28994856541761a951074551518Mindy Pereira } 254920c9d620e79ae28994856541761a951074551518Mindy Pereira 25501852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira private class RecipientReplacementTask extends AsyncTask<Void, Void, Void> { 2551194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private DrawableRecipientChip createFreeChip(RecipientEntry entry) { 25521852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira try { 2553f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira if (mNoChips) { 2554f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira return null; 2555f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira } 2556f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy return constructChipSpan(entry, false, 25575e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp false /*leave space for contact icon */); 25581852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } catch (NullPointerException e) { 25591852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira Log.e(TAG, e.getMessage(), e); 25601852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira return null; 25611852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 25621852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 25631852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira 25641852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira @Override 256578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy protected void onPreExecute() { 256678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // Ensure everything is in chip-form already, so we don't have text that slowly gets 256778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // replaced 2568194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final List<DrawableRecipientChip> originalRecipients = 2569194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy new ArrayList<DrawableRecipientChip>(); 2570194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final DrawableRecipientChip[] existingChips = getSortedRecipients(); 257178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy for (int i = 0; i < existingChips.length; i++) { 257278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy originalRecipients.add(existingChips[i]); 257378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 257478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy if (mRemovedSpans != null) { 257578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy originalRecipients.addAll(mRemovedSpans); 257678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 257778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 2578194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final List<DrawableRecipientChip> replacements = 2579194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy new ArrayList<DrawableRecipientChip>(originalRecipients.size()); 258078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 2581194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (final DrawableRecipientChip chip : originalRecipients) { 258278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy if (RecipientEntry.isCreatedRecipient(chip.getEntry().getContactId()) 258378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy && getSpannable().getSpanStart(chip) != -1) { 258478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy replacements.add(createFreeChip(chip.getEntry())); 258578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } else { 258678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy replacements.add(null); 258778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 258878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 258978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 259078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy processReplacements(originalRecipients, replacements); 259178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 259278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 259378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy @Override 25941852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira protected Void doInBackground(Void... params) { 25951852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira if (mIndividualReplacements != null) { 25961852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira mIndividualReplacements.cancel(true); 25971852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 25981852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // For each chip in the list, look up the matching contact. 25991852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // If there is a match, replace that chip with the matching 26001852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // chip. 2601194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final ArrayList<DrawableRecipientChip> recipients = 2602194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy new ArrayList<DrawableRecipientChip>(); 2603194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip[] existingChips = getSortedRecipients(); 26041852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira for (int i = 0; i < existingChips.length; i++) { 260578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy recipients.add(existingChips[i]); 26061852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 26071852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira if (mRemovedSpans != null) { 260878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy recipients.addAll(mRemovedSpans); 26091852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 261003cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira ArrayList<String> addresses = new ArrayList<String>(); 2611194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip chip; 261278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy for (int i = 0; i < recipients.size(); i++) { 261378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy chip = recipients.get(i); 2614f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira if (chip != null) { 261503cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira addresses.add(createAddressText(chip.getEntry())); 2616f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira } 26171852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 26187a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy final BaseRecipientAdapter adapter = getAdapter(); 261976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert RecipientAlternatesAdapter.getMatchingRecipients(getContext(), adapter, addresses, 26206b7110f320c978c368c28bdb06212c6a6df12f1fAlice Yang adapter.getAccount(), new RecipientMatchCallback() { 262116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp @Override 262294fa301de71cc110671802eba8376c275b4055a4Scott Kennedy public void matchesFound(Map<String, RecipientEntry> entries) { 2623194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final ArrayList<DrawableRecipientChip> replacements = 2624194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy new ArrayList<DrawableRecipientChip>(); 2625194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (final DrawableRecipientChip temp : recipients) { 262616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp RecipientEntry entry = null; 2627f0579ee4ab41c021f20f78b25ecf4c526b308ec6Alice Yang if (temp != null && RecipientEntry.isCreatedRecipient( 2628f0579ee4ab41c021f20f78b25ecf4c526b308ec6Alice Yang temp.getEntry().getContactId()) 262916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp && getSpannable().getSpanStart(temp) != -1) { 263016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp // Replace this. 263116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp entry = createValidatedEntry( 263216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp entries.get(tokenizeAddress(temp.getEntry() 263316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp .getDestination()))); 263416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } 263516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp if (entry != null) { 263616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp replacements.add(createFreeChip(entry)); 263716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } else { 263816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp replacements.add(null); 263916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } 264016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } 264178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy processReplacements(recipients, replacements); 26421852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 264394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy 264494fa301de71cc110671802eba8376c275b4055a4Scott Kennedy @Override 2645f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy public void matchesNotFound(final Set<String> unfoundAddresses) { 2646194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final List<DrawableRecipientChip> replacements = 2647194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy new ArrayList<DrawableRecipientChip>(unfoundAddresses.size()); 264894fa301de71cc110671802eba8376c275b4055a4Scott Kennedy 2649194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (final DrawableRecipientChip temp : recipients) { 2650ad8781e6062f086aa4160eab2799b6848911aa3dAlice Yang if (temp != null && RecipientEntry.isCreatedRecipient( 2651ad8781e6062f086aa4160eab2799b6848911aa3dAlice Yang temp.getEntry().getContactId()) 265294fa301de71cc110671802eba8376c275b4055a4Scott Kennedy && getSpannable().getSpanStart(temp) != -1) { 2653f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy if (unfoundAddresses.contains( 2654f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy temp.getEntry().getDestination())) { 265594fa301de71cc110671802eba8376c275b4055a4Scott Kennedy replacements.add(createFreeChip(temp.getEntry())); 265694fa301de71cc110671802eba8376c275b4055a4Scott Kennedy } else { 265794fa301de71cc110671802eba8376c275b4055a4Scott Kennedy replacements.add(null); 265894fa301de71cc110671802eba8376c275b4055a4Scott Kennedy } 265994fa301de71cc110671802eba8376c275b4055a4Scott Kennedy } else { 266094fa301de71cc110671802eba8376c275b4055a4Scott Kennedy replacements.add(null); 266194fa301de71cc110671802eba8376c275b4055a4Scott Kennedy } 266294fa301de71cc110671802eba8376c275b4055a4Scott Kennedy } 266394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy 266478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy processReplacements(recipients, replacements); 266594fa301de71cc110671802eba8376c275b4055a4Scott Kennedy } 266616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp }); 26671852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira return null; 26681852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 266978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 2670194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private void processReplacements(final List<DrawableRecipientChip> recipients, 2671194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final List<DrawableRecipientChip> replacements) { 267278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy if (replacements != null && replacements.size() > 0) { 267378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy final Runnable runnable = new Runnable() { 267478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy @Override 267578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy public void run() { 26760e128968f242866568bba0f833bb17ffda127f07Scott Kennedy final Editable text = new SpannableStringBuilder(getText()); 267778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy int i = 0; 26780e128968f242866568bba0f833bb17ffda127f07Scott Kennedy for (final DrawableRecipientChip chip : recipients) { 26790e128968f242866568bba0f833bb17ffda127f07Scott Kennedy final DrawableRecipientChip replacement = replacements.get(i); 268078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy if (replacement != null) { 268178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy final RecipientEntry oldEntry = chip.getEntry(); 268278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy final RecipientEntry newEntry = replacement.getEntry(); 268378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy final boolean isBetter = 268478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy RecipientAlternatesAdapter.getBetterRecipient( 268578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy oldEntry, newEntry) == newEntry; 268678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 268778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy if (isBetter) { 268878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // Find the location of the chip in the text currently shown. 26890e128968f242866568bba0f833bb17ffda127f07Scott Kennedy final int start = text.getSpanStart(chip); 269078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy if (start != -1) { 269178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // Replacing the entirety of what the chip represented, 269278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // including the extra space dividing it from other chips. 2693b023f69b1f35199bc17f1d2708acb492d3ae49b0Paul Westbrook final int end = 2694b023f69b1f35199bc17f1d2708acb492d3ae49b0Paul Westbrook Math.min(text.getSpanEnd(chip) + 1, text.length()); 26950e128968f242866568bba0f833bb17ffda127f07Scott Kennedy text.removeSpan(chip); 269678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // Make sure we always have just 1 space at the end to 269778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // separate this chip from the next chip. 26980e128968f242866568bba0f833bb17ffda127f07Scott Kennedy final SpannableString displayText = 269978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy new SpannableString(createAddressText( 27000e128968f242866568bba0f833bb17ffda127f07Scott Kennedy replacement.getEntry()).trim() + " "); 270178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy displayText.setSpan(replacement, 0, 270278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy displayText.length() - 1, 270378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 270478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // Replace the old text we found with with the new display 270578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // text, which now may also contain the display name of the 270678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy // recipient. 27070e128968f242866568bba0f833bb17ffda127f07Scott Kennedy text.replace(start, end, displayText); 270878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy replacement.setOriginalText(displayText.toString()); 270978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy replacements.set(i, null); 271078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 271178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy recipients.set(i, replacement); 271278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 271378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 271478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 271578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy i++; 271678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 27170e128968f242866568bba0f833bb17ffda127f07Scott Kennedy setText(text); 271878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 271978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy }; 272078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy 272178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy if (Looper.myLooper() == Looper.getMainLooper()) { 272278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy runnable.run(); 272378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } else { 272478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy mHandler.post(runnable); 272578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 272678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 272778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy } 27281852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 27291852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira 273078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy private class IndividualReplacementTask 2731194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy extends AsyncTask<ArrayList<DrawableRecipientChip>, Void, Void> { 27321852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira @Override 2733194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy protected Void doInBackground(ArrayList<DrawableRecipientChip>... params) { 27341852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // For each chip in the list, look up the matching contact. 27351852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // If there is a match, replace that chip with the matching 27361852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira // chip. 2737194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy final ArrayList<DrawableRecipientChip> originalRecipients = params[0]; 273803cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira ArrayList<String> addresses = new ArrayList<String>(); 2739194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip chip; 27401852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira for (int i = 0; i < originalRecipients.size(); i++) { 2741f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira chip = originalRecipients.get(i); 2742f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira if (chip != null) { 274303cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira addresses.add(createAddressText(chip.getEntry())); 2744f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira } 27451852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 27467a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy final BaseRecipientAdapter adapter = getAdapter(); 274776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert RecipientAlternatesAdapter.getMatchingRecipients(getContext(), adapter, addresses, 2748858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy adapter.getAccount(), 274916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp new RecipientMatchCallback() { 275016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp 275116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp @Override 275294fa301de71cc110671802eba8376c275b4055a4Scott Kennedy public void matchesFound(Map<String, RecipientEntry> entries) { 2753194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy for (final DrawableRecipientChip temp : originalRecipients) { 275416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp if (RecipientEntry.isCreatedRecipient(temp.getEntry() 275516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp .getContactId()) 275616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp && getSpannable().getSpanStart(temp) != -1) { 275716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp // Replace this. 275876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert final RecipientEntry entry = createValidatedEntry(entries 275916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp .get(tokenizeAddress(temp.getEntry().getDestination()) 276016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp .toLowerCase())); 276176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert if (entry != null) { 276216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp mHandler.post(new Runnable() { 276316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp @Override 276416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp public void run() { 276576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert replaceChip(temp, entry); 276616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } 276716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp }); 276816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } 276916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } 27701852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 277116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp } 277216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp 277394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy @Override 2774f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy public void matchesNotFound(final Set<String> unfoundAddresses) { 277594fa301de71cc110671802eba8376c275b4055a4Scott Kennedy // No action required 277694fa301de71cc110671802eba8376c275b4055a4Scott Kennedy } 277716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp }); 27781852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira return null; 27791852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 27801852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira } 27811d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 2782e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira 2783e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira /** 2784e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira * MoreImageSpan is a simple class created for tracking the existence of a 2785e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira * more chip across activity restarts/ 2786e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira */ 2787e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira private class MoreImageSpan extends ImageSpan { 2788e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira public MoreImageSpan(Drawable b) { 2789e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira super(b); 2790e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } 2791e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira } 2792e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira 27931d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 27941d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public boolean onDown(MotionEvent e) { 27951d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira return false; 27961d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 27971d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 27981d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 27991d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 28001d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira // Do nothing. 28011d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira return false; 28021d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 28031d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 28041d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 28051d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public void onLongPress(MotionEvent event) { 28061d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira if (mSelectedChip != null) { 28071d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira return; 28081d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 28091d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira float x = event.getX(); 28101d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira float y = event.getY(); 28111650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy final int offset = putOffsetInRange(x, y); 2812194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy DrawableRecipientChip currentChip = findChip(offset); 28131d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira if (currentChip != null) { 2814e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham if (mDragEnabled) { 2815e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham // Start drag-and-drop for the selected chip. 2816e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham startDrag(currentChip); 2817e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } else { 2818e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham // Copy the selected chip email address. 2819e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham showCopyDialog(currentChip.getEntry().getDestination()); 2820e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2821e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2822e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2823e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 28241650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy // The following methods are used to provide some functionality on older versions of Android 28251650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy // These methods were copied out of JB MR2's TextView 28261650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy ///////////////////////////////////////////////// 28271650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy private int supportGetOffsetForPosition(float x, float y) { 28281650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy if (getLayout() == null) return -1; 28291650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy final int line = supportGetLineAtCoordinate(y); 28301650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy final int offset = supportGetOffsetAtCoordinate(line, x); 28311650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy return offset; 28321650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy } 28331650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy 28341650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy private float supportConvertToLocalHorizontalCoordinate(float x) { 28351650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy x -= getTotalPaddingLeft(); 28361650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy // Clamp the position to inside of the view. 28371650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy x = Math.max(0.0f, x); 28381650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy x = Math.min(getWidth() - getTotalPaddingRight() - 1, x); 28391650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy x += getScrollX(); 28401650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy return x; 28411650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy } 28421650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy 28431650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy private int supportGetLineAtCoordinate(float y) { 28441650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy y -= getTotalPaddingLeft(); 28451650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy // Clamp the position to inside of the view. 28461650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy y = Math.max(0.0f, y); 28471650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y); 28481650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy y += getScrollY(); 28491650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy return getLayout().getLineForVertical((int) y); 28501650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy } 28511650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy 28521650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy private int supportGetOffsetAtCoordinate(int line, float x) { 28531650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy x = supportConvertToLocalHorizontalCoordinate(x); 28541650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy return getLayout().getOffsetForHorizontal(line, x); 28551650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy } 28561650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy ///////////////////////////////////////////////// 28571650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy 2858e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham /** 2859e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham * Enables drag-and-drop for chips. 2860e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham */ 2861e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham public void enableDrag() { 2862e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham mDragEnabled = true; 2863e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2864e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2865e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham /** 2866e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham * Starts drag-and-drop for the selected chip. 2867e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham */ 2868194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private void startDrag(DrawableRecipientChip currentChip) { 2869e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham String address = currentChip.getEntry().getDestination(); 2870e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham ClipData data = ClipData.newPlainText(address, address + COMMIT_CHAR_COMMA); 2871e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2872e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham // Start drag mode. 2873e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham startDrag(data, new RecipientChipShadow(currentChip), null, 0); 2874e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2875e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham // Remove the current chip, so drag-and-drop will result in a move. 2876e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham // TODO (phamm): consider readd this chip if it's dropped outside a target. 2877e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham removeChip(currentChip); 2878e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2879e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2880e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham /** 2881e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham * Handles drag event. 2882e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham */ 2883e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham @Override 2884e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham public boolean onDragEvent(DragEvent event) { 2885e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham switch (event.getAction()) { 2886e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham case DragEvent.ACTION_DRAG_STARTED: 2887e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham // Only handle plain text drag and drop. 2888e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham return event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN); 2889e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham case DragEvent.ACTION_DRAG_ENTERED: 2890e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham requestFocus(); 2891e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham return true; 2892e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham case DragEvent.ACTION_DROP: 2893e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham handlePasteClip(event.getClipData()); 2894e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham return true; 2895e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2896e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham return false; 2897e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2898e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2899e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham /** 2900e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham * Drag shadow for a {@link RecipientChip}. 2901e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham */ 2902e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham private final class RecipientChipShadow extends DragShadowBuilder { 2903194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy private final DrawableRecipientChip mChip; 2904e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2905194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy public RecipientChipShadow(DrawableRecipientChip chip) { 2906e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham mChip = chip; 2907e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2908e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2909e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham @Override 2910e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { 2911f30a42800318f6790d55421f8f6980eb38db4d3cmindyp Rect rect = mChip.getBounds(); 2912e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham shadowSize.set(rect.width(), rect.height()); 2913e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham shadowTouchPoint.set(rect.centerX(), rect.centerY()); 2914e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham } 2915e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham 2916e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham @Override 2917e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham public void onDrawShadow(Canvas canvas) { 2918f30a42800318f6790d55421f8f6980eb38db4d3cmindyp mChip.draw(canvas); 29191d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 29201d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 29211d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 29221d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira private void showCopyDialog(final String address) { 2923fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler if (!mAttachedToWindow) { 2924fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler return; 2925fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler } 29261d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyAddress = address; 29271d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyDialog.setTitle(address); 292876e62e334ef2b9ec55e5395f8374072f7ee1ea84Mindy Pereira mCopyDialog.setContentView(R.layout.copy_chip_dialog_layout); 29291d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyDialog.setCancelable(true); 29301d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyDialog.setCanceledOnTouchOutside(true); 293180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor Button button = (Button)mCopyDialog.findViewById(android.R.id.button1); 293280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor button.setOnClickListener(this); 293380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor int btnTitleId; 2934c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor if (isPhoneQuery()) { 293580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor btnTitleId = R.string.copy_number; 293680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor } else { 293780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor btnTitleId = R.string.copy_email; 293880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor } 293980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor String buttonTitle = getContext().getResources().getString(btnTitleId); 294080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor button.setText(buttonTitle); 29411d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyDialog.setOnDismissListener(this); 29421d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyDialog.show(); 29431d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 29441d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 29451d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 29461d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 29471d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira // Do nothing. 29481d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira return false; 29491d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 29501d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 29511d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 29521d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public void onShowPress(MotionEvent e) { 29531d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira // Do nothing. 29541d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 29551d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 29561d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 29571d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public boolean onSingleTapUp(MotionEvent e) { 29581d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira // Do nothing. 29591d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira return false; 29601d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 29611d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 29621d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 29631d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public void onDismiss(DialogInterface dialog) { 29641d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyAddress = null; 29651d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 29661d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira 29671d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira @Override 29681d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira public void onClick(View v) { 29691d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira // Copy this to the clipboard. 29701d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService( 29711d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira Context.CLIPBOARD_SERVICE); 29721d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira clipboard.setPrimaryClip(ClipData.newPlainText("", mCopyAddress)); 29731d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira mCopyDialog.dismiss(); 29741d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira } 2975c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor 2976c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor protected boolean isPhoneQuery() { 2977ca5227122474fb124a4fe573a2f07e14d7a33f3eMindy Pereira return getAdapter() != null 2978858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy && getAdapter().getQueryType() == BaseRecipientAdapter.QUERY_TYPE_PHONE; 2979858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy } 2980858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy 2981858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy @Override 2982858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy public BaseRecipientAdapter getAdapter() { 2983858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy return (BaseRecipientAdapter) super.getAdapter(); 2984c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor } 29851852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira} 2986