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
20ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Caoimport android.annotation.TargetApi;
21c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sappersteinimport android.app.Activity;
22e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadamimport android.app.AlertDialog;
23c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sappersteinimport android.app.DialogFragment;
241d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.content.ClipData;
2520c9d620e79ae28994856541761a951074551518Mindy Pereiraimport android.content.ClipDescription;
261d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.content.ClipboardManager;
279159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.content.Context;
28b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadamimport android.content.DialogInterface;
2952c441e2c03e0f48572348953b985a4bf989c057Mindy Pereiraimport android.content.res.Resources;
3022faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereiraimport android.content.res.TypedArray;
31cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.Bitmap;
322bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereiraimport android.graphics.BitmapFactory;
33b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedyimport android.graphics.BitmapShader;
34cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.Canvas;
35b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedyimport android.graphics.Color;
362bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereiraimport android.graphics.Matrix;
37b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport android.graphics.Paint;
38b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedyimport android.graphics.Paint.Style;
39e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Phamimport android.graphics.Point;
40cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.Rect;
412bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereiraimport android.graphics.RectF;
42b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedyimport android.graphics.Shader.TileMode;
43cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.graphics.drawable.BitmapDrawable;
449159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.graphics.drawable.Drawable;
454db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Caoimport android.graphics.drawable.StateListDrawable;
461852931de1e24e77cb708f4ba010eaa269426657Mindy Pereiraimport android.os.AsyncTask;
471650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedyimport android.os.Build;
48b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadamimport android.os.Bundle;
49007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereiraimport android.os.Handler;
5078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedyimport android.os.Looper;
51007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereiraimport android.os.Message;
52dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereiraimport android.os.Parcelable;
53ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Caoimport android.support.annotation.NonNull;
549159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.text.Editable;
5561b48ccefc655549802556947eb8cf3959c6ddadGilles Debunneimport android.text.InputType;
56cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.Layout;
57cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.Spannable;
58cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.SpannableString;
590e128968f242866568bba0f833bb17ffda127f07Scott Kennedyimport android.text.SpannableStringBuilder;
60cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.Spanned;
61cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.TextPaint;
62cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.TextUtils;
63b88ee450829eb4ac24fb47c377b9ec3aab0782daMindy Pereiraimport android.text.TextWatcher;
64cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.text.method.QwertyKeyListener;
653bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereiraimport android.text.util.Rfc822Token;
663bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereiraimport android.text.util.Rfc822Tokenizer;
679159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.util.AttributeSet;
68cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.util.Log;
69fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereiraimport android.view.ActionMode;
70aa2afffe7aba707c2406f2e4503fa6037c4cd196Andy Stadlerimport android.view.ActionMode.Callback;
71e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Phamimport android.view.DragEvent;
721d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereiraimport android.view.GestureDetector;
73cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.view.KeyEvent;
74750e6e52d9e560d5fbf687f15bf388a947e98eb2Mindy Pereiraimport android.view.LayoutInflater;
75fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereiraimport android.view.Menu;
76fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereiraimport android.view.MenuItem;
77cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.view.MotionEvent;
78cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.view.View;
79c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereiraimport android.view.ViewParent;
8004f06ea723b0445428d3f563334d653299a73a74Jin Caoimport android.view.accessibility.AccessibilityEvent;
8104f06ea723b0445428d3f563334d653299a73a74Jin Caoimport android.view.accessibility.AccessibilityManager;
8293364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereiraimport android.view.inputmethod.EditorInfo;
8393364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereiraimport android.view.inputmethod.InputConnection;
84cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.widget.AdapterView;
85cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.widget.AdapterView.OnItemClickListener;
86093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindypimport android.widget.Filterable;
8795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereiraimport android.widget.ListAdapter;
88cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereiraimport android.widget.ListPopupWindow;
89007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereiraimport android.widget.ListView;
909159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereiraimport android.widget.MultiAutoCompleteTextView;
91939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Caoimport android.widget.PopupWindow;
92c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereiraimport android.widget.ScrollView;
93750e6e52d9e560d5fbf687f15bf388a947e98eb2Mindy Pereiraimport android.widget.TextView;
947537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa
95f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovicimport com.android.ex.chips.DropdownChipLayouter.PermissionRequestDismissedListener;
9616923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport com.android.ex.chips.RecipientAlternatesAdapter.RecipientMatchCallback;
97194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedyimport com.android.ex.chips.recipientchip.DrawableRecipientChip;
98194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedyimport com.android.ex.chips.recipientchip.InvisibleRecipientChip;
996c2a016fb00482339dc66b93822042f9f9e2424fJin Caoimport com.android.ex.chips.recipientchip.ReplacementDrawableSpan;
100194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedyimport com.android.ex.chips.recipientchip.VisibleRecipientChip;
101cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
10276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.ArrayList;
10376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Arrays;
10476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Collections;
10576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Comparator;
106e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadamimport java.util.HashSet;
10776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.List;
10876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Map;
10976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albertimport java.util.Set;
11076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
1119159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira/**
1129159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * RecipientEditTextView is an auto complete text view for use with applications
1139159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira * that use the new Chips UI for addressing a message to recipients.
1149159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira */
11595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereirapublic class RecipientEditTextView extends MultiAutoCompleteTextView implements
1161d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        OnItemClickListener, Callback, RecipientAlternatesAdapter.OnCheckedItemChangedListener,
117c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sapperstein        GestureDetector.OnGestureListener, TextView.OnEditorActionListener,
118f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        DropdownChipLayouter.ChipDeleteListener, PermissionRequestDismissedListener {
119a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private static final String TAG = "RecipientEditTextView";
120cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
121aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private static final char COMMIT_CHAR_COMMA = ',';
122aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private static final char COMMIT_CHAR_SEMICOLON = ';';
123aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private static final char COMMIT_CHAR_SPACE = ' ';
12403e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy    private static final String SEPARATOR = String.valueOf(COMMIT_CHAR_COMMA)
12503e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy            + String.valueOf(COMMIT_CHAR_SPACE);
12603e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy
127f11483ad7d1e7abbce9d59ecf01b4b904a777b05Scott Kennedy    private static final int DISMISS = "dismiss".hashCode();
128aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private static final long DISMISS_DELAY = 300;
129aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira
13012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    // TODO: get correct number/ algorithm from with UX.
131f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    // Visible for testing.
132f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    /*package*/ static final int CHIP_LIMIT = 2;
133f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
134f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    private static final int MAX_CHIPS_PARSED = 50;
135b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam    public static final String STATE_TEXT_VIEW = "savedTextView";
136b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam    public static final String STATE_CURRENT_WARNING_TEXT = "savedCurrentWarningText";
13712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
138cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang    private int mUnselectedChipTextColor;
139cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang    private int mUnselectedChipBackgroundColor;
140aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira
141fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao    // Work variables to avoid re-allocation on every typed character.
142fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao    private final Rect mRect = new Rect();
143fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao    private final int[] mCoords = new int[2];
144fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao
145aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Resources for displaying chips.
146cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    private Drawable mChipBackground = null;
147cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    private Drawable mChipDelete = null;
148aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private Drawable mInvalidChipBackground;
149aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira
150a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    // Possible attr overrides
151aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private float mChipHeight;
152aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private float mChipFontSize;
153ffd270a55d80db66bd6a1d7b786443fdc00af372Mindy Pereira    private float mLineSpacingExtra;
15458d541de27090b4bd55746368b3b3b1486a7c094Jin Cao    private int mChipTextStartPadding;
15558d541de27090b4bd55746368b3b3b1486a7c094Jin Cao    private int mChipTextEndPadding;
15615259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy    private final int mTextHeight;
157a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private boolean mDisableDelete;
158a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private int mMaxLines;
159e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private int mWarningIconHeight;
160cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
161b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    /**
162b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * Enumerator for avatar position. See attr.xml for more details.
163c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein     * 0 for end, 1 for start.
164b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     */
165b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    private int mAvatarPosition;
166c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein    private static final int AVATAR_POSITION_END = 0;
167c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein    private static final int AVATAR_POSITION_START = 1;
168b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
169809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao    private Paint mWorkPaint = new Paint();
170809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao
171cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    private Tokenizer mTokenizer;
172aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private Validator mValidator;
173a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private Handler mHandler;
174a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private TextWatcher mTextWatcher;
1756e96c08f77c783d7eb7a926151f16f95255205adMichael Berberet    protected DropdownChipLayouter mDropdownChipLayouter;
176cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
17793d69f407e258e42705c4cc706d6705f45d48761Jin Cao    private View mDropdownAnchor = this;
178a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private ListPopupWindow mAlternatesPopup;
179a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private ListPopupWindow mAddressPopup;
180a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private View mAlternatePopupAnchor;
181a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private OnItemClickListener mAlternatesListener;
182cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
183a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private DrawableRecipientChip mSelectedChip;
1842bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira    private Bitmap mDefaultContactPhoto;
185e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private Bitmap mWarningIcon;
1866c2a016fb00482339dc66b93822042f9f9e2424fJin Cao    private ReplacementDrawableSpan mMoreChip;
187750e6e52d9e560d5fbf687f15bf388a947e98eb2Mindy Pereira    private TextView mMoreItem;
18812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
189b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao    private int mCurrentSuggestionCount;
190b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao
19120c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy    // VisibleForTesting
19220c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy    final ArrayList<String> mPendingChips = new ArrayList<String>();
193f97eb41a7946f2c3013ac74f2451b78070531125Mindy Pereira
19402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    private int mPendingChipsCount = 0;
195a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private int mCheckedItem;
1961163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz    private boolean mNoChipMode = false;
197a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private boolean mShouldShrink = true;
198654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy    private boolean mRequiresShrinkWhenNotGone = false;
1994de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao
20020c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy    // VisibleForTesting
201194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    ArrayList<DrawableRecipientChip> mTemporaryRecipients;
2021852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
2031a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz    private ArrayList<DrawableRecipientChip> mHiddenSpans;
2043bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira
2051d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    // Chip copy fields.
2061d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    private GestureDetector mGestureDetector;
2071d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
208aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Obtain the enclosing scroll view, if it exists, so that the view can be
209aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // scrolled to show the last line of chips content.
210c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira    private ScrollView mScrollView;
211aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    private boolean mTriedGettingScrollView;
212e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    private boolean mDragEnabled = false;
213e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
214a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    private boolean mAttachedToWindow;
2154491a0679fbf4472f9bc199197c1d4abe6a9d969Tom Taylor
21679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    private final Runnable mAddTextWatcher = new Runnable() {
21779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        @Override
21879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        public void run() {
21979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            if (mTextWatcher == null) {
22079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira                mTextWatcher = new RecipientTextWatcher();
22179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira                addTextChangedListener(mTextWatcher);
22279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            }
22379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        }
22479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    };
22579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira
2261852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira    private IndividualReplacementTask mIndividualReplacements;
2271852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
2282cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira    private Runnable mHandlePendingChips = new Runnable() {
2292cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira
2302cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        @Override
2312cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        public void run() {
2322cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira            handlePendingChips();
2332cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        }
2342cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira
2352cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira    };
2362cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira
237d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    private Runnable mDelayedShrink = new Runnable() {
238d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira
239d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira        @Override
240d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira        public void run() {
241d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira            shrink();
242d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira        }
243d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira
244d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    };
245d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira
2460f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein    private RecipientEntryItemClickedListener mRecipientEntryItemClickedListener;
2470f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein
248ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    private RecipientChipAddedListener mRecipientChipAddedListener;
249c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko    private RecipientChipDeletedListener mRecipientChipDeletedListener;
250c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko
251e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    // A set of recipient addresses that are untrusted because they are outside of the user's
252e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    // domain. We will show a warning for these addresses in the recipient chips.
253e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private Set<String> mUntrustedAddresses = new HashSet<>();
254e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
255b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam    private String mWarningTextTemplate = "";
256e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private String mWarningTitle = "";
257b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam    // Text of the warning dialog currently being displayed. Empty if no dialog currently displayed.
258b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam    private String mCurrentWarningText = "";
259e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
260e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    /**
261e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * Sets this recipient edit text view to display warning icons in chips for the given addresses.
262e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     *
263e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * @param untrustedAddresses The addresses to display warning icons for.
264e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * @param warningIcon The icon to show for each address.
265e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * @param warningIconHeight Height of the warning icon in
266b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam     * @param warningTextTemplate Text to display when warning icon is clicked.
267e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * @param warningTitle Title to display for text when warning icon is clicked.
268e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     */
269e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    public void setUntrustedAddressWarning(
270e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            Set<String> untrustedAddresses,
271e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            Bitmap warningIcon,
272e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            int warningIconHeight,
273b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam            String warningTextTemplate,
274e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            String warningTitle) {
275e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        mUntrustedAddresses = untrustedAddresses;
276e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        mWarningIcon = warningIcon;
277e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        mWarningIconHeight = warningIconHeight;
278b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        mWarningTextTemplate = warningTextTemplate;
279e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        mWarningTitle = warningTitle;
280e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    }
281e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
2820f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein    public interface RecipientEntryItemClickedListener {
2830f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein        /**
2840f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein         * Callback that occurs whenever an auto-complete suggestion is clicked.
2850f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein         * @param charactersTyped the number of characters typed by the user to provide the
2860f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein         *                        auto-complete suggestions.
2870f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein         * @param position the position in the dropdown list that the user clicked
2880f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein         */
2890f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein        void onRecipientEntryItemClicked(int charactersTyped, int position);
2900f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein    }
2910f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein
292f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    private PermissionsRequestItemClickedListener mPermissionsRequestItemClickedListener;
293f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
294f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    /**
295f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     * Listener for handling clicks on the {@link RecipientEntry} that have
296f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     * {@link RecipientEntry#ENTRY_TYPE_PERMISSION_REQUEST} type.
297f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     */
298f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    public interface PermissionsRequestItemClickedListener {
299f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
300f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        /**
301f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         * Callback that occurs when user clicks the item that asks user to grant permissions to
302f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         * the app.
303f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         *
304f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         * @param view View that asks for permission.
305f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         */
306f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        void onPermissionsRequestItemClicked(RecipientEditTextView view, String[] permissions);
307f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
308f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        /**
309f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         * Callback that occurs when user dismisses the item that asks user to grant permissions to
310f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         * the app.
311f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic         */
312f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        void onPermissionRequestDismissed();
313f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    }
314f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
315c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko    /**
316c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko     * Listener for handling deletion of chips in the recipient edit text.
317c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko     */
318c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko    public interface RecipientChipDeletedListener {
319c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko        /**
320c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko         * Callback that occurs when a chip is deleted.
321c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko         * @param entry RecipientEntry that contains information about the chip.
322c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko         */
323c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko        void onRecipientChipDeleted(RecipientEntry entry);
324c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko    }
325c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko
326ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    /**
327ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz     * Listener for handling addition of chips in the recipient edit text.
328ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz     */
329ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    public interface RecipientChipAddedListener {
330ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz        /**
331ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz         * Callback that occurs when a chip is added.
332ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz         *
333ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz         * @param entry RecipientEntry that contains information about the chip.
334ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz         */
335ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz        void onRecipientChipAdded(RecipientEntry entry);
336ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    }
337ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz
3389159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira    public RecipientEditTextView(Context context, AttributeSet attrs) {
3399159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira        super(context, attrs);
34022faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira        setChipDimensions(context, attrs);
34115259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy        mTextHeight = calculateTextHeight();
34295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        mAlternatesPopup = new ListPopupWindow(context);
343939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao        setupPopupWindow(mAlternatesPopup);
34401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        mAddressPopup = new ListPopupWindow(context);
345939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao        setupPopupWindow(mAddressPopup);
34695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        mAlternatesListener = new OnItemClickListener() {
34795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            @Override
34895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            public void onItemClick(AdapterView<?> adapterView,View view, int position,
34995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                    long rowId) {
35021cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira                mAlternatesPopup.setOnItemClickListener(null);
35195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                replaceChip(mSelectedChip, ((RecipientAlternatesAdapter) adapterView.getAdapter())
35295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                        .getRecipientEntry(position));
35395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                Message delayed = Message.obtain(mHandler, DISMISS);
35421cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira                delayed.obj = mAlternatesPopup;
35595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                mHandler.sendMessageDelayed(delayed, DISMISS_DELAY);
35695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                clearComposingText();
35795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            }
35895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        };
359e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
360b88ee450829eb4ac24fb47c377b9ec3aab0782daMindy Pereira        setOnItemClickListener(this);
361fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        setCustomSelectionActionModeCallback(this);
362007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira        mHandler = new Handler() {
363007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira            @Override
364007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira            public void handleMessage(Message msg) {
365007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira                if (msg.what == DISMISS) {
36695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                    ((ListPopupWindow) msg.obj).dismiss();
367007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira                    return;
368007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira                }
369007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira                super.handleMessage(msg);
370007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira            }
371007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira        };
3724e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira        mTextWatcher = new RecipientTextWatcher();
3734e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira        addTextChangedListener(mTextWatcher);
3741d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        mGestureDetector = new GestureDetector(context, this);
37593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        setOnEditorActionListener(this);
376b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
377a20a8d02c0b6a9440a61870ee6c7a6b6c92fbf4bJin Cao        setDropdownChipLayouter(new DropdownChipLayouter(LayoutInflater.from(context), context));
378b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
379b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
380939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao    private void setupPopupWindow(ListPopupWindow popup) {
381939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao        popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
382939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao            @Override
383939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao            public void onDismiss() {
384939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao                clearSelectedChip();
385939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao            }
386939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao        });
387939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao    }
388939f5f1fb5b2d851a5b36fc9908c2c6420b193aeJin Cao
38915259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy    private int calculateTextHeight() {
39015259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy        final TextPaint paint = getPaint();
39115259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy
392fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao        mRect.setEmpty();
39315259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy        // First measure the bounds of a sample text.
39415259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy        final String textHeightSample = "a";
395fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao        paint.getTextBounds(textHeightSample, 0, textHeightSample.length(), mRect);
39615259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy
397fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao        mRect.left = 0;
398fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao        mRect.right = 0;
39915259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy
400fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao        return mRect.height();
40115259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy    }
40215259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy
4038af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) {
404b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter = dropdownChipLayouter;
405a20a8d02c0b6a9440a61870ee6c7a6b6c92fbf4bJin Cao        mDropdownChipLayouter.setDeleteListener(this);
406f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        mDropdownChipLayouter.setPermissionRequestDismissedListener(this);
40793364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira    }
40893364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira
4090f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein    public void setRecipientEntryItemClickedListener(RecipientEntryItemClickedListener listener) {
4100f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein        mRecipientEntryItemClickedListener = listener;
4110f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein    }
4120f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein
413f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    public void setPermissionsRequestItemClickedListener(
414f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            PermissionsRequestItemClickedListener listener) {
415f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        mPermissionsRequestItemClickedListener = listener;
416f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    }
417f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
418ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    public void setRecipientChipAddedListener(RecipientChipAddedListener listener) {
419ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz        mRecipientChipAddedListener = listener;
420ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    }
421ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz
422c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko    public void setRecipientChipDeletedListener(RecipientChipDeletedListener listener) {
423c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko        mRecipientChipDeletedListener = listener;
424c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko    }
425c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko
42693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira    @Override
427fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler    protected void onDetachedFromWindow() {
4282b2de7c3146a60d39d5d2078052b2c14b6496ef9Tony Mantler        super.onDetachedFromWindow();
429fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler        mAttachedToWindow = false;
430fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler    }
431fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler
432fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler    @Override
433fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler    protected void onAttachedToWindow() {
4342b2de7c3146a60d39d5d2078052b2c14b6496ef9Tony Mantler        super.onAttachedToWindow();
435fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler        mAttachedToWindow = true;
43693d69f407e258e42705c4cc706d6705f45d48761Jin Cao
43793d69f407e258e42705c4cc706d6705f45d48761Jin Cao        final int anchorId = getDropDownAnchor();
43893d69f407e258e42705c4cc706d6705f45d48761Jin Cao        if (anchorId != View.NO_ID) {
43993d69f407e258e42705c4cc706d6705f45d48761Jin Cao            mDropdownAnchor = getRootView().findViewById(anchorId);
44093d69f407e258e42705c4cc706d6705f45d48761Jin Cao        }
441fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler    }
442fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler
443fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler    @Override
444494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin    public void setDropDownAnchor(int anchorId) {
445494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin        super.setDropDownAnchor(anchorId);
446494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin        if (anchorId != View.NO_ID) {
447494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin          mDropdownAnchor = getRootView().findViewById(anchorId);
448494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin        }
449494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin    }
450494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin
451494bec8daac55c76707e140c71fb8783fb3a9323Kevin Lin    @Override
45293364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira    public boolean onEditorAction(TextView view, int action, KeyEvent keyEvent) {
45393364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        if (action == EditorInfo.IME_ACTION_DONE) {
45493364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            if (commitDefault()) {
45593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira                return true;
45693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            }
45793364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            if (mSelectedChip != null) {
45893364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira                clearSelectedChip();
45993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira                return true;
460ec8fce439b2bfc816c98bd454c56f8fd10ab0740Tavis Bohne            } else if (hasFocus()) {
461ec8fce439b2bfc816c98bd454c56f8fd10ab0740Tavis Bohne                if (focusNext()) {
462ec8fce439b2bfc816c98bd454c56f8fd10ab0740Tavis Bohne                    return true;
463ec8fce439b2bfc816c98bd454c56f8fd10ab0740Tavis Bohne                }
46493364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            }
46593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        }
46693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        return false;
46793364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira    }
46893364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira
46993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira    @Override
470ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    public InputConnection onCreateInputConnection(@NonNull EditorInfo outAttrs) {
47193364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        InputConnection connection = super.onCreateInputConnection(outAttrs);
47293364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        int imeActions = outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION;
47393364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        if ((imeActions&EditorInfo.IME_ACTION_DONE) != 0) {
47493364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            // clear the existing action
47593364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            outAttrs.imeOptions ^= imeActions;
47693364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            // set the DONE action
47793364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
47893364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        }
47993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
48093364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira            outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
48193364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        }
4821af243fdc3b87979252bddc06097c5056ef93894Scott Kennedy
4831af243fdc3b87979252bddc06097c5056ef93894Scott Kennedy        outAttrs.actionId = EditorInfo.IME_ACTION_DONE;
484a5461b72d228191efda5ef084455f3066d5648aeKaikai Wang
485a5461b72d228191efda5ef084455f3066d5648aeKaikai Wang        // Custom action labels are discouraged in L; a checkmark icon is shown in place of the
486a5461b72d228191efda5ef084455f3066d5648aeKaikai Wang        // custom text in this case.
4878f4326a7a29664a49c92292e2466975924e77d1eNick Kralevich        outAttrs.actionLabel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? null :
488a5461b72d228191efda5ef084455f3066d5648aeKaikai Wang            getContext().getString(R.string.action_label);
48993364a61b0dcd90e19b96ea9b5900a77e2c4902eMindy Pereira        return connection;
490e33555f13a9b05d835cb860e2c30ef40af3c8502Erik    }
4917afe160db4a48f66c964ced89e29e0b63b23c7c1Mindy Pereira
492194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    /*package*/ DrawableRecipientChip getLastChip() {
493194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip last = null;
494194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip[] chips = getSortedRecipients();
495aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        if (chips != null && chips.length > 0) {
496aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            last = chips[chips.length - 1];
497e33555f13a9b05d835cb860e2c30ef40af3c8502Erik        }
498aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        return last;
499fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira    }
500fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira
5011248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai    /**
5021248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai     * @return The list of {@link RecipientEntry}s that have been selected by the user.
5031248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai     */
5041248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai    public List<RecipientEntry> getSelectedRecipients() {
5051248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai        DrawableRecipientChip[] chips =
5061248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai                getText().getSpans(0, getText().length(), DrawableRecipientChip.class);
507ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        List<RecipientEntry> results = new ArrayList<RecipientEntry>();
5081248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai        if (chips == null) {
5091248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai            return results;
5101248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai        }
5111248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai
5121248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai        for (DrawableRecipientChip c : chips) {
5131248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai            results.add(c.getEntry());
5141248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai        }
5151248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai
5161248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai        return results;
5171248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai    }
5181248e53bed1011088489b0d9adf17f5a05be8d78Ken Thai
5191a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz    /**
5201a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz     * @return The list of {@link RecipientEntry}s that have been selected by the user and also
5211a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz     *         hidden due to {@link #mMoreChip} span.
5221a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz     */
5231a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz    public List<RecipientEntry> getAllRecipients() {
5241a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz        List<RecipientEntry> results = getSelectedRecipients();
5251a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz
5261a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz        if (mHiddenSpans != null) {
5271a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz            for (DrawableRecipientChip chip : mHiddenSpans) {
5281a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz                results.add(chip.getEntry());
5291a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz            }
5301a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz        }
5311a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz
5321a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz        return results;
5331a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz    }
5341a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz
535fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira    @Override
536fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira    public void onSelectionChanged(int start, int end) {
537fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        // When selection changes, see if it is inside the chips area.
538fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        // If so, move the cursor back after the chips again.
539ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        // Only exception is when we change the selection due to a selected chip.
540194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip last = getLastChip();
541ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        if (mSelectedChip == null && last != null && start < getSpannable().getSpanEnd(last)) {
542aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            // Grab the last chip and set the cursor to after it.
543aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            setSelection(Math.min(getSpannable().getSpanEnd(last) + 1, getText().length()));
54405dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira        }
54505dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira        super.onSelectionChanged(start, end);
54605dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira    }
54705dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira
548dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira    @Override
549dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira    public void onRestoreInstanceState(Parcelable state) {
550b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        Bundle savedInstanceState = (Bundle) state;
551dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira        if (!TextUtils.isEmpty(getText())) {
552dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira            super.onRestoreInstanceState(null);
553dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira        } else {
554b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam            super.onRestoreInstanceState(
555b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                    savedInstanceState.getParcelable(STATE_TEXT_VIEW));
556b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        }
557b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        String savedWarningText = savedInstanceState.getString(
558b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam            STATE_CURRENT_WARNING_TEXT);
559b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        if (!savedWarningText.isEmpty()) {
560b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam            showWarningDialog(savedWarningText);
561dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira        }
562dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira    }
563dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira
564aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    @Override
565daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira    public Parcelable onSaveInstanceState() {
566daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira        // If the user changes orientation while they are editing, just roll back the selection.
567daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira        clearSelectedChip();
568b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        Bundle savedInstanceState = new Bundle();
569b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        savedInstanceState.putParcelable(STATE_TEXT_VIEW, super.onSaveInstanceState());
570b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        savedInstanceState.putString(STATE_CURRENT_WARNING_TEXT, mCurrentWarningText);
571b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        return savedInstanceState;
572daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira    }
573daa640c82bfa8b1f81d986d7109029d2fdec263dMindy Pereira
57402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    /**
57502a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira     * Convenience method: Append the specified text slice to the TextView's
57602a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira     * display buffer, upgrading it to BufferType.EDITABLE if it was
57702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira     * not already editable. Commas are excluded as they are added automatically
57802a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira     * by the view.
57902a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira     */
58002a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    @Override
58102a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    public void append(CharSequence text, int start, int end) {
582e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira        // We don't care about watching text changes while appending.
583e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira        if (mTextWatcher != null) {
584e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira            removeTextChangedListener(mTextWatcher);
585e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira        }
58602a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira        super.append(text, start, end);
58702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira        if (!TextUtils.isEmpty(text) && TextUtils.getTrimmedLength(text) > 0) {
5880124468e712b4098ea240b8d45f84e52826b293dmindyp            String displayString = text.toString();
58903e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy
59003e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy            if (!displayString.trim().endsWith(String.valueOf(COMMIT_CHAR_COMMA))) {
59103e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy                // We have no separator, so we should add it
59203e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy                super.append(SEPARATOR, 0, SEPARATOR.length());
59303e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy                displayString += SEPARATOR;
5940124468e712b4098ea240b8d45f84e52826b293dmindyp            }
59503e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy
59620d7af71188a4a2d94c8d9edd7eff7879d6df4c4mindyp            if (!TextUtils.isEmpty(displayString)
59702a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira                    && TextUtils.getTrimmedLength(displayString) > 0) {
59802a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira                mPendingChipsCount++;
59903e9e9833bced880a5e29bb06598d98dfa8d686eScott Kennedy                mPendingChips.add(displayString);
60002a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira            }
60102a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira        }
602abb864d610bdd171d6b0dfd2e83648952155e6ebmindyp        // Put a message on the queue to make sure we ALWAYS handle pending
603abb864d610bdd171d6b0dfd2e83648952155e6ebmindyp        // chips.
6042cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        if (mPendingChipsCount > 0) {
6052cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira            postHandlePendingChips();
6062cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        }
6074e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira        mHandler.post(mAddTextWatcher);
60802a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    }
60902a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira
61005dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira    @Override
61105dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira    public void onFocusChanged(boolean hasFocus, int direction, Rect previous) {
612d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira        super.onFocusChanged(hasFocus, direction, previous);
61305dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira        if (!hasFocus) {
61412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            shrink();
615fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        } else {
61612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            expand();
617fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        }
6189159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira    }
6199159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira
6206b7110f320c978c368c28bdb06212c6a6df12f1fAlice Yang    @Override
621ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    public <T extends ListAdapter & Filterable> void setAdapter(@NonNull T adapter) {
622093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp        super.setAdapter(adapter);
623b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        BaseRecipientAdapter baseAdapter = (BaseRecipientAdapter) adapter;
624b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        baseAdapter.registerUpdateObserver(new BaseRecipientAdapter.EntriesUpdatedObserver() {
625b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            @Override
626b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            public void onChanged(List<RecipientEntry> entries) {
627f76bb008467aa6b86af54265b2976bb23d640252Mark Wei                int suggestionCount = entries == null ? 0 : entries.size();
628f76bb008467aa6b86af54265b2976bb23d640252Mark Wei
629b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                // Scroll the chips field to the top of the screen so
630b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                // that the user can see as many results as possible.
631b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                if (entries != null && entries.size() > 0) {
632b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                    scrollBottomIntoView();
633b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao                    // Here the current suggestion count is still the old one since we update
634b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao                    // the count at the bottom of this function.
635b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao                    if (mCurrentSuggestionCount == 0) {
636b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao                        // Announce the new number of possible choices for accessibility.
637f76bb008467aa6b86af54265b2976bb23d640252Mark Wei                        announceForAccessibilityCompat(
638f76bb008467aa6b86af54265b2976bb23d640252Mark Wei                                getSuggestionDropdownOpenedVerbalization(suggestionCount));
639b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao                    }
640b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                }
64193d69f407e258e42705c4cc706d6705f45d48761Jin Cao
642626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde                // Is the dropdown closing?
643626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde                if ((entries == null || entries.size() == 0)
644626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde                        // Here the current suggestion count is still the old one since we update
645626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde                        // the count at the bottom of this function.
646c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko                        && mCurrentSuggestionCount != 0
647c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko                        // If there is no text, there's no need to know if no suggestions are
648c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko                        // available.
649c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko                        && getText().length() > 0) {
650626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde                    announceForAccessibilityCompat(getResources().getString(
651626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde                            R.string.accessbility_suggestion_dropdown_closed));
652626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde                }
653626f8cd53e91d43e98a4fe37ceedd43e1a5829b5Shri Borde
6548bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                if ((entries != null)
6558bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                        && (entries.size() == 1)
6568bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                        && (entries.get(0).getEntryType() ==
6578bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                                RecipientEntry.ENTRY_TYPE_PERMISSION_REQUEST)) {
6588bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                    // Do nothing; showing a single permissions entry. Resizing not required.
6598bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                } else {
6608bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                    // Set the dropdown height to be the remaining height from the anchor to the
6618bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                    // bottom.
662087b818dae4783d7757c0bac151d60acb7692e4fGreg Bullock                    mDropdownAnchor.getLocationOnScreen(mCoords);
6638bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                    getWindowVisibleDisplayFrame(mRect);
6648bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                    setDropDownHeight(mRect.bottom - mCoords[1] - mDropdownAnchor.getHeight() -
6658bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                            getDropDownVerticalOffset());
6668bfbed87a9bf290eb6757f352fa39ed6766932e3Greg Bullock                }
667b1aeb7b6e08b0b8dd548c4f0e5ac55844fd7f55dJin Cao
668f76bb008467aa6b86af54265b2976bb23d640252Mark Wei                mCurrentSuggestionCount = suggestionCount;
669b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            }
670b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        });
671b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        baseAdapter.setDropdownChipLayouter(mDropdownChipLayouter);
672093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp    }
673093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp
674f76bb008467aa6b86af54265b2976bb23d640252Mark Wei    /**
675f76bb008467aa6b86af54265b2976bb23d640252Mark Wei     * Return the accessibility verbalization when the suggestion dropdown is opened.
676f76bb008467aa6b86af54265b2976bb23d640252Mark Wei     */
677f76bb008467aa6b86af54265b2976bb23d640252Mark Wei    public String getSuggestionDropdownOpenedVerbalization(int suggestionCount) {
678f76bb008467aa6b86af54265b2976bb23d640252Mark Wei        return getResources().getString(R.string.accessbility_suggestion_dropdown_opened);
679f76bb008467aa6b86af54265b2976bb23d640252Mark Wei    }
680f76bb008467aa6b86af54265b2976bb23d640252Mark Wei
681ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
68204f06ea723b0445428d3f563334d653299a73a74Jin Cao    private void announceForAccessibilityCompat(String text) {
6835037dea4aa3f04ff1878ddbdbd7cf4d215c426e3Scott Kennedy        final AccessibilityManager accessibilityManager =
6845037dea4aa3f04ff1878ddbdbd7cf4d215c426e3Scott Kennedy                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
6855037dea4aa3f04ff1878ddbdbd7cf4d215c426e3Scott Kennedy        final boolean isAccessibilityOn = accessibilityManager.isEnabled();
6865037dea4aa3f04ff1878ddbdbd7cf4d215c426e3Scott Kennedy
6875037dea4aa3f04ff1878ddbdbd7cf4d215c426e3Scott Kennedy        if (isAccessibilityOn && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
68804f06ea723b0445428d3f563334d653299a73a74Jin Cao            final ViewParent parent = getParent();
68904f06ea723b0445428d3f563334d653299a73a74Jin Cao            if (parent != null) {
69004f06ea723b0445428d3f563334d653299a73a74Jin Cao                AccessibilityEvent event = AccessibilityEvent.obtain(
69104f06ea723b0445428d3f563334d653299a73a74Jin Cao                        AccessibilityEvent.TYPE_ANNOUNCEMENT);
69204f06ea723b0445428d3f563334d653299a73a74Jin Cao                onInitializeAccessibilityEvent(event);
69304f06ea723b0445428d3f563334d653299a73a74Jin Cao                event.getText().add(text);
69404f06ea723b0445428d3f563334d653299a73a74Jin Cao                event.setContentDescription(null);
69504f06ea723b0445428d3f563334d653299a73a74Jin Cao                parent.requestSendAccessibilityEvent(this, event);
69604f06ea723b0445428d3f563334d653299a73a74Jin Cao            }
69704f06ea723b0445428d3f563334d653299a73a74Jin Cao        }
69804f06ea723b0445428d3f563334d653299a73a74Jin Cao    }
69904f06ea723b0445428d3f563334d653299a73a74Jin Cao
70053e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin    protected void scrollBottomIntoView() {
701093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp        if (mScrollView != null && mShouldShrink) {
702fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao            getLocationInWindow(mCoords);
703093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp            // Desired position shows at least 1 line of chips below the action
704093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp            // bar. We add excess padding to make sure this is always below other
705093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp            // content.
70614efc5795c38120de239fad3d44b1b8b81045043Jin Cao            final int height = getHeight();
707fcaf9868ae2b549f1b95465e4e7877482403398dJin Cao            final int currentPos = mCoords[1] + height;
708f6ddb7541feb5e415f9750fb403814dbbc9068beJin Cao            mScrollView.getLocationInWindow(mCoords);
709f6ddb7541feb5e415f9750fb403814dbbc9068beJin Cao            final int desiredPos = mCoords[1] + height / getLineCount();
710093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp            if (currentPos > desiredPos) {
711093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp                mScrollView.scrollBy(0, currentPos - desiredPos);
712093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp            }
713093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp        }
714093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp    }
715093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp
71653e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin    protected ScrollView getScrollView() {
7177a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        return mScrollView;
71853e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin    }
71953e718a52258e2240ed82faaee0ecef05fe112a0Kevin Lin
720d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira    @Override
721d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira    public void performValidation() {
722d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira        // Do nothing. Chips handles its own validation.
723d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira    }
724d5d86aafdbb1487ade3ecf70b92c00d20b94f9c8Mindy Pereira
72512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    private void shrink() {
7265c125afa54288595524c85182031421cbad08ac3Mindy Pereira        if (mTokenizer == null) {
7275c125afa54288595524c85182031421cbad08ac3Mindy Pereira            return;
7285c125afa54288595524c85182031421cbad08ac3Mindy Pereira        }
7295e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        long contactId = mSelectedChip != null ? mSelectedChip.getEntry().getContactId() : -1;
7305e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        if (mSelectedChip != null && contactId != RecipientEntry.INVALID_CONTACT
7315e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                && (!isPhoneQuery() && contactId != RecipientEntry.GENERATED_CONTACT)) {
73212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            clearSelectedChip();
73312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        } else {
734d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira            if (getWidth() <= 0) {
735d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira                mHandler.removeCallbacks(mDelayedShrink);
736654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy
737654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                if (getVisibility() == GONE) {
738654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    // We aren't going to have a width any time soon, so defer
739654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    // this until we're not GONE.
740654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    mRequiresShrinkWhenNotGone = true;
741654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                } else {
742654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    // We don't have the width yet which means the view hasn't been drawn yet
743654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    // and there is no reason to attempt to commit chips yet.
744654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    // This focus lost must be the result of an orientation change
745654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    // or an initial rendering.
746654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    // Re-post the shrink for later.
747654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                    mHandler.post(mDelayedShrink);
748654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy                }
749d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira                return;
750d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira            }
751e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira            // Reset any pending chips as they would have been handled
752e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira            // when the field lost focus.
753e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira            if (mPendingChipsCount > 0) {
7542cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                postHandlePendingChips();
755e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira            } else {
756e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                Editable editable = getText();
757e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                int end = getSelectionEnd();
758e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                int start = mTokenizer.findTokenStart(editable, end);
759194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                DrawableRecipientChip[] chips =
760194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                        getSpannable().getSpans(start, end, DrawableRecipientChip.class);
761e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                if ((chips == null || chips.length == 0)) {
762d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira                    Editable text = getText();
763d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira                    int whatEnd = mTokenizer.findTokenEnd(text, start);
764d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira                    // This token was already tokenized, so skip past the ending token.
765448e90b97a4df8102d6e1d2039274d9ea188dff9Mindy Pereira                    if (whatEnd < text.length() && text.charAt(whatEnd) == ',') {
7664f82d888c680a61b95373740ce68bfb48a242617mindyp                        whatEnd = movePastTerminators(whatEnd);
767d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira                    }
768e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                    // In the middle of chip; treat this as an edit
769e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                    // and commit the whole token.
770d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira                    int selEnd = getSelectionEnd();
7716337231a660b717cd6f5c40d524f4aabfcc865b0Mindy Pereira                    if (whatEnd != selEnd) {
772e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                        handleEdit(start, whatEnd);
773e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                    } else {
774e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                        commitChip(start, end, editable);
775e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                    }
776e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira                }
777a74f40dc1073117349d1d39b7c8396a39d24f57fMindy Pereira            }
778e81f8a3d2c9bc916a72b3bf42f56c3ef8fb547f3Mindy Pereira            mHandler.post(mAddTextWatcher);
77912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        }
7803bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        createMoreChip();
78112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
78212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
78312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    private void expand() {
784374ccb2935ea793d2fbee54ec5fe6d71af934d27mindyp        if (mShouldShrink) {
785374ccb2935ea793d2fbee54ec5fe6d71af934d27mindyp            setMaxLines(Integer.MAX_VALUE);
786374ccb2935ea793d2fbee54ec5fe6d71af934d27mindyp        }
78712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        removeMoreChip();
78812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        setCursorVisible(true);
78912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        Editable text = getText();
79012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        setSelection(text != null && text.length() > 0 ? text.length() : 0);
7911852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        // If there are any temporary chips, try replacing them now that the user
7921852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        // has expanded the field.
7931852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0) {
7941852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            new RecipientReplacementTask().execute();
7951852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            mTemporaryRecipients = null;
7961852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        }
79712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
79812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
7992bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira    private CharSequence ellipsizeText(CharSequence text, TextPaint paint, float maxWidth) {
8006e8e8e8165a797611f80a2c17249147333d55ea7Mindy Pereira        paint.setTextSize(mChipFontSize);
80102a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira        if (maxWidth <= 0 && Log.isLoggable(TAG, Log.DEBUG)) {
80202a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira            Log.d(TAG, "Max width is negative: " + maxWidth);
80302a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira        }
80402a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira        return TextUtils.ellipsize(text, paint, maxWidth,
80502a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira                TextUtils.TruncateAt.END);
8062bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira    }
807cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
808b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    /**
809b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * Creates a bitmap of the given contact on a selected chip.
810b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     *
811b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * @param contact The recipient entry to pull data from.
812b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * @param paint The paint to use to draw the bitmap.
813b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     */
814e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private ChipBitmapContainer createChipBitmap(RecipientEntry contact, TextPaint paint) {
815cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang        paint.setColor(getDefaultChipTextColor(contact));
81633515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        ChipBitmapContainer bitmapContainer = createChipBitmap(contact, paint,
81733515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao                getChipBackground(contact), getDefaultChipBackgroundColor(contact));
81835e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao
81935e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        if (bitmapContainer.loadIcon) {
820809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao            loadAvatarIcon(contact, bitmapContainer);
82135e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        }
822e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        return bitmapContainer;
823b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
824b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
82535e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    private ChipBitmapContainer createChipBitmap(RecipientEntry contact, TextPaint paint,
82633515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao            Drawable overrideBackgroundDrawable, int backgroundColor) {
82735e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        final ChipBitmapContainer result = new ChipBitmapContainer();
82835e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao
82984f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker        Drawable indicatorIcon = null;
83084f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker        int indicatorPadding = 0;
831f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        if (contact.getIndicatorIconId() != 0) {
832f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            indicatorIcon = getContext().getDrawable(contact.getIndicatorIconId());
83384f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker            indicatorIcon.setBounds(0, 0,
83484f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker                    indicatorIcon.getIntrinsicWidth(), indicatorIcon.getIntrinsicHeight());
83584f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker            indicatorPadding = indicatorIcon.getBounds().width() + mChipTextEndPadding;
83684f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker        }
83784f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker
838b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        Rect backgroundPadding = new Rect();
83933515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        if (overrideBackgroundDrawable != null) {
84033515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao            overrideBackgroundDrawable.getPadding(backgroundPadding);
84133515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        }
842b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
843cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // Ellipsize the text so that it takes AT MOST the entire width of the
844cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // autocomplete text entry area. Make sure to leave space for padding
845cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // on the sides.
8466e8e8e8165a797611f80a2c17249147333d55ea7Mindy Pereira        int height = (int) mChipHeight;
847b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        // Since the icon is a square, it's width is equal to the maximum height it can be inside
848a233af88e381e4a8caf917176974aeb0aa259c19Sandeep Siddhartha        // the chip. Don't include iconWidth for invalid contacts and when not displaying photos.
849a233af88e381e4a8caf917176974aeb0aa259c19Sandeep Siddhartha        boolean displayIcon = contact.isValid() && contact.shouldDisplayIcon();
850a233af88e381e4a8caf917176974aeb0aa259c19Sandeep Siddhartha        int iconWidth = displayIcon ?
85133515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao                height - backgroundPadding.top - backgroundPadding.bottom : 0;
852e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
853e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final boolean shouldDisplayWarningIcon = mUntrustedAddresses.contains(
854e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                contact.getDestination());
855e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final float warningIconWidth = shouldDisplayWarningIcon ? mWarningIconHeight : 0;
856e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final float warningIconTopMargin = (mChipHeight - mWarningIconHeight) / 2f;
857e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final float warningIconEndMargin = shouldDisplayWarningIcon ? mChipTextEndPadding : 0;
858e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
8596ed7ded9deabbc92ed8341cf922673fd4626ba7eMindy Pereira        float[] widths = new float[1];
8606ed7ded9deabbc92ed8341cf922673fd4626ba7eMindy Pereira        paint.getTextWidths(" ", widths);
861aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        CharSequence ellipsizedText = ellipsizeText(createChipDisplayText(contact), paint,
862e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                calculateAvailableWidth()
863e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - iconWidth
864e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - warningIconWidth
865e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - warningIconEndMargin
866e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - widths[0]
867e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - backgroundPadding.left
868e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - backgroundPadding.right
869e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - indicatorPadding);
870b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        int textWidth = (int) paint.measureText(ellipsizedText, 0, ellipsizedText.length());
871b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
87233515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        // Chip start padding is the same as the end padding if there is no contact image.
873a233af88e381e4a8caf917176974aeb0aa259c19Sandeep Siddhartha        final int startPadding = displayIcon ? mChipTextStartPadding : mChipTextEndPadding;
8749d2a1980bbcad5dae3b0fb03c35208724b377fa8Mindy Pereira        // Make sure there is a minimum chip width so the user can ALWAYS
8759d2a1980bbcad5dae3b0fb03c35208724b377fa8Mindy Pereira        // tap a chip without difficulty.
876e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        int width = Math.max(iconWidth * 2,
877e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                textWidth
878e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + startPadding
879e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + mChipTextEndPadding
880e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + iconWidth
881e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + (int) warningIconWidth
882e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + (int) warningIconEndMargin
883e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + backgroundPadding.left
884e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + backgroundPadding.right
885e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + indicatorPadding);
886cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
887cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // Create the background of the chip.
88835e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        result.bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
88935e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        final Canvas canvas = new Canvas(result.bitmap);
8906b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira
89133515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        // Check if the background drawable is set via attr
89233515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        if (overrideBackgroundDrawable != null) {
89333515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao            overrideBackgroundDrawable.setBounds(0, 0, width, height);
89433515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao            overrideBackgroundDrawable.draw(canvas);
89533515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        } else {
89633515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao            // Draw the default chip background
8976c2a016fb00482339dc66b93822042f9f9e2424fJin Cao            mWorkPaint.reset();
8986c2a016fb00482339dc66b93822042f9f9e2424fJin Cao            mWorkPaint.setColor(backgroundColor);
89933515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao            final float radius = height / 2;
90033515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao            canvas.drawRoundRect(new RectF(0, 0, width, height), radius, radius,
9016c2a016fb00482339dc66b93822042f9f9e2424fJin Cao                    mWorkPaint);
90233515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        }
90333515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao
904b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        // Draw the text vertically aligned
905c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein        int textX = shouldPositionAvatarOnRight() ?
906e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                mChipTextEndPadding
907e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + backgroundPadding.left
908e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + indicatorPadding
909e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + (int) warningIconWidth
910e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    + (int) warningIconEndMargin :
911e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                width
912e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - backgroundPadding.right
913e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - mChipTextEndPadding
914e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - textWidth
915e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - indicatorPadding
916e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - (int) warningIconWidth
917e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    - (int) warningIconEndMargin;
918b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        canvas.drawText(ellipsizedText, 0, ellipsizedText.length(),
91915259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy                textX, getTextYOffset(height), paint);
92035e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao
92184f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker        if (indicatorIcon != null) {
92284f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker            int indicatorX = shouldPositionAvatarOnRight()
92384f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker                ? backgroundPadding.left + mChipTextEndPadding
92484f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker                : width - backgroundPadding.right - indicatorIcon.getBounds().width()
92584f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker                        - mChipTextEndPadding;
92684f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker            int indicatorY = height / 2 - indicatorIcon.getBounds().height() / 2;
92784f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker            indicatorIcon.getBounds().offsetTo(indicatorX, indicatorY);
92884f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker            indicatorIcon.draw(canvas);
92984f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker        }
93084f848c6601f1759689c5318e8d01924ce2f0b2bBraden Walker
93135e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        // Set the variables that are needed to draw the icon bitmap once it's loaded
932e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final int iconX = shouldPositionAvatarOnRight() ?
933e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                width - backgroundPadding.right - iconWidth :
93435e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao                backgroundPadding.left;
93535e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        result.left = iconX;
93635e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        result.top = backgroundPadding.top;
93735e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        result.right = iconX + iconWidth;
93835e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        result.bottom = height - backgroundPadding.bottom;
939a233af88e381e4a8caf917176974aeb0aa259c19Sandeep Siddhartha        result.loadIcon = displayIcon;
94035e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao
941e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        // Set the variables needed to draw the warning icon bitmap once it's loaded.
942e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final float warningIconX = shouldPositionAvatarOnRight() ?
943e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                backgroundPadding.left + warningIconEndMargin :
944e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                width - backgroundPadding.right - warningIconWidth - warningIconEndMargin;
945e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final float warningIconY = warningIconTopMargin;
946e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        result.warningIconLeft = warningIconX;
947e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        result.warningIconTop = warningIconY;
948e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        result.warningIconRight = warningIconX + warningIconWidth;
949e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        result.warningIconBottom = warningIconY + mWarningIconHeight;
950e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
95135e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        return result;
95235e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    }
95335e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao
95435e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    /**
95535e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao     * Helper function that draws the loaded icon bitmap into the chips bitmap
95635e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao     */
957809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao    private void drawIcon(ChipBitmapContainer bitMapResult, Bitmap icon) {
958e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        if (icon == null) {
959e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            return;
960e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        }
96135e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        final Canvas canvas = new Canvas(bitMapResult.bitmap);
96235e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        final RectF src = new RectF(0, 0, icon.getWidth(), icon.getHeight());
96335e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        final RectF dst = new RectF(bitMapResult.left, bitMapResult.top, bitMapResult.right,
96435e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao                bitMapResult.bottom);
965e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        drawCircularIconOnCanvas(icon, canvas, src, dst);
966e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    }
967e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
968e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    /**
969e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * Draws the warning icon onto the chip's bitmap and returns the rectangle it drew on.
970e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     */
971e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private RectF drawWarningIcon(ChipBitmapContainer bitMapResult) {
972e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        if (mWarningIcon == null) {
973e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            return new RectF(0, 0, 0, 0);
974e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        }
975e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final Canvas canvas = new Canvas(bitMapResult.bitmap);
976e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final RectF src = new RectF(0, 0, mWarningIcon.getWidth(), mWarningIcon.getHeight());
977e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final RectF dst = new RectF(bitMapResult.warningIconLeft, bitMapResult.warningIconTop,
978e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                bitMapResult.warningIconRight, bitMapResult.warningIconBottom);
979e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        drawRectanglularIconOnCanvas(mWarningIcon, canvas, src, dst);
980e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        return dst;
9812bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira    }
982cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
983aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    /**
984c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein     * Returns true if the avatar should be positioned at the right edge of the chip.
985c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein     * Takes into account both the set avatar position (start or end) as well as whether
986c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein     * the layout direction is LTR or RTL.
987c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein     */
988c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein    private boolean shouldPositionAvatarOnRight() {
989ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        final boolean isRtl = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 &&
990ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                getLayoutDirection() == LAYOUT_DIRECTION_RTL;
991c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein        final boolean assignedPosition = mAvatarPosition == AVATAR_POSITION_END;
992c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein        // If in Rtl mode, the position should be flipped.
993c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein        return isRtl ? !assignedPosition : assignedPosition;
994c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein    }
995c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein
996c0e6023ca7129914a3850bceb1a151acc75fd1f8Andrew Sapperstein    /**
997b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * Returns the avatar icon to use for this recipient entry. Returns null if we don't want to
998b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * draw an icon for this recipient.
999b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     */
100035e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    private void loadAvatarIcon(final RecipientEntry contact,
1001809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao            final ChipBitmapContainer bitmapContainer) {
1002b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        // Don't draw photos for recipients that have been typed in OR generated on the fly.
1003b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        long contactId = contact.getContactId();
1004b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        boolean drawPhotos = isPhoneQuery() ?
1005b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                contactId != RecipientEntry.INVALID_CONTACT
1006b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                : (contactId != RecipientEntry.INVALID_CONTACT
1007bf84bd8f061da4f5a494d20192eae53db9867785Jin Cao                        && contactId != RecipientEntry.GENERATED_CONTACT);
1008b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
1009b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        if (drawPhotos) {
101035e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao            final byte[] origPhotoBytes = contact.getPhotoBytes();
1011b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            // There may not be a photo yet if anything but the first contact address
1012b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            // was selected.
101382d388545ba0b52854801c946ed2f62e05e7b563Jin Cao            if (origPhotoBytes == null) {
1014b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                // TODO: cache this in the recipient entry?
101535e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao                getAdapter().fetchPhoto(contact, new PhotoManager.PhotoManagerCallback() {
101652e4814f46230835ed48d7561de8359b79b978cdJin Cao                    @Override
101750429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein                    public void onPhotoBytesPopulated() {
101850429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein                        // Call through to the async version which will ensure
101950429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein                        // proper threading.
102050429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein                        onPhotoBytesAsynchronouslyPopulated();
102150429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein                    }
102250429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein
102350429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein                    @Override
102452e4814f46230835ed48d7561de8359b79b978cdJin Cao                    public void onPhotoBytesAsynchronouslyPopulated() {
102552e4814f46230835ed48d7561de8359b79b978cdJin Cao                        final byte[] loadedPhotoBytes = contact.getPhotoBytes();
102652e4814f46230835ed48d7561de8359b79b978cdJin Cao                        final Bitmap icon = BitmapFactory.decodeByteArray(loadedPhotoBytes, 0,
102752e4814f46230835ed48d7561de8359b79b978cdJin Cao                                loadedPhotoBytes.length);
102852e4814f46230835ed48d7561de8359b79b978cdJin Cao                        tryDrawAndInvalidate(icon);
102952e4814f46230835ed48d7561de8359b79b978cdJin Cao                    }
10300efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao
103152e4814f46230835ed48d7561de8359b79b978cdJin Cao                    @Override
103252e4814f46230835ed48d7561de8359b79b978cdJin Cao                    public void onPhotoBytesAsyncLoadFailed() {
103352e4814f46230835ed48d7561de8359b79b978cdJin Cao                        // TODO: can the scaled down default photo be cached?
103452e4814f46230835ed48d7561de8359b79b978cdJin Cao                        tryDrawAndInvalidate(mDefaultContactPhoto);
103552e4814f46230835ed48d7561de8359b79b978cdJin Cao                    }
103652e4814f46230835ed48d7561de8359b79b978cdJin Cao
103752e4814f46230835ed48d7561de8359b79b978cdJin Cao                    private void tryDrawAndInvalidate(Bitmap icon) {
1038809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao                        drawIcon(bitmapContainer, icon);
103952e4814f46230835ed48d7561de8359b79b978cdJin Cao                        // The caller might originated from a background task. However, if the
104052e4814f46230835ed48d7561de8359b79b978cdJin Cao                        // background task has already completed, the view might be already drawn
104152e4814f46230835ed48d7561de8359b79b978cdJin Cao                        // on the UI but the callback would happen on the background thread.
104252e4814f46230835ed48d7561de8359b79b978cdJin Cao                        // So if we are on a background thread, post an invalidate call to the UI.
104352e4814f46230835ed48d7561de8359b79b978cdJin Cao                        if (Looper.myLooper() == Looper.getMainLooper()) {
104452e4814f46230835ed48d7561de8359b79b978cdJin Cao                            // The view might not redraw itself since it's loaded asynchronously
10450efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao                            invalidate();
104652e4814f46230835ed48d7561de8359b79b978cdJin Cao                        } else {
104752e4814f46230835ed48d7561de8359b79b978cdJin Cao                            post(new Runnable() {
104852e4814f46230835ed48d7561de8359b79b978cdJin Cao                                @Override
104952e4814f46230835ed48d7561de8359b79b978cdJin Cao                                public void run() {
105052e4814f46230835ed48d7561de8359b79b978cdJin Cao                                    invalidate();
105152e4814f46230835ed48d7561de8359b79b978cdJin Cao                                }
105252e4814f46230835ed48d7561de8359b79b978cdJin Cao                            });
10530efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao                        }
105452e4814f46230835ed48d7561de8359b79b978cdJin Cao                    }
105535e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao                });
1056b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            } else {
105735e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao                final Bitmap icon = BitmapFactory.decodeByteArray(origPhotoBytes, 0,
105835e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao                        origPhotoBytes.length);
1059809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao                drawIcon(bitmapContainer, icon);
1060b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            }
1061b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        }
1062b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
1063b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
1064b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    /**
1065aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira     * Get the background drawable for a RecipientChip.
1066aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira     */
1067aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Visible for testing.
1068f30a42800318f6790d55421f8f6980eb38db4d3cmindyp    /* package */Drawable getChipBackground(RecipientEntry contact) {
1069f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        return contact.isValid() ? mChipBackground : mInvalidChipBackground;
1070aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    }
1071aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira
1072cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang    private int getDefaultChipTextColor(RecipientEntry contact) {
1073cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang        return contact.isValid() ? mUnselectedChipTextColor :
1074cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang                getResources().getColor(android.R.color.black);
1075cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang    }
1076cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang
107733515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao    private int getDefaultChipBackgroundColor(RecipientEntry contact) {
1078cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang        return contact.isValid() ? mUnselectedChipBackgroundColor :
1079cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang                getResources().getColor(R.color.chip_background_invalid);
108033515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao    }
108133515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao
1082b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    /**
1083b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * Given a height, returns a Y offset that will draw the text in the middle of the height.
1084b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     */
108515259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy    protected float getTextYOffset(int height) {
108615259d5bab45b9e561d33e5d1d23df3a1476a430Scott Kennedy        return height - ((height - mTextHeight) / 2);
10871d37d9775446acd8e4e6bb0637565e88b5360096Mindy Pereira    }
10881d37d9775446acd8e4e6bb0637565e88b5360096Mindy Pereira
1089b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    /**
1090b8985b7b595e38518d2a0657b89ff47bd34862abScott Kennedy     * Draws the icon onto the canvas given the source rectangle of the bitmap and the destination
1091b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     * rectangle of the canvas.
1092e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     *
1093e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * <p>The icon is drawn as a circle.
1094b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin     */
1095e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    protected void drawCircularIconOnCanvas(Bitmap icon, Canvas canvas, RectF src, RectF dst) {
1096e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        setWorkPaintForIcon(icon, src, dst);
1097e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2f, mWorkPaint);
1098e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
1099e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final float borderWidth = 1f;
1100e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        setWorkPaintForBorder(borderWidth);
1101e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2f - borderWidth / 2,
1102e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                mWorkPaint);
1103e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
1104e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        mWorkPaint.reset();
1105e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    }
1106e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
1107e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    /**
1108e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * Draws the icon onto the canvas given the source rectangle of the bitmap and the destination
1109e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * rectangle of the canvas.
1110e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     *
1111e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * <p>The icon is drawn as a rectangle.
1112e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     */
1113e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private void drawRectanglularIconOnCanvas(Bitmap icon, Canvas canvas, RectF src, RectF dst) {
1114e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        setWorkPaintForIcon(icon, src, dst);
1115e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        canvas.drawRect(dst, mWorkPaint);
1116e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
1117e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final float borderWidth = 1f;
1118e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        setWorkPaintForBorder(borderWidth);
1119e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        canvas.drawRect(dst, mWorkPaint);
1120e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
1121e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        mWorkPaint.reset();
1122e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    }
1123e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam
1124e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    /**
1125e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * Sets WorkPaint for drawing the icon from src onto dst.
1126e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     */
1127e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private void setWorkPaintForIcon(Bitmap icon, RectF src, RectF dst) {
1128b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy        final Matrix matrix = new Matrix();
1129b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy
1130b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy        // Draw bitmap through shader first.
1131b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy        final BitmapShader shader = new BitmapShader(icon, TileMode.CLAMP, TileMode.CLAMP);
1132b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy        matrix.reset();
1133b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy
1134b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy        // Fit bitmap to bounds.
1135b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.FILL);
1136b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy
1137b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy        shader.setLocalMatrix(matrix);
1138809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.reset();
1139809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setShader(shader);
1140809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setAntiAlias(true);
1141809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setFilterBitmap(true);
1142809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setDither(true);
1143e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    }
1144b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy
1145e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    /**
1146e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     * Sets WorkPaint for drawing the icon border with the given width.
1147e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam     */
1148e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam    private void setWorkPaintForBorder(float borderWidth) {
1149809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.reset();
1150809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setColor(Color.TRANSPARENT);
1151809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setStyle(Style.STROKE);
1152809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setStrokeWidth(borderWidth);
1153809c595762c1d5adfe93d61eafa5fc9aa48c7de3Jin Cao        mWorkPaint.setAntiAlias(true);
1154b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
1155b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
1156ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    private DrawableRecipientChip constructChipSpan(RecipientEntry contact) {
11572bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira        TextPaint paint = getPaint();
11582bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira        float defaultSize = paint.getTextSize();
1159e50b0a1f168322390b63f435f222766cdae6ba7dMindy Pereira        int defaultColor = paint.getColor();
11602bfe48e4dda1e172f4c857a0ee1a99d924fb226fMindy Pereira
1161e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        ChipBitmapContainer bitmapContainer = createChipBitmap(contact, paint);
1162e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final Rect warningIconBounds = new Rect(0, 0, 0, 0);
1163e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        if (mUntrustedAddresses.contains(contact.getDestination())) {
1164e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            drawWarningIcon(bitmapContainer).round(warningIconBounds);
1165e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        }
1166e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final Bitmap tmpBitmap = bitmapContainer.bitmap;
1167cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
1168cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // Pass the full text, un-ellipsized, to the chip.
1169e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final int iconWidth = tmpBitmap != null ? tmpBitmap.getWidth() : 0;
1170e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        final int iconHeight = tmpBitmap != null ? tmpBitmap.getHeight() : 0;
1171cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        Drawable result = new BitmapDrawable(getResources(), tmpBitmap);
1172e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        result.setBounds(0, 0, iconWidth, iconHeight);
11736c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        VisibleRecipientChip recipientChip =
11746c2a016fb00482339dc66b93822042f9f9e2424fJin Cao                new VisibleRecipientChip(result, contact);
11756c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        recipientChip.setExtraMargin(mLineSpacingExtra);
1176cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // Return text to the original size.
1177cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        paint.setTextSize(defaultSize);
1178e50b0a1f168322390b63f435f222766cdae6ba7dMindy Pereira        paint.setColor(defaultColor);
1179e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        // Put warning icon dimensions info in the chip
1180e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        recipientChip.setWarningIconBounds(warningIconBounds);
1181cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        return recipientChip;
11829159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira    }
11839159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira
1184a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
11856fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao     * Calculate the offset from bottom of the EditText to top of the provided line.
11866fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao     */
11876fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao    private int calculateOffsetFromBottomToTop(int line) {
11886fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao        return -(int) ((mChipHeight + (2 * mLineSpacingExtra)) * (Math
11896fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao                .abs(getLineCount() - line)) + getPaddingBottom());
11906fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao    }
11916fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao
11926fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao    /**
1193a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * Get the max amount of space a chip can take up. The formula takes into
1194a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * account the width of the EditTextView, any view padding, and padding
1195a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * that will be added to the chip.
1196a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
1197f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy    private float calculateAvailableWidth() {
119858d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        return getWidth() - getPaddingLeft() - getPaddingRight() - mChipTextStartPadding
119958d541de27090b4bd55746368b3b3b1486a7c094Jin Cao                - mChipTextEndPadding;
12009159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira    }
12019159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira
120252c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira
120322faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira    private void setChipDimensions(Context context, AttributeSet attrs) {
12045da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecipientEditTextView, 0,
12055da0234c9a7108d3386039816c7469753b79c307Mindy Pereira                0);
120652c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira        Resources r = getContext().getResources();
12078c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
120822faaa6f1eacb6ae69e4fd5d97aeeefc77973cd1Mindy Pereira        mChipBackground = a.getDrawable(R.styleable.RecipientEditTextView_chipBackground);
120933515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao        mInvalidChipBackground = a
121033515e70c6dc7b488bc0aa62f6a009bec4bf8812Jin Cao                .getDrawable(R.styleable.RecipientEditTextView_invalidChipBackground);
12115da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        mChipDelete = a.getDrawable(R.styleable.RecipientEditTextView_chipDelete);
12125da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        if (mChipDelete == null) {
1213a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao            mChipDelete = r.getDrawable(R.drawable.ic_cancel_wht_24dp);
12145da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        }
121558d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        mChipTextStartPadding = mChipTextEndPadding
121658d541de27090b4bd55746368b3b3b1486a7c094Jin Cao                = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipPadding, -1);
121758d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        if (mChipTextStartPadding == -1) {
121858d541de27090b4bd55746368b3b3b1486a7c094Jin Cao            mChipTextStartPadding = mChipTextEndPadding =
121958d541de27090b4bd55746368b3b3b1486a7c094Jin Cao                    (int) r.getDimension(R.dimen.chip_padding);
122058d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        }
122158d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        // xml-overrides for each individual padding
122258d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        // TODO: add these to attr?
122358d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        int overridePadding = (int) r.getDimension(R.dimen.chip_padding_start);
122458d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        if (overridePadding >= 0) {
122558d541de27090b4bd55746368b3b3b1486a7c094Jin Cao            mChipTextStartPadding = overridePadding;
122658d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        }
122758d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        overridePadding = (int) r.getDimension(R.dimen.chip_padding_end);
122858d541de27090b4bd55746368b3b3b1486a7c094Jin Cao        if (overridePadding >= 0) {
122958d541de27090b4bd55746368b3b3b1486a7c094Jin Cao            mChipTextEndPadding = overridePadding;
12305da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        }
12315da0234c9a7108d3386039816c7469753b79c307Mindy Pereira
12325da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        mDefaultContactPhoto = BitmapFactory.decodeResource(r, R.drawable.ic_contact_picture);
12335da0234c9a7108d3386039816c7469753b79c307Mindy Pereira
123452c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira        mMoreItem = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.more_item, null);
12355da0234c9a7108d3386039816c7469753b79c307Mindy Pereira
12365da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        mChipHeight = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipHeight, -1);
12375da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        if (mChipHeight == -1) {
12385da0234c9a7108d3386039816c7469753b79c307Mindy Pereira            mChipHeight = r.getDimension(R.dimen.chip_height);
12395da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        }
12405da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        mChipFontSize = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipFontSize, -1);
12415da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        if (mChipFontSize == -1) {
12425da0234c9a7108d3386039816c7469753b79c307Mindy Pereira            mChipFontSize = r.getDimension(R.dimen.chip_text_size);
12435da0234c9a7108d3386039816c7469753b79c307Mindy Pereira        }
1244b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy        mAvatarPosition =
1245b232e5c33e1e2879c7bc8e96a1b9c41c4fa41244Scott Kennedy                a.getInt(R.styleable.RecipientEditTextView_avatarPosition, AVATAR_POSITION_START);
1246b8985b7b595e38518d2a0657b89ff47bd34862abScott Kennedy        mDisableDelete = a.getBoolean(R.styleable.RecipientEditTextView_disableDelete, false);
1247b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
1248f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        mMaxLines = r.getInteger(R.integer.chips_max_lines);
12496c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        mLineSpacingExtra = r.getDimensionPixelOffset(R.dimen.line_spacing_extra);
1250b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
1251cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang        mUnselectedChipTextColor = a.getColor(
1252cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang                R.styleable.RecipientEditTextView_unselectedChipTextColor,
1253cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang                r.getColor(android.R.color.black));
1254cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang
1255cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang        mUnselectedChipBackgroundColor = a.getColor(
1256cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang                R.styleable.RecipientEditTextView_unselectedChipBackgroundColor,
1257cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang                r.getColor(R.color.chip_background));
1258cb11fced23f3a08ad68ea61719d7f0fdeb0f7279Kaikai Wang
1259ef79800ff65490ec91ce77f55c4ade142d1cc4b2Mindy Pereira        a.recycle();
126052c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira    }
126152c441e2c03e0f48572348953b985a4bf989c057Mindy Pereira
1262d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    // Visible for testing.
1263d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    /* package */ void setMoreItem(TextView moreItem) {
1264d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira        mMoreItem = moreItem;
1265d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    }
1266d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira
126797cb25912dab282cf732757f68b0405ed005f00bMindy Pereira
126897cb25912dab282cf732757f68b0405ed005f00bMindy Pereira    // Visible for testing.
126997cb25912dab282cf732757f68b0405ed005f00bMindy Pereira    /* package */ void setChipBackground(Drawable chipBackground) {
127097cb25912dab282cf732757f68b0405ed005f00bMindy Pereira        mChipBackground = chipBackground;
127197cb25912dab282cf732757f68b0405ed005f00bMindy Pereira    }
127297cb25912dab282cf732757f68b0405ed005f00bMindy Pereira
127397cb25912dab282cf732757f68b0405ed005f00bMindy Pereira    // Visible for testing.
127497cb25912dab282cf732757f68b0405ed005f00bMindy Pereira    /* package */ void setChipHeight(int height) {
127597cb25912dab282cf732757f68b0405ed005f00bMindy Pereira        mChipHeight = height;
127697cb25912dab282cf732757f68b0405ed005f00bMindy Pereira    }
127797cb25912dab282cf732757f68b0405ed005f00bMindy Pereira
1278b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public float getChipHeight() {
1279b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mChipHeight;
1280b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
1281b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
12821163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz    /** Returns whether view is in no-chip or chip mode. */
12831163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz    public boolean isNoChipMode() {
12841163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz        return mNoChipMode;
12851163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz    }
12861163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz
1287076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira    /**
1288076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira     * Set whether to shrink the recipients field such that at most
1289076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira     * one line of recipients chips are shown when the field loses
1290076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira     * focus. By default, the number of displayed recipients will be
1291076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira     * limited and a "more" chip will be shown when focus is lost.
1292076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira     * @param shrink
1293076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira     */
1294076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira    public void setOnFocusListShrinkRecipients(boolean shrink) {
1295076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira        mShouldShrink = shrink;
1296076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira    }
1297076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira
1298cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    @Override
129902a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    public void onSizeChanged(int width, int height, int oldw, int oldh) {
130002a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira        super.onSizeChanged(width, height, oldw, oldh);
1301db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira        if (width != 0 && height != 0) {
1302db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira            if (mPendingChipsCount > 0) {
1303db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira                postHandlePendingChips();
1304db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira            } else {
1305db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira                checkChipWidths();
1306db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira            }
13078005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira        }
13081852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        // Try to find the scroll view parent, if it exists.
1309aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        if (mScrollView == null && !mTriedGettingScrollView) {
1310c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira            ViewParent parent = getParent();
1311c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira            while (parent != null && !(parent instanceof ScrollView)) {
1312c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira                parent = parent.getParent();
1313c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira            }
1314c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira            if (parent != null) {
1315c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira                mScrollView = (ScrollView) parent;
1316c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira            }
1317aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            mTriedGettingScrollView = true;
1318c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira        }
131902a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    }
132002a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira
13212cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira    private void postHandlePendingChips() {
13222cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        mHandler.removeCallbacks(mHandlePendingChips);
13232cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        mHandler.post(mHandlePendingChips);
13242cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira    }
13252cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira
1326db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira    private void checkChipWidths() {
1327db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira        // Check the widths of the associated chips.
1328194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip[] chips = getSortedRecipients();
1329db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira        if (chips != null) {
1330db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira            Rect bounds;
1331194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            for (DrawableRecipientChip chip : chips) {
1332f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                bounds = chip.getBounds();
1333c61c13ffde5d4224ee861f7e7fe3ea0900128a7cKevin Lin                if (getWidth() > 0 && bounds.right - bounds.left >
1334c61c13ffde5d4224ee861f7e7fe3ea0900128a7cKevin Lin                        getWidth() - getPaddingLeft() - getPaddingRight()) {
1335db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira                    // Need to redraw that chip.
1336db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira                    replaceChip(chip, chip.getEntry());
1337db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira                }
1338db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira            }
13392cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        }
1340db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira    }
1341db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira
1342f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    // Visible for testing.
1343f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    /*package*/ void handlePendingChips() {
1344f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        if (getViewWidth() <= 0) {
13458005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira            // The widget has not been sized yet.
13468005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira            // This will be called as a result of onSizeChanged
13478005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira            // at a later point.
13488005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira            return;
13498005f42b51fdca5382ce3fd0b083e40900191da6Mindy Pereira        }
1350db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira        if (mPendingChipsCount <= 0) {
1351db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira            return;
1352db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira        }
1353db1ca655cdbd92286b2f7dc942f504a50c491100Mindy Pereira
13542cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira        synchronized (mPendingChips) {
13552cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira            Editable editable = getText();
13562cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira            // Tokenize!
1357f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            if (mPendingChipsCount <= MAX_CHIPS_PARSED) {
1358f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                for (int i = 0; i < mPendingChips.size(); i++) {
1359f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                    String current = mPendingChips.get(i);
1360f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                    int tokenStart = editable.toString().indexOf(current);
1361488718e8f464d8554dd1cb00395fdfc7871f0fc8mindyp                    // Always leave a space at the end between tokens.
1362488718e8f464d8554dd1cb00395fdfc7871f0fc8mindyp                    int tokenEnd = tokenStart + current.length() - 1;
1363f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                    if (tokenStart >= 0) {
1364f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                        // When we have a valid token, include it with the token
1365f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                        // to the left.
1366f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                        if (tokenEnd < editable.length() - 2
1367f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                                && editable.charAt(tokenEnd) == COMMIT_CHAR_COMMA) {
1368f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                            tokenEnd++;
1369f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                        }
1370f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                        createReplacementChip(tokenStart, tokenEnd, editable, i < CHIP_LIMIT
1371f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                                || !mShouldShrink);
13722cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                    }
1373f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                    mPendingChipsCount--;
13743bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                }
1375f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                sanitizeEnd();
1376f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            } else {
13771163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz                mNoChipMode = true;
13783bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            }
1379f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
1380dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira            if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0
13812cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                    && mTemporaryRecipients.size() <= RecipientAlternatesAdapter.MAX_LOOKUPS) {
13822cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                if (hasFocus() || mTemporaryRecipients.size() < CHIP_LIMIT) {
13832cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                    new RecipientReplacementTask().execute();
13842cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                    mTemporaryRecipients = null;
13852cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                } else {
13862cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                    // Create the "more" chip
13872cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                    mIndividualReplacements = new IndividualReplacementTask();
1388194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                    mIndividualReplacements.execute(new ArrayList<DrawableRecipientChip>(
13892cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                            mTemporaryRecipients.subList(0, CHIP_LIMIT)));
13905e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                    if (mTemporaryRecipients.size() > CHIP_LIMIT) {
1391194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                        mTemporaryRecipients = new ArrayList<DrawableRecipientChip>(
13925e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                                mTemporaryRecipients.subList(CHIP_LIMIT,
13935e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                                        mTemporaryRecipients.size()));
13945e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                    } else {
13955e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                        mTemporaryRecipients = null;
13965e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                    }
13972cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                    createMoreChip();
13982cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                }
13992cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira            } else {
14002cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                // There are too many recipients to look up, so just fall back
1401dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira                // to showing addresses for all of them.
14022cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira                mTemporaryRecipients = null;
14031852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                createMoreChip();
14041852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
14052cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira            mPendingChipsCount = 0;
14062cac23112a97802d460bffe41fa80a939bf730a5Mindy Pereira            mPendingChips.clear();
14073bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        }
14083bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    }
14093bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira
1410f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    // Visible for testing.
1411f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    /*package*/ int getViewWidth() {
1412f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        return getWidth();
1413f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    }
1414f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
14153bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    /**
14163bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira     * Remove any characters after the last valid chip.
14173bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira     */
1418aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Visible for testing.
1419aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    /*package*/ void sanitizeEnd() {
14203c42baf5b81810bce77e776963f20c960865e85bMindy Pereira        // Don't sanitize while we are waiting for pending chips to complete.
14213c42baf5b81810bce77e776963f20c960865e85bMindy Pereira        if (mPendingChipsCount > 0) {
14223c42baf5b81810bce77e776963f20c960865e85bMindy Pereira            return;
14233c42baf5b81810bce77e776963f20c960865e85bMindy Pereira        }
14243bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        // Find the last chip; eliminate any commit characters after it.
1425194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip[] chips = getSortedRecipients();
1426f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        Spannable spannable = getSpannable();
14273bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        if (chips != null && chips.length > 0) {
14283bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            int end;
1429dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira            mMoreChip = getMoreChip();
14303bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            if (mMoreChip != null) {
1431f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                end = spannable.getSpanEnd(mMoreChip);
14323bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            } else {
1433f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                end = getSpannable().getSpanEnd(getLastChip());
14343bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            }
14353bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            Editable editable = getText();
14363bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            int length = editable.length();
14373bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            if (length > end) {
14383bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                // See what characters occur after that and eliminate them.
14393bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                if (Log.isLoggable(TAG, Log.DEBUG)) {
14403bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                    Log.d(TAG, "There were extra characters after the last tokenizable entry."
14413bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                            + editable);
14423bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                }
14433bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                editable.delete(end + 1, length);
14443bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            }
14453bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        }
14463bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    }
14473bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira
14483bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    /**
14493bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira     * Create a chip that represents just the email address of a recipient. At some later
14503bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira     * point, this chip will be attached to a real contact entry, if one exists.
14513bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira     */
145220c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy    // VisibleForTesting
145320c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy    void createReplacementChip(int tokenStart, int tokenEnd, Editable editable,
1454f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            boolean visible) {
145532366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira        if (alreadyHasChip(tokenStart, tokenEnd)) {
145632366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira            // There is already a chip present at this location.
145732366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira            // Don't recreate it.
145832366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira            return;
145932366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira        }
14607ebb40ff05dbf28edd9bbed4eba7e57c8c6005aeMindy Pereira        String token = editable.toString().substring(tokenStart, tokenEnd);
146120c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy        final String trimmedToken = token.trim();
146220c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy        int commitCharIndex = trimmedToken.lastIndexOf(COMMIT_CHAR_COMMA);
146341137c249b5fe547a3631d9c6db57ff980022affTom Taylor        if (commitCharIndex != -1 && commitCharIndex == trimmedToken.length() - 1) {
146420c8aa75e7213ee82089b9aea9f407ca62302167Scott Kennedy            token = trimmedToken.substring(0, trimmedToken.length() - 1);
14653bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        }
14663bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        RecipientEntry entry = createTokenizedEntry(token);
1467ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira        if (entry != null) {
1468194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            DrawableRecipientChip chip = null;
1469ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            try {
14701163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz                if (!mNoChipMode) {
1471ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    chip = visible ? constructChipSpan(entry) : new InvisibleRecipientChip(entry);
1472f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                }
1473ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            } catch (NullPointerException e) {
1474ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira                Log.e(TAG, e.getMessage(), e);
1475ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            }
1476cb76b4d2bcb7b72b9505a620ca71f753506e48b9mindyp            editable.setSpan(chip, tokenStart, tokenEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1477ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            // Add this chip to the list of entries "to replace"
1478ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            if (chip != null) {
1479dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira                if (mTemporaryRecipients == null) {
1480194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                    mTemporaryRecipients = new ArrayList<DrawableRecipientChip>();
1481dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira                }
14820613ff859a6b44685af62821eac369597cf69b26Scott Kennedy                chip.setOriginalText(token);
1483ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira                mTemporaryRecipients.add(chip);
1484ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            }
14851852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        }
14863bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    }
14873bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira
1488983c99b9e00913e8d2a5642cd05ccb9a81570be7Scott Kennedy    // VisibleForTesting
1489983c99b9e00913e8d2a5642cd05ccb9a81570be7Scott Kennedy    RecipientEntry createTokenizedEntry(final String token) {
1490ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira        if (TextUtils.isEmpty(token)) {
1491ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            return null;
1492ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira        }
149323f4fd2e3581f29e164d4b62a9095ab1e492c009Michael Berberet        if (isPhoneQuery() && PhoneUtil.isPhoneNumber(token)) {
14940ba9133c904b8c35af8209a54604331cd671bc1fTom Taylor            return RecipientEntry.constructFakePhoneEntry(token, true);
14955e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        }
14963bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(token);
1497f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        boolean isValid = isValid(token);
1498f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        if (isValid && tokens != null && tokens.length > 0) {
1499ee58f4904fe6d992ad4631604b595ba47d08ca6bMindy Pereira            // If we can get a name from tokenizing, then generate an entry from
1500ee58f4904fe6d992ad4631604b595ba47d08ca6bMindy Pereira            // this.
1501ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            String display = tokens[0].getName();
150201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            if (!TextUtils.isEmpty(display)) {
1503f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                return RecipientEntry.constructGeneratedEntry(display, tokens[0].getAddress(),
1504f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                        isValid);
1505401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira            } else {
1506401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira                display = tokens[0].getAddress();
1507401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira                if (!TextUtils.isEmpty(display)) {
1508f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    return RecipientEntry.constructFakeEntry(display, isValid);
1509401cd96a036a3752b81698c21d1bd5dce16657e4Mindy Pereira                }
151001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            }
151101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        }
15124e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira        // Unable to validate the token or to create a valid token from it.
15134e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira        // Just create a chip the user can edit.
1514dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira        String validatedToken = null;
1515f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        if (mValidator != null && !isValid) {
15164e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira            // Try fixing up the entry using the validator.
1517dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira            validatedToken = mValidator.fixText(token).toString();
1518dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira            if (!TextUtils.isEmpty(validatedToken)) {
1519dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                if (validatedToken.contains(token)) {
1520f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    // protect against the case of a validator with a null
1521f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    // domain,
1522dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                    // which doesn't add a domain to the token
1523dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                    Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(validatedToken);
1524dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                    if (tokenized.length > 0) {
1525dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                        validatedToken = tokenized[0].getAddress();
1526f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                        isValid = true;
1527dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                    }
1528dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                } else {
1529f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    // We ran into a case where the token was invalid and
1530f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    // removed
1531f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    // by the validator. In this case, just use the original
1532f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    // token
1533dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                    // and let the user sort out the error chip.
1534dbb8560594f297da570a4dc2c800eefe0faa6c16Mindy Pereira                    validatedToken = null;
1535f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    isValid = false;
1536184d3773dc096c7a6a83f7aeda042fa8346c2024Erik                }
1537ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira            }
15384e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira        }
1539ee58f4904fe6d992ad4631604b595ba47d08ca6bMindy Pereira        // Otherwise, fallback to just creating an editable email address chip.
1540f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        return RecipientEntry.constructFakeEntry(
1541f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                !TextUtils.isEmpty(validatedToken) ? validatedToken : token, isValid);
154201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira    }
154301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira
15444e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira    private boolean isValid(String text) {
15454e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira        return mValidator == null ? true : mValidator.isValid(text);
15464e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira    }
15474e18d8b62dc38558c8911470ef830b638a26cf00Mindy Pereira
1548f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy    private static String tokenizeAddress(String destination) {
154901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(destination);
155001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        if (tokens != null && tokens.length > 0) {
155101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            return tokens[0].getAddress();
15523bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        }
155301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        return destination;
15543bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    }
15553bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira
155602a1f5fc67366addfa194b864b85a5d467f0543dMindy Pereira    @Override
1557cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    public void setTokenizer(Tokenizer tokenizer) {
1558cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        mTokenizer = tokenizer;
1559cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        super.setTokenizer(mTokenizer);
1560cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
1561cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
1562a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    @Override
1563a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    public void setValidator(Validator validator) {
1564a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira        mValidator = validator;
1565a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira        super.setValidator(validator);
1566a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    }
1567a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira
1568a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
1569a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * We cannot use the default mechanism for replaceText. Instead,
1570a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * we override onItemClickListener so we can get all the associated
1571a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * contact information including display text, address, and id.
1572a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
1573cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    @Override
1574cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    protected void replaceText(CharSequence text) {
1575cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        return;
1576cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
1577cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
1578a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
1579a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * Dismiss any selected chips when the back key is pressed.
1580a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
1581cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    @Override
1582ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
158303cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira        if (keyCode == KeyEvent.KEYCODE_BACK && mSelectedChip != null) {
15844c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira            clearSelectedChip();
158503cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira            return true;
15864c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira        }
15874c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira        return super.onKeyPreIme(keyCode, event);
15884c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira    }
15894c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira
1590a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
1591a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * Monitor key presses in this view to see if the user types
1592a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * any commit keys, which consist of ENTER, TAB, or DPAD_CENTER.
1593a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * If the user has entered text that has contact matches and types
1594a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * a commit key, create a chip from the topmost matching contact.
1595a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * If the user has entered text that has no contact matches and types
1596a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * a commit key, then create a chip from the text they have entered.
1597a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
15984c5fc2c74cb001fa930b1a264fb10a1071317086Mindy Pereira    @Override
1599ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
1600cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        switch (keyCode) {
16010f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira            case KeyEvent.KEYCODE_TAB:
16020f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira                if (event.hasNoModifiers()) {
16030f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira                    if (mSelectedChip != null) {
16040f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira                        clearSelectedChip();
16050f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira                    } else {
16060f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira                        commitDefault();
16070f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira                    }
16080f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira                }
160903cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira                break;
1610cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
1611cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        return super.onKeyUp(keyCode, event);
1612cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
1613cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
16140f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira    private boolean focusNext() {
16150f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira        View next = focusSearch(View.FOCUS_DOWN);
16160f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira        if (next != null) {
16170f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira            next.requestFocus();
16180f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira            return true;
16190f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira        }
16200f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira        return false;
16210f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira    }
16220f0df298b8bc7973f98397153802da0bd1607c83Mindy Pereira
1623342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira    /**
1624342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira     * Create a chip from the default selection. If the popup is showing, the
1625e4945e96e5b67a1a5b8fd165debc616a351fda17Scott Kennedy     * default is the selected item (if one is selected), or the first item, in the popup
1626e4945e96e5b67a1a5b8fd165debc616a351fda17Scott Kennedy     * suggestions list. Otherwise, it is whatever the user had typed in. End represents where the
1627e4945e96e5b67a1a5b8fd165debc616a351fda17Scott Kennedy     * tokenizer should search for a token to turn into a chip.
1628342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira     * @return If a chip was created from a real contact.
1629342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira     */
1630a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    private boolean commitDefault() {
163160d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira        // If there is no tokenizer, don't try to commit.
163260d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira        if (mTokenizer == null) {
163360d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira            return false;
163460d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira        }
163512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        Editable editable = getText();
1636342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira        int end = getSelectionEnd();
163712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        int start = mTokenizer.findTokenStart(editable, end);
16382b4ffc53a3b51631cb6aabf535986a9344ee6cbbMindy Pereira
163979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        if (shouldCreateChip(start, end)) {
164079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            int whatEnd = mTokenizer.findTokenEnd(getText(), start);
164179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            // In the middle of chip; treat this as an edit
164279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            // and commit the whole token.
16434f82d888c680a61b95373740ce68bfb48a242617mindyp            whatEnd = movePastTerminators(whatEnd);
164479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            if (whatEnd != getSelectionEnd()) {
164579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira                handleEdit(start, whatEnd);
164679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira                return true;
164712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            }
164879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            return commitChip(start, end , editable);
164979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        }
165079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        return false;
165179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    }
165279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira
165379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    private void commitByCharacter() {
165460d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira        // We can't possibly commit by character if we can't tokenize.
165560d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira        if (mTokenizer == null) {
165660d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira            return;
165760d96dccf8f82c31976d29842b85f12dc905cfd1Mindy Pereira        }
165879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        Editable editable = getText();
165979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        int end = getSelectionEnd();
166079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        int start = mTokenizer.findTokenStart(editable, end);
166179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        if (shouldCreateChip(start, end)) {
166279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            commitChip(start, end, editable);
166312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        }
1664ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira        setSelection(getText().length());
166579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    }
166612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
166779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    private boolean commitChip(int start, int end, Editable editable) {
16684686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        int position = positionOfFirstEntryWithTypePerson();
16694686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        if (position != -1 && enoughToFilter()
1670dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz                && end == getSelectionEnd() && !isPhoneQuery()
1671dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz                && !isValidEmailAddress(editable.toString().substring(start, end).trim())) {
1672e4945e96e5b67a1a5b8fd165debc616a351fda17Scott Kennedy            // let's choose the selected or first entry if only the input text is NOT an email
1673e4945e96e5b67a1a5b8fd165debc616a351fda17Scott Kennedy            // address so we won't try to replace the user's potentially correct but
1674e4945e96e5b67a1a5b8fd165debc616a351fda17Scott Kennedy            // new/unencountered email input
1675dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz            final int selectedPosition = getListSelection();
1676dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz            if (selectedPosition == -1 || !isEntryAtPositionTypePerson(selectedPosition)) {
1677dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz                // Nothing is selected or selected item is not type person; use the first item
1678dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz                submitItemAtPosition(position);
1679dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz            } else {
1680dbe88c3002c8a8d7f8cceaef988462502e4c557bEkin Oguz                submitItemAtPosition(selectedPosition);
1681b3b248acd845b017e952fb802e0976e82fc1c583Jin Cao            }
168279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            dismissDropDown();
168379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            return true;
168479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        } else {
168579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            int tokenEnd = mTokenizer.findTokenEnd(editable, start);
168620c9d620e79ae28994856541761a951074551518Mindy Pereira            if (editable.length() > tokenEnd + 1) {
168720c9d620e79ae28994856541761a951074551518Mindy Pereira                char charAt = editable.charAt(tokenEnd + 1);
168820c9d620e79ae28994856541761a951074551518Mindy Pereira                if (charAt == COMMIT_CHAR_COMMA || charAt == COMMIT_CHAR_SEMICOLON) {
168920c9d620e79ae28994856541761a951074551518Mindy Pereira                    tokenEnd++;
169020c9d620e79ae28994856541761a951074551518Mindy Pereira                }
1691d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira            }
169262397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira            String text = editable.toString().substring(start, tokenEnd).trim();
169379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            clearComposingText();
1694ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            if (text.length() > 0 && !text.equals(" ")) {
169501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira                RecipientEntry entry = createTokenizedEntry(text);
1696ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira                if (entry != null) {
1697ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira                    QwertyKeyListener.markAsReplaced(editable, start, end, "");
1698ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    CharSequence chipText = createChip(entry);
1699ee6d83fe1da297b2f9af0fb221be376fdc816830Mindy Pereira                    if (chipText != null && start > -1 && end > -1) {
1700f177bdab5724635aed964de889febe96ecab6bc0Mindy Pereira                        editable.replace(start, end, chipText);
1701f177bdab5724635aed964de889febe96ecab6bc0Mindy Pereira                    }
1702ba5642a5729d9d1483702d42ae4a21d8cbfaa804Mindy Pereira                }
170320c9d620e79ae28994856541761a951074551518Mindy Pereira                // Only dismiss the dropdown if it is related to the text we
170420c9d620e79ae28994856541761a951074551518Mindy Pereira                // just committed.
170520c9d620e79ae28994856541761a951074551518Mindy Pereira                // For paste, it may not be as there are possibly multiple
170620c9d620e79ae28994856541761a951074551518Mindy Pereira                // tokens being added.
170720c9d620e79ae28994856541761a951074551518Mindy Pereira                if (end == getSelectionEnd()) {
170820c9d620e79ae28994856541761a951074551518Mindy Pereira                    dismissDropDown();
170920c9d620e79ae28994856541761a951074551518Mindy Pereira                }
17105df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira                sanitizeBetween();
171112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira                return true;
171205dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira            }
171305dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira        }
171412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        return false;
171505dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira    }
171605dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira
17174686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong    private int positionOfFirstEntryWithTypePerson() {
17184686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        ListAdapter adapter = getAdapter();
17194686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        int itemCount = adapter != null ? adapter.getCount() : 0;
17204686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        for (int i = 0; i < itemCount; i++) {
17214686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong            if (isEntryAtPositionTypePerson(i)) {
17224686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong                return i;
17234686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong            }
17244686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        }
17254686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        return -1;
17264686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong    }
17274686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong
17284686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong    private boolean isEntryAtPositionTypePerson(int position) {
17294686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong        return getAdapter().getItem(position).getEntryType() == RecipientEntry.ENTRY_TYPE_PERSON;
17304686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong    }
17314686ef5cf68cb1ea190f82a05b244d276d99d1bbJae Yoon Chong
173201162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira    // Visible for testing.
173301162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira    /* package */ void sanitizeBetween() {
17343c42baf5b81810bce77e776963f20c960865e85bMindy Pereira        // Don't sanitize while we are waiting for content to chipify.
17353c42baf5b81810bce77e776963f20c960865e85bMindy Pereira        if (mPendingChipsCount > 0) {
17363c42baf5b81810bce77e776963f20c960865e85bMindy Pereira            return;
17373c42baf5b81810bce77e776963f20c960865e85bMindy Pereira        }
17385df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira        // Find the last chip.
1739194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip[] recips = getSortedRecipients();
17405df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira        if (recips != null && recips.length > 0) {
1741194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            DrawableRecipientChip last = recips[recips.length - 1];
1742194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            DrawableRecipientChip beforeLast = null;
17435df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira            if (recips.length > 1) {
17445df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira                beforeLast = recips[recips.length - 2];
17455df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira            }
17465df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira            int startLooking = 0;
17475df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira            int end = getSpannable().getSpanStart(last);
17485df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira            if (beforeLast != null) {
17495df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira                startLooking = getSpannable().getSpanEnd(beforeLast);
1750399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira                Editable text = getText();
175101162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira                if (startLooking == -1 || startLooking > text.length() - 1) {
1752399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira                    // There is nothing after this chip.
1753399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira                    return;
1754399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira                }
1755399bda87ad1a4d003609d6d27afc50c8359846b9Mindy Pereira                if (text.charAt(startLooking) == ' ') {
17565df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira                    startLooking++;
17575df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira                }
17585df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira            }
1759e13775cc6e9342e42db0b853cc42dbfda6d1365fMindy Pereira            if (startLooking >= 0 && end >= 0 && startLooking < end) {
17605df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira                getText().delete(startLooking, end);
17615df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira            }
17625df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira        }
17635df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira    }
17645df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira
176579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    private boolean shouldCreateChip(int start, int end) {
17661163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz        return !mNoChipMode && hasFocus() && enoughToFilter() && !alreadyHasChip(start, end);
176732366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira    }
176832366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira
176932366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira    private boolean alreadyHasChip(int start, int end) {
17701163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz        if (mNoChipMode) {
1771f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            return true;
1772f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        }
1773194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip[] chips =
1774194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                getSpannable().getSpans(start, end, DrawableRecipientChip.class);
1775ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        return chips != null && chips.length > 0;
177679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    }
177779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira
177879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    private void handleEdit(int start, int end) {
1779a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira        if (start == -1 || end == -1) {
1780a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira            // This chip no longer exists in the field.
1781a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira            dismissDropDown();
1782a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira            return;
1783a49e7fa477692ba2f6208a1a3e80e75e67e65a60Mindy Pereira        }
178479fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        // This is in the middle of a chip, so select out the whole chip
178579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        // and commit it.
178679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        Editable editable = getText();
178779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        setSelection(end);
178879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        String text = getText().toString().substring(start, end);
1789fe52b97e748ec0b9bd44b759780ed42b9dcee7ffMindy Pereira        if (!TextUtils.isEmpty(text)) {
1790f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            RecipientEntry entry = RecipientEntry.constructFakeEntry(text, isValid(text));
1791fe52b97e748ec0b9bd44b759780ed42b9dcee7ffMindy Pereira            QwertyKeyListener.markAsReplaced(editable, start, end, "");
1792ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            CharSequence chipText = createChip(entry);
17935cfd6fea275724ce223cb8f4a1821922c8763631Mindy Pereira            int selEnd = getSelectionEnd();
17945cfd6fea275724ce223cb8f4a1821922c8763631Mindy Pereira            if (chipText != null && start > -1 && selEnd > -1) {
17955cfd6fea275724ce223cb8f4a1821922c8763631Mindy Pereira                editable.replace(start, selEnd, chipText);
17962d7709d276c03e536d37961076107af9f98522f5Mindy Pereira            }
1797fe52b97e748ec0b9bd44b759780ed42b9dcee7ffMindy Pereira        }
1798ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira        dismissDropDown();
179964af2da9970f59eee2dfd0c8dd2a06f09171bad2Mindy Pereira    }
180064af2da9970f59eee2dfd0c8dd2a06f09171bad2Mindy Pereira
1801a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
1802a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * If there is a selected chip, delegate the key events
1803a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * to the selected chip.
1804a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
1805cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    @Override
1806ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
180795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (mSelectedChip != null && keyCode == KeyEvent.KEYCODE_DEL) {
180895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            if (mAlternatesPopup != null && mAlternatesPopup.isShowing()) {
180995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira                mAlternatesPopup.dismiss();
181095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            }
181195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            removeChip(mSelectedChip);
1812cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
1813cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
18142fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy        switch (keyCode) {
18152fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy            case KeyEvent.KEYCODE_ENTER:
18162fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy            case KeyEvent.KEYCODE_DPAD_CENTER:
18172fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                if (event.hasNoModifiers()) {
18182fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                    if (commitDefault()) {
18192fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                        return true;
18202fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                    }
18212fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                    if (mSelectedChip != null) {
18222fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                        clearSelectedChip();
18232fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                        return true;
18242fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                    } else if (focusNext()) {
18252fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                        return true;
18262fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                    }
18272fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                }
18282fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy                break;
18292fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy        }
18302fe0cffa8dce1712fb5b568ab06e13857ef4f7c1Scott Kennedy
18318cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz        final DrawableRecipientChip lastRecipientChip = getLastChip();
18328cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz        boolean isHandled = super.onKeyDown(keyCode, event);
18338cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz
18348cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz        /*
18358cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz         * Hacky way to report a deleted chip:
18368cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz         * In some devices/configurations, {@link KeyEvent#KEYCODE_DEL} character is causing
18378cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz         * onKeyDown() to be called, which in turns handles the chip deletion instead of
18388cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz         * {@link RecipientTextWatcher#onTextChanged}. We want to call
18398cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz         * {@link RecipientChipDeletedListener#onRecipientChipDeleted} callback for these cases.
18408cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz         */
18418cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz        if (keyCode == KeyEvent.KEYCODE_DEL && isHandled && lastRecipientChip != null) {
18428cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz            final RecipientEntry entry = lastRecipientChip.getEntry();
18438cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz            if (!mNoChipMode && mRecipientChipDeletedListener != null && entry != null) {
18448cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz                mRecipientChipDeletedListener.onRecipientChipDeleted(entry);
18458cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz            }
18468cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz        }
18478cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz
18488cd6353bdce56d423a9381f2efbc20d26f600710Ekin Oguz        return isHandled;
18499159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira    }
18509159cd236a0bd16c3ee632eb085c4e432909493cMindy Pereira
185101162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira    // Visible for testing.
185201162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira    /* package */ Spannable getSpannable() {
1853e33555f13a9b05d835cb860e2c30ef40af3c8502Erik        return getText();
1854cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
1855cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
1856194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    private int getChipStart(DrawableRecipientChip chip) {
185795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        return getSpannable().getSpanStart(chip);
185895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
185995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira
1860194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    private int getChipEnd(DrawableRecipientChip chip) {
186195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        return getSpannable().getSpanEnd(chip);
186295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
186395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira
1864cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    /**
1865cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira     * Instead of filtering on the entire contents of the edit box,
1866cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira     * this subclass method filters on the range from
1867cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira     * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd}
1868cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira     * if the length of that range meets or exceeds {@link #getThreshold}
1869cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira     * and makes sure that the range is not already a Chip.
1870cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira     */
1871cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    @Override
1872f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    public void performFiltering(@NonNull CharSequence text, int keyCode) {
1873093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp        boolean isCompletedToken = isCompletedToken(text);
1874093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp        if (enoughToFilter() && !isCompletedToken) {
1875cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            int end = getSelectionEnd();
1876cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            int start = mTokenizer.findTokenStart(text, end);
1877cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            // If this is a RecipientChip, don't filter
1878cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            // on its contents.
1879cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            Spannable span = getSpannable();
1880194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            DrawableRecipientChip[] chips = span.getSpans(start, end, DrawableRecipientChip.class);
1881cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            if (chips != null && chips.length > 0) {
188241e93fbe82cd4d802e3f1fbe265038f7a0521dddKevin Lin                dismissDropDown();
1883cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira                return;
1884cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            }
1885093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp        } else if (isCompletedToken) {
188641e93fbe82cd4d802e3f1fbe265038f7a0521dddKevin Lin            dismissDropDown();
1887093ecdd3cde3e54df3fb30f5ca47067fdcf46f3cmindyp            return;
1888cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
1889cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        super.performFiltering(text, keyCode);
1890cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
1891cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
189220c9d620e79ae28994856541761a951074551518Mindy Pereira    // Visible for testing.
189320c9d620e79ae28994856541761a951074551518Mindy Pereira    /*package*/ boolean isCompletedToken(CharSequence text) {
189420c9d620e79ae28994856541761a951074551518Mindy Pereira        if (TextUtils.isEmpty(text)) {
189520c9d620e79ae28994856541761a951074551518Mindy Pereira            return false;
189620c9d620e79ae28994856541761a951074551518Mindy Pereira        }
189720c9d620e79ae28994856541761a951074551518Mindy Pereira        // Check to see if this is a completed token before filtering.
189820c9d620e79ae28994856541761a951074551518Mindy Pereira        int end = text.length();
189920c9d620e79ae28994856541761a951074551518Mindy Pereira        int start = mTokenizer.findTokenStart(text, end);
190020c9d620e79ae28994856541761a951074551518Mindy Pereira        String token = text.toString().substring(start, end).trim();
190120c9d620e79ae28994856541761a951074551518Mindy Pereira        if (!TextUtils.isEmpty(token)) {
190220c9d620e79ae28994856541761a951074551518Mindy Pereira            char atEnd = token.charAt(token.length() - 1);
190320c9d620e79ae28994856541761a951074551518Mindy Pereira            return atEnd == COMMIT_CHAR_COMMA || atEnd == COMMIT_CHAR_SEMICOLON;
190420c9d620e79ae28994856541761a951074551518Mindy Pereira        }
190520c9d620e79ae28994856541761a951074551518Mindy Pereira        return false;
190620c9d620e79ae28994856541761a951074551518Mindy Pereira    }
190720c9d620e79ae28994856541761a951074551518Mindy Pereira
190849744b029d48dd9082bc02cb1c00dc34d1619c85Jin Cao    /**
190949744b029d48dd9082bc02cb1c00dc34d1619c85Jin Cao     * Clears the selected chip if there is one (and dismissing any popups related to the selected
191049744b029d48dd9082bc02cb1c00dc34d1619c85Jin Cao     * chip in the process).
191149744b029d48dd9082bc02cb1c00dc34d1619c85Jin Cao     */
191249744b029d48dd9082bc02cb1c00dc34d1619c85Jin Cao    public void clearSelectedChip() {
1913cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        if (mSelectedChip != null) {
191495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            unselectChip(mSelectedChip);
1915cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            mSelectedChip = null;
1916cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
1917b86dcd5230ebcc57e5fc7a669c2304aca142dbf5Mindy Pereira        setCursorVisible(true);
1918ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        setSelection(getText().length());
1919cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
1920cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
1921a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
1922a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * Monitor touch events in the RecipientEditTextView.
1923a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * If the view does not have focus, any tap on the view
1924a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * will just focus the view. If the view has focus, determine
1925a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * if the touch target is a recipient chip. If it is and the chip
1926a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * is not selected, select it and clear any other selected chips.
1927a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * If it isn't, then select that chip.
1928a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
1929cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    @Override
1930ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    public boolean onTouchEvent(@NonNull MotionEvent event) {
1931e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        boolean handled;
193205dbd33931e590ccb538bbf9d7d93d53aaee366eMindy Pereira        int action = event.getAction();
1933e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        final float x = event.getX();
1934e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        final float y = event.getY();
1935e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        final int offset = putOffsetInRange(x, y);
1936e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        final DrawableRecipientChip currentChip = findChip(offset);
1937c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sapperstein        if (action == MotionEvent.ACTION_UP) {
1938e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            boolean touchedWarningIcon = touchedWarningIcon(x, y, currentChip);
1939e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            if (touchedWarningIcon) {
1940e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam                String warningText = String.format(mWarningTextTemplate,
1941e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam                    currentChip.getEntry().getDestination());
1942e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam                showWarningDialog(warningText);
1943e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam                return true;
1944e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            }
1945e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            if (!isFocused()) {
1946e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                // Ignore further chip taps until this view is focused.
1947e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                return touchedWarningIcon || super.onTouchEvent(event);
1948e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            }
1949e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            handled = super.onTouchEvent(event);
1950e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            if (mSelectedChip == null) {
1951e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                mGestureDetector.onTouchEvent(event);
1952e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            }
1953e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            boolean chipWasSelected = false;
1954cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            if (currentChip != null) {
1955ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                if (mSelectedChip != null && mSelectedChip != currentChip) {
1956ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    clearSelectedChip();
1957ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    selectChip(currentChip);
1958ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                } else if (mSelectedChip == null) {
1959ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    commitDefault();
1960ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    selectChip(currentChip);
1961ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                } else {
1962ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    onClick(mSelectedChip);
1963cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira                }
1964cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira                chipWasSelected = true;
1965c1fed8b74327fccbab18458aef2d1bb0a6996e9cMindy Pereira                handled = true;
19665e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp            } else if (mSelectedChip != null && shouldShowEditableText(mSelectedChip)) {
1967c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira                chipWasSelected = true;
1968cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            }
1969e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            if (!chipWasSelected) {
1970e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                clearSelectedChip();
1971e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            }
1972e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        } else {
1973e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            boolean touchedWarningIcon = touchedWarningIcon(x, y, currentChip);
1974e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            if (touchedWarningIcon) {
1975e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam                return true;
1976e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            }
1977e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            handled = super.onTouchEvent(event);
1978e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            if (!isFocused()) {
1979e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                return handled;
1980e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            }
1981e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            if (mSelectedChip == null) {
1982e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                mGestureDetector.onTouchEvent(event);
1983e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            }
1984cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
1985cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        return handled;
1986cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
1987cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
1988e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam    private boolean touchedWarningIcon(float x, float y, DrawableRecipientChip currentChip) {
1989e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        boolean touchedWarningIcon = false;
1990e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        if (currentChip != null) {
1991e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            Rect outOfDomainWarningBounds = currentChip.getWarningIconBounds();
1992e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            if (outOfDomainWarningBounds != null) {
19931c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                int chipLeftOffset = shouldPositionAvatarOnRight()
19941c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                        ? getChipEnd(currentChip) : getChipStart(currentChip);
19951c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                float chipLeftPosition = this.getLayout().getPrimaryHorizontal(chipLeftOffset);
19961c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                float chipTopPosition = this.getLayout().getLineTop(
19971c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                        this.getLayout().getLineForOffset(chipLeftOffset)) + getTotalPaddingTop();
1998e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam                final RectF touchOutOfDomainWarning = new RectF(
19991c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                        chipLeftPosition + outOfDomainWarningBounds.left,
20001c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                        chipTopPosition + outOfDomainWarningBounds.top,
20011c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                        chipLeftPosition + outOfDomainWarningBounds.right,
20021c4ebfe46e0c7088d377763b92dd4b8ea89c6fb8Joseph Moghadam                        chipTopPosition + outOfDomainWarningBounds.bottom);
2003e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam                touchedWarningIcon = touchOutOfDomainWarning.contains(x, y);
2004e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam            }
2005e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        }
2006e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam        return touchedWarningIcon;
2007e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam    }
2008e2f29e0ad6e5426955852217686a3d55ec0a7ad7Joseph Moghadam
2009b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam    private void showWarningDialog(String warningText) {
2010b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        mCurrentWarningText = warningText;
2011b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam        new AlertDialog.Builder(RecipientEditTextView.this.getContext())
2012b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                .setTitle(mWarningTitle)
2013b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                .setOnDismissListener(new DialogInterface.OnDismissListener() {
2014b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                    @Override
2015b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                    public void onDismiss(DialogInterface dialog) {
2016b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                        mCurrentWarningText = "";
2017b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                    }
2018b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                })
2019b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                .setMessage(mCurrentWarningText)
2020b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam                .show();
2021b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam    }
2022b37b09e7269db1d135abcd9ff0660f9ee00a83c4Joseph Moghadam
2023272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy    private void showAlternates(final DrawableRecipientChip currentChip,
20249b166209d723ed996243de5d89b489f3992daf0eJin Cao            final ListPopupWindow alternatesPopup) {
2025272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy        new AsyncTask<Void, Void, ListAdapter>() {
2026272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy            @Override
2027272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy            protected ListAdapter doInBackground(final Void... params) {
2028272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                return createAlternatesAdapter(currentChip);
2029272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy            }
2030272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy
20316b7110f320c978c368c28bdb06212c6a6df12f1fAlice Yang            @Override
2032272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy            protected void onPostExecute(final ListAdapter result) {
2033fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler                if (!mAttachedToWindow) {
2034fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler                    return;
2035fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler                }
2036272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                int line = getLayout().getLineForOffset(getChipStart(currentChip));
20376fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao                int bottomOffset = calculateOffsetFromBottomToTop(line);
20386fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao
2039272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                // Align the alternates popup with the left side of the View,
2040272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                // regardless of the position of the chip tapped.
20414de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao                alternatesPopup.setAnchorView((mAlternatePopupAnchor != null) ?
20424de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao                        mAlternatePopupAnchor : RecipientEditTextView.this);
20436fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao                alternatesPopup.setVerticalOffset(bottomOffset);
2044272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                alternatesPopup.setAdapter(result);
2045272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                alternatesPopup.setOnItemClickListener(mAlternatesListener);
2046272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                // Clear the checked item.
2047272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                mCheckedItem = -1;
2048272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                alternatesPopup.show();
2049272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                ListView listView = alternatesPopup.getListView();
2050272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
2051272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                // Checked item would be -1 if the adapter has not
2052272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                // loaded the view that should be checked yet. The
2053272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                // variable will be set correctly when onCheckedItemChanged
2054272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                // is called in a separate thread.
2055272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                if (mCheckedItem != -1) {
2056272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                    listView.setItemChecked(mCheckedItem, true);
2057272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                    mCheckedItem = -1;
2058272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy                }
2059272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy            }
2060272aae0dadf8b63b83e61bc7d9f5093f51e06564Scott Kennedy        }.execute((Void[]) null);
206195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
206295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira
20636e96c08f77c783d7eb7a926151f16f95255205adMichael Berberet    protected ListAdapter createAlternatesAdapter(DrawableRecipientChip chip) {
20647a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        return new RecipientAlternatesAdapter(getContext(), chip.getContactId(),
20657a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                chip.getDirectoryId(), chip.getLookupKey(), chip.getDataId(),
20664db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao                getAdapter().getQueryType(), this, mDropdownChipLayouter,
20674b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic                constructStateListDeleteDrawable(), getAdapter().getPermissionsCheckListener());
206895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
206995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira
2070194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    private ListAdapter createSingleAddressAdapter(DrawableRecipientChip currentChip) {
2071b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return new SingleRecipientArrayAdapter(getContext(), currentChip.getEntry(),
20724db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao                mDropdownChipLayouter, constructStateListDeleteDrawable());
20734db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao    }
20744db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao
20754db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao    private StateListDrawable constructStateListDeleteDrawable() {
20764db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        // Construct the StateListDrawable from deleteDrawable
20774db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        StateListDrawable deleteDrawable = new StateListDrawable();
2078a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao        if (!mDisableDelete) {
2079a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao            deleteDrawable.addState(new int[]{android.R.attr.state_activated}, mChipDelete);
2080a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao        }
20814db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        deleteDrawable.addState(new int[0], null);
20824db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        return deleteDrawable;
208301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira    }
208401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira
2085aa2afffe7aba707c2406f2e4503fa6037c4cd196Andy Stadler    @Override
208695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    public void onCheckedItemChanged(int position) {
208795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        ListView listView = mAlternatesPopup.getListView();
208895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (listView != null && listView.getCheckedItemCount() == 0) {
208995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            listView.setItemChecked(position, true);
209095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        }
209198b547f2eeb80259036e3f528636d7cbd823bf6dMindy Pereira        mCheckedItem = position;
209295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
209395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira
20941650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    private int putOffsetInRange(final float x, final float y) {
20951650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        final int offset;
20961650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy
20971650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
20981650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy            offset = getOffsetForPosition(x, y);
20991650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        } else {
21001650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy            offset = supportGetOffsetForPosition(x, y);
21011650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        }
21021650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy
21031650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        return putOffsetInRange(offset);
21041650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    }
21051650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy
2106cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    // TODO: This algorithm will need a lot of tweaking after more people have used
2107cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    // the chips ui. This attempts to be "forgiving" to fat finger touches by favoring
2108cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    // what comes before the finger.
2109cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    private int putOffsetInRange(int o) {
2110cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        int offset = o;
2111cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        Editable text = getText();
2112cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        int length = text.length();
2113cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // Remove whitespace from end to find "real end"
2114cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        int realLength = length;
2115cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        for (int i = length - 1; i >= 0; i--) {
2116cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            if (text.charAt(i) == ' ') {
2117cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira                realLength--;
2118cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            } else {
2119cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira                break;
2120cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            }
2121cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
2122cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
2123fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        // If the offset is beyond or at the end of the text,
2124fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        // leave it alone.
2125fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        if (offset >= realLength) {
2126cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            return offset;
2127cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
2128fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        Editable editable = getText();
2129b88ee450829eb4ac24fb47c377b9ec3aab0782daMindy Pereira        while (offset >= 0 && findText(editable, offset) == -1 && findChip(offset) == null) {
2130cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            // Keep walking backward!
2131cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            offset--;
2132cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
2133cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        return offset;
2134cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
2135cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
2136f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy    private static int findText(Editable text, int offset) {
2137fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        if (text.charAt(offset) != ' ') {
2138fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira            return offset;
2139fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        }
2140fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira        return -1;
2141fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira    }
2142fd68ea64d80f3b299b20ab72b6ed71d3c4ff6c9cMindy Pereira
2143194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    private DrawableRecipientChip findChip(int offset) {
2144ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        final Spannable span = getSpannable();
2145ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        final DrawableRecipientChip[] chips =
2146ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                span.getSpans(0, span.length(), DrawableRecipientChip.class);
2147cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // Find the chip that contains this offset.
2148ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        for (DrawableRecipientChip chip : chips) {
214995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            int start = getChipStart(chip);
215095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            int end = getChipEnd(chip);
215195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            if (offset >= start && offset <= end) {
2152cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira                return chip;
2153cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira            }
2154cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
2155cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        return null;
2156cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
2157cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
215801162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira    // Visible for testing.
2159aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Use this method to generate text to add to the list of addresses.
216032aff5f5bdc7c860169e50eacb7120983b3901a8Mindy Pereira    /* package */String createAddressText(RecipientEntry entry) {
21613ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira        String display = entry.getDisplayName();
21623ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira        String address = entry.getDestination();
21633ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira        if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) {
21643ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira            display = null;
21653ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira        }
2166abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor        String trimmedDisplayText;
216723f4fd2e3581f29e164d4b62a9095ab1e492c009Michael Berberet        if (isPhoneQuery() && PhoneUtil.isPhoneNumber(address)) {
2168abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor            trimmedDisplayText = address.trim();
2169abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor        } else {
2170abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor            if (address != null) {
2171abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor                // Tokenize out the address in case the address already
2172abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor                // contained the username as well.
2173abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor                Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(address);
2174abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor                if (tokenized != null && tokenized.length > 0) {
2175abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor                    address = tokenized[0].getAddress();
2176abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor                }
2177ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira            }
2178abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor            Rfc822Token token = new Rfc822Token(display, address, null);
2179abff606ceb5e103d4a21c56fb9b78d7844c8e5b7Tom Taylor            trimmedDisplayText = token.toString().trim();
2180d8c15c328eaa109946f8f9093f3c2f2773d525ddMindy Pereira        }
21813ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira        int index = trimmedDisplayText.indexOf(",");
218232aff5f5bdc7c860169e50eacb7120983b3901a8Mindy Pereira        return mTokenizer != null && !TextUtils.isEmpty(trimmedDisplayText)
218332aff5f5bdc7c860169e50eacb7120983b3901a8Mindy Pereira                && index < trimmedDisplayText.length() - 1 ? (String) mTokenizer
2184aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira                .terminateToken(trimmedDisplayText) : trimmedDisplayText;
2185aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    }
2186aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira
2187aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Visible for testing.
2188aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Use this method to generate text to display in a chip.
2189aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    /*package*/ String createChipDisplayText(RecipientEntry entry) {
2190aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        String display = entry.getDisplayName();
2191aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        String address = entry.getDestination();
2192aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) {
2193aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            display = null;
2194aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        }
2195aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        if (!TextUtils.isEmpty(display)) {
2196aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            return display;
2197aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        } else if (!TextUtils.isEmpty(address)){
2198aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            return address;
2199aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        } else {
2200aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira            return new Rfc822Token(display, address, null).toString();
2201aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira        }
22023ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira    }
22033ea9955a3c06d241bc6e4ab659ec3a1d069fd6dfMindy Pereira
2204ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    private CharSequence createChip(RecipientEntry entry) {
22054db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        final String displayText = createAddressText(entry);
22062d7709d276c03e536d37961076107af9f98522f5Mindy Pereira        if (TextUtils.isEmpty(displayText)) {
22072d7709d276c03e536d37961076107af9f98522f5Mindy Pereira            return null;
22082d7709d276c03e536d37961076107af9f98522f5Mindy Pereira        }
2209cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        // Always leave a blank space at the end of a chip.
22104db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        final int textLength = displayText.length() - 1;
22114db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        final SpannableString  chipText = new SpannableString(displayText);
22121163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz        if (!mNoChipMode) {
2213f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            try {
2214ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                DrawableRecipientChip chip = constructChipSpan(entry);
2215f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                chipText.setSpan(chip, 0, textLength,
2216f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
2217f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                chip.setOriginalText(chipText.toString());
2218f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            } catch (NullPointerException e) {
2219f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                Log.e(TAG, e.getMessage(), e);
2220f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                return null;
2221f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            }
2222cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
22233e05cfa55dbb8840adaf3dfcf31a5a30ce129c9eKevin Lin        onChipCreated(entry);
2224cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        return chipText;
2225cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
2226cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
2227a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
22283e05cfa55dbb8840adaf3dfcf31a5a30ce129c9eKevin Lin     * A callback for subclasses to use to know when a chip was created with the
22293e05cfa55dbb8840adaf3dfcf31a5a30ce129c9eKevin Lin     * given RecipientEntry.
22303e05cfa55dbb8840adaf3dfcf31a5a30ce129c9eKevin Lin     */
2231ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    protected void onChipCreated(RecipientEntry entry) {
22321163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz        if (!mNoChipMode && mRecipientChipAddedListener != null) {
2233ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz            mRecipientChipAddedListener.onRecipientChipAdded(entry);
2234ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz        }
2235ae2cd1536163c0166679e6b4d64ba2df28588ff3Ekin Oguz    }
22363e05cfa55dbb8840adaf3dfcf31a5a30ce129c9eKevin Lin
22373e05cfa55dbb8840adaf3dfcf31a5a30ce129c9eKevin Lin    /**
2238a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * When an item in the suggestions list has been clicked, create a chip from the
2239a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * contact information of the selected item.
2240a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
2241cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    @Override
2242cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
2243114c89364bd00a445f0b017ae658928c1dc26c5aTom Taylor        if (position < 0) {
2244114c89364bd00a445f0b017ae658928c1dc26c5aTom Taylor            return;
2245114c89364bd00a445f0b017ae658928c1dc26c5aTom Taylor        }
22460f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein
2247f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        final RecipientEntry entry = getAdapter().getItem(position);
2248f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        if (entry.getEntryType() == RecipientEntry.ENTRY_TYPE_PERMISSION_REQUEST) {
2249f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            if (mPermissionsRequestItemClickedListener != null) {
2250f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                mPermissionsRequestItemClickedListener
2251f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                        .onPermissionsRequestItemClicked(this, entry.getPermissions());
2252f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            }
2253f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            return;
2254f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        }
2255f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
22560f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein        final int charactersTyped = submitItemAtPosition(position);
22570f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein        if (charactersTyped > -1 && mRecipientEntryItemClickedListener != null) {
22580f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein            mRecipientEntryItemClickedListener
22590f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein                    .onRecipientEntryItemClicked(charactersTyped, position);
22600f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein        }
2261cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
2262cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
22630f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein    private int submitItemAtPosition(int position) {
2264858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy        RecipientEntry entry = createValidatedEntry(getAdapter().getItem(position));
2265f7eaa5fc06e3dd57457f7bf0169374a4c9a1e413Erik        if (entry == null) {
22660f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein            return -1;
2267f7eaa5fc06e3dd57457f7bf0169374a4c9a1e413Erik        }
2268cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        clearComposingText();
2269cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
2270cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        int end = getSelectionEnd();
2271cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        int start = mTokenizer.findTokenStart(getText(), end);
2272cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
2273cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        Editable editable = getText();
2274cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        QwertyKeyListener.markAsReplaced(editable, start, end, "");
2275ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        CharSequence chip = createChip(entry);
2276005b2e29742d7b9f3ecc505a50b11d91bd44c818Mindy Pereira        if (chip != null && start >= 0 && end >= 0) {
2277e90bfad1a7cc118e721b723aba48e71b952e5c6eMindy Pereira            editable.replace(start, end, chip);
2278e90bfad1a7cc118e721b723aba48e71b952e5c6eMindy Pereira        }
22795df0aa8368be2733caee5d57f5b20357b0f5d99dMindy Pereira        sanitizeBetween();
22800f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein
22810f2b174c6d8a19fb8ffbd71f742e11d3a04add9bAndrew Sapperstein        return end - start;
2282cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    }
2283cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
22843bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    private RecipientEntry createValidatedEntry(RecipientEntry item) {
22853bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        if (item == null) {
22863bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            return null;
22873bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        }
22883bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        final RecipientEntry entry;
22893bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        // If the display name and the address are the same, or if this is a
22903bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        // valid contact, but the destination is invalid, then make this a fake
22913bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        // recipient that is editable.
22923bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        String destination = item.getDestination();
22935e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        if (!isPhoneQuery() && item.getContactId() == RecipientEntry.GENERATED_CONTACT) {
22945e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp            entry = RecipientEntry.constructGeneratedEntry(item.getDisplayName(),
2295f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    destination, item.isValid());
22965e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        } else if (RecipientEntry.isCreatedRecipient(item.getContactId())
2297b4e244af14950aee7d612612d5406981315d3454Mindy Pereira                && (TextUtils.isEmpty(item.getDisplayName())
2298b4e244af14950aee7d612612d5406981315d3454Mindy Pereira                        || TextUtils.equals(item.getDisplayName(), destination)
2299b4e244af14950aee7d612612d5406981315d3454Mindy Pereira                        || (mValidator != null && !mValidator.isValid(destination)))) {
2300f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            entry = RecipientEntry.constructFakeEntry(destination, item.isValid());
23013bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        } else {
23023bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            entry = item;
23033bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        }
23043bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        return entry;
23053bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira    }
23063bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira
230701162ce6739af1c9d9870f8e7e489f805c7e6794Mindy Pereira    // Visible for testing.
2308194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    /* package */DrawableRecipientChip[] getSortedRecipients() {
2309194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip[] recips = getSpannable()
2310194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                .getSpans(0, getText().length(), DrawableRecipientChip.class);
2311194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        ArrayList<DrawableRecipientChip> recipientsList = new ArrayList<DrawableRecipientChip>(
2312194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                Arrays.asList(recips));
2313e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        final Spannable spannable = getSpannable();
2314194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        Collections.sort(recipientsList, new Comparator<DrawableRecipientChip>() {
2315e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira
2316e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira            @Override
2317194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            public int compare(DrawableRecipientChip first, DrawableRecipientChip second) {
2318e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                int firstStart = spannable.getSpanStart(first);
2319e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                int secondStart = spannable.getSpanStart(second);
2320e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                if (firstStart < secondStart) {
2321e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                    return -1;
2322e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                } else if (firstStart > secondStart) {
2323e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                    return 1;
2324e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                } else {
2325e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                    return 0;
2326e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                }
2327e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira            }
2328e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        });
2329194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        return recipientsList.toArray(new DrawableRecipientChip[recipientsList.size()]);
2330e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira    }
2331e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira
233212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    @Override
233312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
233412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        return false;
233512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
233612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
233712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    @Override
233812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    public void onDestroyActionMode(ActionMode mode) {
233912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
234012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
234112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    @Override
234212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
234312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        return false;
234412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
234512cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
2346a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
2347a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * No chips are selectable.
2348a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
234912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    @Override
235012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
235112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        return false;
235212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
235312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
2354d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    // Visible for testing.
23556c2a016fb00482339dc66b93822042f9f9e2424fJin Cao    /* package */ReplacementDrawableSpan getMoreChip() {
2356dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira        MoreImageSpan[] moreSpans = getSpannable().getSpans(0, getText().length(),
2357dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira                MoreImageSpan.class);
2358dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira        return moreSpans != null && moreSpans.length > 0 ? moreSpans[0] : null;
2359dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira    }
2360dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira
2361f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    private MoreImageSpan createMoreSpan(int count) {
2362f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        String moreText = String.format(mMoreItem.getText().toString(), count);
23636c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        mWorkPaint.set(getPaint());
23646c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        mWorkPaint.setTextSize(mMoreItem.getTextSize());
23656c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        mWorkPaint.setColor(mMoreItem.getCurrentTextColor());
23666c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        final int width = (int) mWorkPaint.measureText(moreText) + mMoreItem.getPaddingLeft()
2367f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                + mMoreItem.getPaddingRight();
23686c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        final int height = (int) mChipHeight;
2369f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        Bitmap drawable = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
2370f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        Canvas canvas = new Canvas(drawable);
2371f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        int adjustedHeight = height;
2372f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        Layout layout = getLayout();
2373f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        if (layout != null) {
2374f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            adjustedHeight -= layout.getLineDescent(0);
2375f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        }
23766c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        canvas.drawText(moreText, 0, moreText.length(), 0, adjustedHeight, mWorkPaint);
2377f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
2378f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        Drawable result = new BitmapDrawable(getResources(), drawable);
2379f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        result.setBounds(0, 0, width, height);
2380f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        return new MoreImageSpan(result);
2381f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    }
2382f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
2383f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    // Visible for testing.
2384f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    /*package*/ void createMoreChipPlainText() {
2385f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        // Take the first <= CHIP_LIMIT addresses and get to the end of the second one.
2386f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        Editable text = getText();
2387f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        int start = 0;
2388f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        int end = start;
2389f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        for (int i = 0; i < CHIP_LIMIT; i++) {
2390f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            end = movePastTerminators(mTokenizer.findTokenEnd(text, start));
2391f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            start = end; // move to the next token and get its end.
2392f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        }
2393f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        // Now, count total addresses.
2394f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        int tokenCount = countTokens(text);
2395f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        MoreImageSpan moreSpan = createMoreSpan(tokenCount - CHIP_LIMIT);
2396f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        SpannableString chipText = new SpannableString(text.subSequence(end, text.length()));
2397f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        chipText.setSpan(moreSpan, 0, chipText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
2398f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        text.replace(end, text.length(), chipText);
2399f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        mMoreChip = moreSpan;
2400f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    }
2401f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
2402f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    // Visible for testing.
2403f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    /* package */int countTokens(Editable text) {
2404f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        int tokenCount = 0;
2405f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        int start = 0;
2406f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        while (start < text.length()) {
2407f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            start = movePastTerminators(mTokenizer.findTokenEnd(text, start));
2408f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            tokenCount++;
2409f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            if (start >= text.length()) {
2410f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                break;
2411f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            }
2412f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        }
2413f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        return tokenCount;
2414f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira    }
2415f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
2416a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
2417342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira     * Create the more chip. The more chip is text that replaces any chips that
2418342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira     * do not fit in the pre-defined available space when the
2419342142087308bd9f5f7d682bf2ccd2420a3c7538Mindy Pereira     * RecipientEditTextView loses focus.
2420a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
2421d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    // Visible for testing.
2422d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    /* package */ void createMoreChip() {
24231163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz        if (mNoChipMode) {
2424f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            createMoreChipPlainText();
2425f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            return;
2426f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        }
2427f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
2428076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira        if (!mShouldShrink) {
2429076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira            return;
2430076d1f93a2613611f9f3aca8c8615ffea9b3e11cMindy Pereira        }
24316c2a016fb00482339dc66b93822042f9f9e2424fJin Cao        ReplacementDrawableSpan[] tempMore = getSpannable().getSpans(0, getText().length(),
24326c2a016fb00482339dc66b93822042f9f9e2424fJin Cao                MoreImageSpan.class);
2433e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        if (tempMore.length > 0) {
2434e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira            getSpannable().removeSpan(tempMore[0]);
2435e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        }
2436194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip[] recipients = getSortedRecipients();
2437f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira
2438d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira        if (recipients == null || recipients.length <= CHIP_LIMIT) {
24393bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            mMoreChip = null;
24403bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira            return;
244112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        }
2442e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        Spannable spannable = getSpannable();
2443d4c4e77389def134f5751853b838e9d5edb80be4Mindy Pereira        int numRecipients = recipients.length;
244412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        int overage = numRecipients - CHIP_LIMIT;
2445f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        MoreImageSpan moreSpan = createMoreSpan(overage);
24461a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz        mHiddenSpans = new ArrayList<DrawableRecipientChip>();
244712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        int totalReplaceStart = 0;
244812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        int totalReplaceEnd = 0;
2449e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        Editable text = getText();
245021cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira        for (int i = numRecipients - overage; i < recipients.length; i++) {
24511a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz            mHiddenSpans.add(recipients[i]);
2452364e7cf182a4124808d3a699dbea8dd1cc1d7bedMindy Pereira            if (i == numRecipients - overage) {
245321cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira                totalReplaceStart = spannable.getSpanStart(recipients[i]);
2454364e7cf182a4124808d3a699dbea8dd1cc1d7bedMindy Pereira            }
245521cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira            if (i == recipients.length - 1) {
245621cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira                totalReplaceEnd = spannable.getSpanEnd(recipients[i]);
2457364e7cf182a4124808d3a699dbea8dd1cc1d7bedMindy Pereira            }
2458e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira            if (mTemporaryRecipients == null || !mTemporaryRecipients.contains(recipients[i])) {
2459e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                int spanStart = spannable.getSpanStart(recipients[i]);
2460e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                int spanEnd = spannable.getSpanEnd(recipients[i]);
2461e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                recipients[i].setOriginalText(text.toString().substring(spanStart, spanEnd));
24621852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
246321cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira            spannable.removeSpan(recipients[i]);
246412cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        }
2465f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        if (totalReplaceEnd < text.length()) {
2466f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            totalReplaceEnd = text.length();
2467f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira        }
24681852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        int end = Math.max(totalReplaceStart, totalReplaceEnd);
24691852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        int start = Math.min(totalReplaceStart, totalReplaceEnd);
24701852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        SpannableString chipText = new SpannableString(text.subSequence(start, end));
247112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        chipText.setSpan(moreSpan, 0, chipText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
24721852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        text.replace(start, end, chipText);
24733bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        mMoreChip = moreSpan;
24745e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        // If adding the +more chip goes over the limit, resize accordingly.
24755e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        if (!isPhoneQuery() && getLineCount() > mMaxLines) {
24765e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp            setMaxLines(getLineCount());
24775e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        }
247812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
247912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
2480a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira    /**
2481a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * Replace the more chip, if it exists, with all of the recipient chips it had
2482a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     * replaced when the RecipientEditTextView gains focus.
2483a816690a8b3ecfeb537f6ad403a903f39205f738Mindy Pereira     */
2484d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    // Visible for testing.
2485d71925073f008dccd8c4b65d5d66534e6451e64dMindy Pereira    /*package*/ void removeMoreChip() {
248612cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        if (mMoreChip != null) {
248712cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            Spannable span = getSpannable();
248812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            span.removeSpan(mMoreChip);
248912cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            mMoreChip = null;
24901a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz            // Re-add the spans that were hidden.
24911a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz            if (mHiddenSpans != null && mHiddenSpans.size() > 0) {
24921a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz                // Recreate each hidden span.
2493194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                DrawableRecipientChip[] recipients = getSortedRecipients();
249454effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                // Start the search for tokens after the last currently visible
249554effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                // chip.
2496ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira                if (recipients == null || recipients.length == 0) {
2497ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira                    return;
2498ee48f7311ec169af7ed134cf5c9c5e16b243cf05Mindy Pereira                }
249954effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                int end = span.getSpanEnd(recipients[recipients.length - 1]);
250012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira                Editable editable = getText();
25011a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz                for (DrawableRecipientChip chip : mHiddenSpans) {
2502e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                    int chipStart;
25033bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                    int chipEnd;
25043bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira                    String token;
2505e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                    // Need to find the location of the chip, again.
2506e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira                    token = (String) chip.getOriginalText();
25071a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz                    // As we find the matching recipient for the hidden spans,
250854effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                    // reduce the size of the string we need to search.
250954effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                    // That way, if there are duplicates, we always find the correct
251054effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                    // recipient.
251154effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                    chipStart = editable.toString().indexOf(token, end);
251254effe95bd2f988e5d7472f17f4fafdc29fe616eMindy Pereira                    end = chipEnd = Math.min(editable.length(), chipStart + token.length());
25130d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira                    // Only set the span if we found a matching token.
25140d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira                    if (chipStart != -1) {
25150d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira                        editable.setSpan(chip, chipStart, chipEnd,
25160d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
25170d8b77a804fa34cd6fadc632067d562a69b7026aMindy Pereira                    }
251812cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira                }
25191a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz                mHiddenSpans.clear();
252012cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira            }
252112cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira        }
252212cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira    }
252312cf3fc6e24ffeb3b84ec5fbbaa7add81b25db09Mindy Pereira
2524cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira    /**
252595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * Show specified chip as selected. If the RecipientChip is just an email address,
252695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * selecting the chip will take the contents of the chip and place it at
252795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * the end of the RecipientEditTextView for inline editing. If the
252895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * RecipientChip is a complete contact, then selecting the chip
2529ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao     * will show a popup window with the address in use highlighted and any other
253095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * alternate addresses for the contact.
253195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * @param currentChip Chip to select.
2532cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira     */
2533ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    private void selectChip(DrawableRecipientChip currentChip) {
25345e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        if (shouldShowEditableText(currentChip)) {
253501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            CharSequence text = currentChip.getValue();
253601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            Editable editable = getText();
2537f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            Spannable spannable = getSpannable();
2538f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            int spanStart = spannable.getSpanStart(currentChip);
2539f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            int spanEnd = spannable.getSpanEnd(currentChip);
2540f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            spannable.removeSpan(currentChip);
254115a5ebc25941143d5fb65498ebc835d79351d3adJin Cao            // Don't need leading space if it's the only chip
254215a5ebc25941143d5fb65498ebc835d79351d3adJin Cao            if (spanEnd - spanStart == editable.length() - 1) {
254315a5ebc25941143d5fb65498ebc835d79351d3adJin Cao                spanEnd++;
254415a5ebc25941143d5fb65498ebc835d79351d3adJin Cao            }
2545f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            editable.delete(spanStart, spanEnd);
254601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            setCursorVisible(true);
254701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            setSelection(editable.length());
2548f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            editable.append(text);
2549ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            mSelectedChip = constructChipSpan(
2550ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    RecipientEntry.constructFakeEntry((String) text, isValid(text.toString())));
25515ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz
25525ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz            /*
25535ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz             * Because chip is destroyed and converted into an editable text, we call
25545ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz             * {@link RecipientChipDeletedListener#onRecipientChipDeleted}. For the cases where
25555ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz             * editable text is not shown (i.e. chip is in user's contact list), chip is focused
25565ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz             * and below callback is not called.
25575ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz             */
25581163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz            if (!mNoChipMode && mRecipientChipDeletedListener != null) {
25595ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz                mRecipientChipDeletedListener.onRecipientChipDeleted(currentChip.getEntry());
25605ae8a7a2389589bf9b139a019460405ab20ea5acEkin Oguz            }
25618af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        } else {
25628af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            final boolean showAddress =
25638af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                    currentChip.getContactId() == RecipientEntry.GENERATED_CONTACT ||
25648af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                    getAdapter().forceShowAddress();
25651163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz            if (showAddress && mNoChipMode) {
2566ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                return;
256781fd3d1ed9ea08706e297a227fcab10eac2cf0e3Mindy Pereira            }
2568a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde
2569a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde            if (isTouchExplorationEnabled()) {
2570a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde                // The chips cannot be touch-explored. However, doing a double-tap results in
2571a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde                // the popup being shown for the last chip, which is of no value.
2572a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde                return;
2573a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde            }
2574a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde
2575ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            mSelectedChip = currentChip;
2576ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            setSelection(getText().getSpanEnd(mSelectedChip));
2577ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            setCursorVisible(false);
2578ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao
25798af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            if (showAddress) {
2580ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                showAddress(currentChip, mAddressPopup);
258101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            } else {
2582ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                showAlternates(currentChip, mAlternatesPopup);
258301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            }
25840ab7e735e82c81baf9ab87d028611561ce0592b7Mindy Pereira        }
258595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
25860ab7e735e82c81baf9ab87d028611561ce0592b7Mindy Pereira
2587a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde    private boolean isTouchExplorationEnabled() {
2588a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
2589a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde            return false;
2590a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde        }
2591a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde
2592a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde        final AccessibilityManager accessibilityManager = (AccessibilityManager)
2593a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
2594a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde        return accessibilityManager.isTouchExplorationEnabled();
2595a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde    }
2596a6a68f4ea972fe37a9f963447b9b6cc6f7ddadc2Shri Borde
2597194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    private boolean shouldShowEditableText(DrawableRecipientChip currentChip) {
25985e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        long contactId = currentChip.getContactId();
25995e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp        return contactId == RecipientEntry.INVALID_CONTACT
26005e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT);
26015e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp    }
2602cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
26039b166209d723ed996243de5d89b489f3992daf0eJin Cao    private void showAddress(final DrawableRecipientChip currentChip, final ListPopupWindow popup) {
2604fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler        if (!mAttachedToWindow) {
2605fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler            return;
2606fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler        }
260701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        int line = getLayout().getLineForOffset(getChipStart(currentChip));
26086fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao        int bottomOffset = calculateOffsetFromBottomToTop(line);
260901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        // Align the alternates popup with the left side of the View,
261001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        // regardless of the position of the chip tapped.
26114de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao        popup.setAnchorView((mAlternatePopupAnchor != null) ? mAlternatePopupAnchor : this);
26126fc1fee597ebe3c30ebe5efebb4d6ea105641e0fJin Cao        popup.setVerticalOffset(bottomOffset);
261301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        popup.setAdapter(createSingleAddressAdapter(currentChip));
261401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        popup.setOnItemClickListener(new OnItemClickListener() {
261501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            @Override
261601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
261701382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira                unselectChip(currentChip);
261801382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira                popup.dismiss();
261901382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            }
262001382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        });
262101382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        popup.show();
262201382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        ListView listView = popup.getListView();
262301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
262401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira        listView.setItemChecked(0, true);
262501382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira    }
262601382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira
262795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    /**
262895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * Remove selection from this chip. Unselecting a RecipientChip will render
2629f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira     * the chip without a delete icon and with an unfocused background. This is
2630f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira     * called when the RecipientChip no longer has focus.
263195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     */
2632194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    private void unselectChip(DrawableRecipientChip chip) {
263395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        int start = getChipStart(chip);
263495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        int end = getChipEnd(chip);
263595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        Editable editable = getText();
2636e82e61f026fe8edfc12743fb038ddac9af5cf1efMindy Pereira        mSelectedChip = null;
263795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (start == -1 || end == -1) {
2638f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira            Log.w(TAG, "The chip doesn't exist or may be a chip a user was editing");
2639c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira            setSelection(editable.length());
2640c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira            commitDefault();
264195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        } else {
2642e82e61f026fe8edfc12743fb038ddac9af5cf1efMindy Pereira            getSpannable().removeSpan(chip);
264395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            QwertyKeyListener.markAsReplaced(editable, start, end, "");
264404da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira            editable.removeSpan(chip);
264504da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira            try {
26461163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz                if (!mNoChipMode) {
2647ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    editable.setSpan(constructChipSpan(chip.getEntry()),
26485e60c2b9b39b0aec39e3a03f9a178c2cb857b9d6mindyp                            start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
2649f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                }
265004da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira            } catch (NullPointerException e) {
265104da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira                Log.e(TAG, e.getMessage(), e);
265204da325c2112c4ef192276f1d7701cbef6635d1bMindy Pereira            }
2653cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
265495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        setCursorVisible(true);
265595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        setSelection(editable.length());
265695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (mAlternatesPopup != null && mAlternatesPopup.isShowing()) {
265795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            mAlternatesPopup.dismiss();
2658cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
265995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
2660cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
26614db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao    @Override
26624db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao    public void onChipDelete() {
26634db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        if (mSelectedChip != null) {
26641163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz            if (!mNoChipMode && mRecipientChipDeletedListener != null) {
2665339ffd585c0a033822b8a01d39834d6f5870b04aEkin Oguz                mRecipientChipDeletedListener.onRecipientChipDeleted(mSelectedChip.getEntry());
2666339ffd585c0a033822b8a01d39834d6f5870b04aEkin Oguz            }
26674db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao            removeChip(mSelectedChip);
26684db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao        }
2669568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao        dismissPopups();
2670568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao    }
2671568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao
2672f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    @Override
2673f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    public void onPermissionRequestDismissed() {
2674f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        if (mPermissionsRequestItemClickedListener != null) {
2675f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            mPermissionsRequestItemClickedListener.onPermissionRequestDismissed();
2676f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        }
2677f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        dismissDropDown();
2678f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    }
2679f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
2680568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao    private void dismissPopups() {
2681568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao        if (mAlternatesPopup != null && mAlternatesPopup.isShowing()) {
2682568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao            mAlternatesPopup.dismiss();
2683568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao        }
2684568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao        if (mAddressPopup != null && mAddressPopup.isShowing()) {
2685568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao            mAddressPopup.dismiss();
2686568fb229f9e17ab882cedaa9ee17c9f78a97ba9fJin Cao        }
2687ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        setSelection(getText().length());
26884db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao    }
26894db8cccf3332ad7c6fb1915f9f0f169953c3953aJin Cao
269095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    /**
269195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * Remove the chip and any text associated with it from the RecipientEditTextView.
269295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     */
269397cb25912dab282cf732757f68b0405ed005f00bMindy Pereira    // Visible for testing.
2694b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    /* package */void removeChip(DrawableRecipientChip chip) {
269595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        Spannable spannable = getSpannable();
269695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        int spanStart = spannable.getSpanStart(chip);
269795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        int spanEnd = spannable.getSpanEnd(chip);
269895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        Editable text = getText();
269962397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira        int toDelete = spanEnd;
270095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        boolean wasSelected = chip == mSelectedChip;
270195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        // Clear that there is a selected chip before updating any text.
270295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (wasSelected) {
270395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            mSelectedChip = null;
2704cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
270562397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira        // Always remove trailing spaces when removing a chip.
270662397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira        while (toDelete >= 0 && toDelete < text.length() && text.charAt(toDelete) == ' ') {
270762397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira            toDelete++;
270862397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira        }
270995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        spannable.removeSpan(chip);
2710f6d4bfce7cae3cc385396963dc8ea3eda90b917eMindy Pereira        if (spanStart >= 0 && toDelete > 0) {
2711f6d4bfce7cae3cc385396963dc8ea3eda90b917eMindy Pereira            text.delete(spanStart, toDelete);
2712f6d4bfce7cae3cc385396963dc8ea3eda90b917eMindy Pereira        }
271395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (wasSelected) {
271495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            clearSelectedChip();
2715cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
271695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
2717cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
271895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    /**
271995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * Replace this currently selected chip with a new chip
272095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * that uses the contact data provided.
272195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     */
2722aca23c4de8d85b04e6044c9a8f047c337cf427c9Mindy Pereira    // Visible for testing.
2723194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    /*package*/ void replaceChip(DrawableRecipientChip chip, RecipientEntry entry) {
272495a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        boolean wasSelected = chip == mSelectedChip;
272595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (wasSelected) {
272695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            mSelectedChip = null;
2727cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
272895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        int start = getChipStart(chip);
272995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        int end = getChipEnd(chip);
273095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        getSpannable().removeSpan(chip);
273195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        Editable editable = getText();
273211c15260cb6878a67eb5d958277fce33e641720fEkin Oguz        entry.setInReplacedChip(true);
2733ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        CharSequence chipText = createChip(entry);
27342d7709d276c03e536d37961076107af9f98522f5Mindy Pereira        if (chipText != null) {
27352d7709d276c03e536d37961076107af9f98522f5Mindy Pereira            if (start == -1 || end == -1) {
27362d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                Log.e(TAG, "The chip to replace does not exist but should.");
27372d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                editable.insert(0, chipText);
27382d7709d276c03e536d37961076107af9f98522f5Mindy Pereira            } else {
27392d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                if (!TextUtils.isEmpty(chipText)) {
27402d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                    // There may be a space to replace with this chip's new
2741b5afbc70a27f7534fd6dfa7440b3a3450bbc52cfmindyp                    // associated space. Check for it
27422d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                    int toReplace = end;
27432d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                    while (toReplace >= 0 && toReplace < editable.length()
27442d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                            && editable.charAt(toReplace) == ' ') {
27452d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                        toReplace++;
27462d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                    }
27472d7709d276c03e536d37961076107af9f98522f5Mindy Pereira                    editable.replace(start, toReplace, chipText);
274862397a5ac6b2df89ec4c2f8f23680add5850c17aMindy Pereira                }
27497ebb40ff05dbf28edd9bbed4eba7e57c8c6005aeMindy Pereira            }
2750cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
275195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        setCursorVisible(true);
275295a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (wasSelected) {
275395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira            clearSelectedChip();
2754cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
275595a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
2756cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
275795a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    /**
275895a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * Handle click events for a chip. When a selected chip receives a click
275995a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * event, see if that event was in the delete icon. If so, delete it.
276095a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     * Otherwise, unselect the chip.
276195a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira     */
2762a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao    public void onClick(DrawableRecipientChip chip) {
276395a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira        if (chip.isSelected()) {
2764a21fd887528a3b985f71decb2a19c917c2fcbd55Jin Cao            clearSelectedChip();
2765cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira        }
276695a692517516f6cdcd77dd13582e679dd59fcef7Mindy Pereira    }
2767cd61195b9be5614aefc4cda76c1732cc4840b18eMindy Pereira
276821cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira    private boolean chipsPending() {
27691a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz        return mPendingChipsCount > 0 || (mHiddenSpans != null && mHiddenSpans.size() > 0);
277021cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira    }
277121cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira
2772883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira    @Override
2773883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira    public void removeTextChangedListener(TextWatcher watcher) {
2774883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira        mTextWatcher = null;
2775883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira        super.removeTextChangedListener(watcher);
2776883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira    }
2777883c49973f4ea92a26cb6f40814d31f3bd389a89Mindy Pereira
2778b3b248acd845b017e952fb802e0976e82fc1c583Jin Cao    private boolean isValidEmailAddress(String input) {
2779b3b248acd845b017e952fb802e0976e82fc1c583Jin Cao        return !TextUtils.isEmpty(input) && mValidator != null &&
2780b3b248acd845b017e952fb802e0976e82fc1c583Jin Cao                mValidator.isValid(input);
2781b3b248acd845b017e952fb802e0976e82fc1c583Jin Cao    }
2782b3b248acd845b017e952fb802e0976e82fc1c583Jin Cao
278379fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    private class RecipientTextWatcher implements TextWatcher {
278472a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira
278579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        @Override
278679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        public void afterTextChanged(Editable s) {
278732366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira            // If the text has been set to null or empty, make sure we remove
278832366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira            // all the spans we applied.
278932366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira            if (TextUtils.isEmpty(s)) {
279032366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                // Remove all the chips spans.
279132366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                Spannable spannable = getSpannable();
2792194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                DrawableRecipientChip[] chips = spannable.getSpans(0, getText().length(),
2793194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                        DrawableRecipientChip.class);
2794194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                for (DrawableRecipientChip chip : chips) {
279532366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                    spannable.removeSpan(chip);
279632366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                }
279732366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                if (mMoreChip != null) {
279832366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                    spannable.removeSpan(mMoreChip);
279932366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                }
280041e93fbe82cd4d802e3f1fbe265038f7a0521dddKevin Lin                clearSelectedChip();
280132366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira                return;
280232366d47357b32ddd62efc03e50fc10819acd866Mindy Pereira            }
280301382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            // Get whether there are any recipients pending addition to the
280401382d764d2cbfbb4d2c1b554fe5c1ce1575dd26Mindy Pereira            // view. If there are, don't do anything in the text watcher.
280521cc0a2d9ce5c6ac5f90f51a59ccafacdb4cf7a5Mindy Pereira            if (chipsPending()) {
280679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira                return;
280779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            }
2808c0a34aba4889151d822dd1ac0ae8b722cf5edebbMindy Pereira            // If the user is editing a chip, don't clear it.
2809f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            if (mSelectedChip != null) {
2810f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                if (!isGeneratedContact(mSelectedChip)) {
2811f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    setCursorVisible(true);
2812f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    setSelection(getText().length());
2813f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    clearSelectedChip();
2814f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                } else {
2815f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    return;
2816f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                }
281779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            }
281879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            int length = s.length();
281979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            // Make sure there is content there to parse and that it is
2820ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira            // not just the commit character.
282179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            if (length > 1) {
2822f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                if (lastCharacterIsCommitCharacter(s)) {
2823f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    commitByCharacter();
2824f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                    return;
2825f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                }
2826ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira                char last;
282711a2adb53f764ffcadd262678ff782e6b4992decMindy Pereira                int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1;
2828ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira                int len = length() - 1;
2829ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira                if (end != len) {
2830ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira                    last = s.charAt(end);
2831ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira                } else {
2832ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira                    last = s.charAt(len);
2833ab5a9644d4ab95fe753a07b2bdf4202d78fa8af7Mindy Pereira                }
2834f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                if (last == COMMIT_CHAR_SPACE) {
28354afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                    if (!isPhoneQuery()) {
28364afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                        // Check if this is a valid email address. If it is,
28374afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                        // commit it.
28384afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                        String text = getText().toString();
28394afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                        int tokenStart = mTokenizer.findTokenStart(text, getSelectionEnd());
28404afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                        String sub = text.substring(tokenStart, mTokenizer.findTokenEnd(text,
28414afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                                tokenStart));
2842b3b248acd845b017e952fb802e0976e82fc1c583Jin Cao                        if (isValidEmailAddress(sub)) {
28434afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                            commitByCharacter();
28444afc73e1e15f7a7fdf608302b9b8488b7a4206f8Tom Taylor                        }
284579fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira                    }
284679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira                }
284779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira            }
284879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        }
284979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira
285079fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        @Override
285179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        public void onTextChanged(CharSequence s, int start, int before, int count) {
2852f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            // The user deleted some text OR some text was replaced; check to
2853f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            // see if the insertion point is on a space
285472a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira            // following a chip.
2855f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            if (before - count == 1) {
285672a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                // If the item deleted is a space, and the thing before the
285772a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                // space is a chip, delete the entire span.
285872a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                int selStart = getSelectionStart();
2859194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                DrawableRecipientChip[] repl = getSpannable().getSpans(selStart, selStart,
2860194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                        DrawableRecipientChip.class);
286172a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                if (repl.length > 0) {
286272a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                    // There is a chip there! Just remove it.
2863ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    DrawableRecipientChip toDelete = repl[0];
286472a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                    Editable editable = getText();
286572a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                    // Add the separator token.
2866ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    int deleteStart = editable.getSpanStart(toDelete);
2867ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    int deleteEnd = editable.getSpanEnd(toDelete) + 1;
2868ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    if (deleteEnd > editable.length()) {
2869ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                        deleteEnd = editable.length();
287062fce9316c48a188d89c31d7e616f755bd7cf14cMindy Pereira                    }
28711163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz                    if (!mNoChipMode && mRecipientChipDeletedListener != null) {
2872c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko                        mRecipientChipDeletedListener.onRecipientChipDeleted(toDelete.getEntry());
2873c4da98ae5163692b343b3f4c034a79840b1f3995Borys Boyko                    }
2874ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    editable.removeSpan(toDelete);
2875ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                    editable.delete(deleteStart, deleteEnd);
287672a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira                }
28774e7d20f7b74ddce5a104138714c9683c8bd2300dMindy Pereira            } else if (count > before) {
28787e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp                if (mSelectedChip != null
28797e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp                    && isGeneratedContact(mSelectedChip)) {
28807e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp                    if (lastCharacterIsCommitCharacter(s)) {
28817e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp                        commitByCharacter();
28827e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp                        return;
28837e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp                    }
28847e10c86c8bead949f2f4694e97d7844c6aef9e08mindyp                }
288572a2339009029d3217a0ff39f5a92523e8fb3d41Mindy Pereira            }
288679fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        }
288779fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira
288879fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        @Override
288979fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
2890dc04cd76d8992ad3df98dce9be5c5b6540ab3619Mindy Pereira            // Do nothing.
289179fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira        }
289279fb4360e567c5ce9e80142af2807126d3bb67f0Mindy Pereira    }
28931852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
2894f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    public boolean lastCharacterIsCommitCharacter(CharSequence s) {
2895f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        char last;
2896f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1;
2897f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        int len = length() - 1;
2898f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        if (end != len) {
2899f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            last = s.charAt(end);
2900f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        } else {
2901f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            last = s.charAt(len);
2902f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        }
2903f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        return last == COMMIT_CHAR_COMMA || last == COMMIT_CHAR_SEMICOLON;
2904f30a42800318f6790d55421f8f6980eb38db4d3cmindyp    }
2905f30a42800318f6790d55421f8f6980eb38db4d3cmindyp
2906194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    public boolean isGeneratedContact(DrawableRecipientChip chip) {
2907f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        long contactId = chip.getContactId();
2908f30a42800318f6790d55421f8f6980eb38db4d3cmindyp        return contactId == RecipientEntry.INVALID_CONTACT
2909f30a42800318f6790d55421f8f6980eb38db4d3cmindyp                || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT);
2910f30a42800318f6790d55421f8f6980eb38db4d3cmindyp    }
2911f30a42800318f6790d55421f8f6980eb38db4d3cmindyp
2912e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    /**
2913e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     * Handles pasting a {@link ClipData} to this {@link RecipientEditTextView}.
2914e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     */
2915219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps    // Visible for testing.
2916219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps    void handlePasteClip(ClipData clip) {
2917219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps        if (clip == null) {
2918219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            // Do nothing.
2919219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            return;
2920219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps        }
2921219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps
2922219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps        final ClipDescription clipDesc = clip.getDescription();
2923e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        boolean containsSupportedType = clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
2924e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                || clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML);
2925219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps        if (!containsSupportedType) {
2926219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            return;
2927219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps        }
2928219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps
2929e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        removeTextChangedListener(mTextWatcher);
2930e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
2931219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps        final ClipDescription clipDescription = clip.getDescription();
2932219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps        for (int i = 0; i < clip.getItemCount(); i++) {
2933219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            final String mimeType = clipDescription.getMimeType(i);
2934e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam            final boolean supportedType = ClipDescription.MIMETYPE_TEXT_PLAIN.equals(mimeType)
2935e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam                    || ClipDescription.MIMETYPE_TEXT_HTML.equals(mimeType);
2936219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            if (!supportedType) {
2937219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                // Only plain text and html can be pasted.
2938219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                continue;
2939219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            }
2940219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps
2941219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            final CharSequence pastedItem = clip.getItemAt(i).getText();
2942219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps            if (!TextUtils.isEmpty(pastedItem)) {
2943219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                final Editable editable = getText();
2944219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                final int start = getSelectionStart();
2945219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                final int end = getSelectionEnd();
2946219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                if (start < 0 || end < 1) {
2947219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                    // No selection.
2948219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                    editable.append(pastedItem);
2949219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                } else if (start == end) {
2950219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                    // Insert at position.
2951219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                    editable.insert(start, pastedItem);
2952219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                } else {
2953219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                    editable.append(pastedItem, start, end);
2954e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                }
2955219cbfbd7806b17ce10ba2cfb3f0cd45d167d46cRégis Décamps                handlePasteAndReplace();
2956e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            }
2957e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        }
2958e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
2959e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        mHandler.post(mAddTextWatcher);
2960e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    }
2961e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
296220c9d620e79ae28994856541761a951074551518Mindy Pereira    @Override
296320c9d620e79ae28994856541761a951074551518Mindy Pereira    public boolean onTextContextMenuItem(int id) {
296420c9d620e79ae28994856541761a951074551518Mindy Pereira        if (id == android.R.id.paste) {
296520c9d620e79ae28994856541761a951074551518Mindy Pereira            ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(
296620c9d620e79ae28994856541761a951074551518Mindy Pereira                    Context.CLIPBOARD_SERVICE);
2967e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            handlePasteClip(clipboard.getPrimaryClip());
296820c9d620e79ae28994856541761a951074551518Mindy Pereira            return true;
296920c9d620e79ae28994856541761a951074551518Mindy Pereira        }
297020c9d620e79ae28994856541761a951074551518Mindy Pereira        return super.onTextContextMenuItem(id);
297120c9d620e79ae28994856541761a951074551518Mindy Pereira    }
297220c9d620e79ae28994856541761a951074551518Mindy Pereira
2973b4e244af14950aee7d612612d5406981315d3454Mindy Pereira    private void handlePasteAndReplace() {
2974194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        ArrayList<DrawableRecipientChip> created = handlePaste();
2975b4e244af14950aee7d612612d5406981315d3454Mindy Pereira        if (created != null && created.size() > 0) {
2976b4e244af14950aee7d612612d5406981315d3454Mindy Pereira            // Perform reverse lookups on the pasted contacts.
2977b4e244af14950aee7d612612d5406981315d3454Mindy Pereira            IndividualReplacementTask replace = new IndividualReplacementTask();
2978b4e244af14950aee7d612612d5406981315d3454Mindy Pereira            replace.execute(created);
2979b4e244af14950aee7d612612d5406981315d3454Mindy Pereira        }
2980b4e244af14950aee7d612612d5406981315d3454Mindy Pereira    }
2981b4e244af14950aee7d612612d5406981315d3454Mindy Pereira
298220c9d620e79ae28994856541761a951074551518Mindy Pereira    // Visible for testing.
2983194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    /* package */ArrayList<DrawableRecipientChip> handlePaste() {
298420c9d620e79ae28994856541761a951074551518Mindy Pereira        String text = getText().toString();
298520c9d620e79ae28994856541761a951074551518Mindy Pereira        int originalTokenStart = mTokenizer.findTokenStart(text, getSelectionEnd());
298620c9d620e79ae28994856541761a951074551518Mindy Pereira        String lastAddress = text.substring(originalTokenStart);
298720c9d620e79ae28994856541761a951074551518Mindy Pereira        int tokenStart = originalTokenStart;
29880cae85010120a2a8490baf3488ed0bd2293e1d4djli        int prevTokenStart = 0;
2989194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip findChip = null;
2990194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        ArrayList<DrawableRecipientChip> created = new ArrayList<DrawableRecipientChip>();
299120c9d620e79ae28994856541761a951074551518Mindy Pereira        if (tokenStart != 0) {
299220c9d620e79ae28994856541761a951074551518Mindy Pereira            // There are things before this!
29930cae85010120a2a8490baf3488ed0bd2293e1d4djli            while (tokenStart != 0 && findChip == null && tokenStart != prevTokenStart) {
299420c9d620e79ae28994856541761a951074551518Mindy Pereira                prevTokenStart = tokenStart;
299520c9d620e79ae28994856541761a951074551518Mindy Pereira                tokenStart = mTokenizer.findTokenStart(text, tokenStart);
299620c9d620e79ae28994856541761a951074551518Mindy Pereira                findChip = findChip(tokenStart);
2997fda0170c661e237c202a8519baba1ce3294c6afeTom Taylor                if (tokenStart == originalTokenStart && findChip == null) {
2998fda0170c661e237c202a8519baba1ce3294c6afeTom Taylor                    break;
2999fda0170c661e237c202a8519baba1ce3294c6afeTom Taylor                }
300020c9d620e79ae28994856541761a951074551518Mindy Pereira            }
300120c9d620e79ae28994856541761a951074551518Mindy Pereira            if (tokenStart != originalTokenStart) {
300220c9d620e79ae28994856541761a951074551518Mindy Pereira                if (findChip != null) {
300320c9d620e79ae28994856541761a951074551518Mindy Pereira                    tokenStart = prevTokenStart;
300420c9d620e79ae28994856541761a951074551518Mindy Pereira                }
300520c9d620e79ae28994856541761a951074551518Mindy Pereira                int tokenEnd;
3006194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                DrawableRecipientChip createdChip;
300720c9d620e79ae28994856541761a951074551518Mindy Pereira                while (tokenStart < originalTokenStart) {
3008b5afbc70a27f7534fd6dfa7440b3a3450bbc52cfmindyp                    tokenEnd = movePastTerminators(mTokenizer.findTokenEnd(getText().toString(),
3009b5afbc70a27f7534fd6dfa7440b3a3450bbc52cfmindyp                            tokenStart));
301020c9d620e79ae28994856541761a951074551518Mindy Pereira                    commitChip(tokenStart, tokenEnd, getText());
301120c9d620e79ae28994856541761a951074551518Mindy Pereira                    createdChip = findChip(tokenStart);
3012c52ea8e900948bf42ed63b2d3d6c9afbd70d9df0Mindy Pereira                    if (createdChip == null) {
3013c52ea8e900948bf42ed63b2d3d6c9afbd70d9df0Mindy Pereira                        break;
3014c52ea8e900948bf42ed63b2d3d6c9afbd70d9df0Mindy Pereira                    }
301520c9d620e79ae28994856541761a951074551518Mindy Pereira                    // +1 for the space at the end.
301620c9d620e79ae28994856541761a951074551518Mindy Pereira                    tokenStart = getSpannable().getSpanEnd(createdChip) + 1;
3017b4e244af14950aee7d612612d5406981315d3454Mindy Pereira                    created.add(createdChip);
301820c9d620e79ae28994856541761a951074551518Mindy Pereira                }
301920c9d620e79ae28994856541761a951074551518Mindy Pereira            }
302020c9d620e79ae28994856541761a951074551518Mindy Pereira        }
302120c9d620e79ae28994856541761a951074551518Mindy Pereira        // Take a look at the last token. If the token has been completed with a
302220c9d620e79ae28994856541761a951074551518Mindy Pereira        // commit character, create a chip.
302320c9d620e79ae28994856541761a951074551518Mindy Pereira        if (isCompletedToken(lastAddress)) {
302420c9d620e79ae28994856541761a951074551518Mindy Pereira            Editable editable = getText();
3025b4e244af14950aee7d612612d5406981315d3454Mindy Pereira            tokenStart = editable.toString().indexOf(lastAddress, originalTokenStart);
3026b4e244af14950aee7d612612d5406981315d3454Mindy Pereira            commitChip(tokenStart, editable.length(), editable);
3027b4e244af14950aee7d612612d5406981315d3454Mindy Pereira            created.add(findChip(tokenStart));
302820c9d620e79ae28994856541761a951074551518Mindy Pereira        }
3029b4e244af14950aee7d612612d5406981315d3454Mindy Pereira        return created;
303020c9d620e79ae28994856541761a951074551518Mindy Pereira    }
303120c9d620e79ae28994856541761a951074551518Mindy Pereira
303220c9d620e79ae28994856541761a951074551518Mindy Pereira    // Visible for testing.
303320c9d620e79ae28994856541761a951074551518Mindy Pereira    /* package */int movePastTerminators(int tokenEnd) {
303420c9d620e79ae28994856541761a951074551518Mindy Pereira        if (tokenEnd >= length()) {
303520c9d620e79ae28994856541761a951074551518Mindy Pereira            return tokenEnd;
303620c9d620e79ae28994856541761a951074551518Mindy Pereira        }
303720c9d620e79ae28994856541761a951074551518Mindy Pereira        char atEnd = getText().toString().charAt(tokenEnd);
303820c9d620e79ae28994856541761a951074551518Mindy Pereira        if (atEnd == COMMIT_CHAR_COMMA || atEnd == COMMIT_CHAR_SEMICOLON) {
303920c9d620e79ae28994856541761a951074551518Mindy Pereira            tokenEnd++;
304020c9d620e79ae28994856541761a951074551518Mindy Pereira        }
304120c9d620e79ae28994856541761a951074551518Mindy Pereira        // This token had not only an end token character, but also a space
304220c9d620e79ae28994856541761a951074551518Mindy Pereira        // separating it from the next token.
304320c9d620e79ae28994856541761a951074551518Mindy Pereira        if (tokenEnd < length() && getText().toString().charAt(tokenEnd) == ' ') {
304420c9d620e79ae28994856541761a951074551518Mindy Pereira            tokenEnd++;
304520c9d620e79ae28994856541761a951074551518Mindy Pereira        }
304620c9d620e79ae28994856541761a951074551518Mindy Pereira        return tokenEnd;
304720c9d620e79ae28994856541761a951074551518Mindy Pereira    }
304820c9d620e79ae28994856541761a951074551518Mindy Pereira
30491852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira    private class RecipientReplacementTask extends AsyncTask<Void, Void, Void> {
3050194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        private DrawableRecipientChip createFreeChip(RecipientEntry entry) {
30511852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            try {
30521163a27340e6b18ef630fee4dd07c74c579c697cEkin Oguz                if (mNoChipMode) {
3053f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                    return null;
3054f6519d2aabaac51fd62dc819c109db86748d79e8Mindy Pereira                }
3055ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                return constructChipSpan(entry);
30561852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            } catch (NullPointerException e) {
30571852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                Log.e(TAG, e.getMessage(), e);
30581852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                return null;
30591852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
30601852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        }
30611852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
30621852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        @Override
306378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy        protected void onPreExecute() {
306478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy            // Ensure everything is in chip-form already, so we don't have text that slowly gets
306578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy            // replaced
3066194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            final List<DrawableRecipientChip> originalRecipients =
3067194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                    new ArrayList<DrawableRecipientChip>();
3068194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            final DrawableRecipientChip[] existingChips = getSortedRecipients();
3069ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            Collections.addAll(originalRecipients, existingChips);
30701a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz            if (mHiddenSpans != null) {
30711a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz                originalRecipients.addAll(mHiddenSpans);
307278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy            }
307378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
3074194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            final List<DrawableRecipientChip> replacements =
3075194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                    new ArrayList<DrawableRecipientChip>(originalRecipients.size());
307678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
3077194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            for (final DrawableRecipientChip chip : originalRecipients) {
307878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                if (RecipientEntry.isCreatedRecipient(chip.getEntry().getContactId())
307978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                        && getSpannable().getSpanStart(chip) != -1) {
308078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                    replacements.add(createFreeChip(chip.getEntry()));
308178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                } else {
308278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                    replacements.add(null);
308378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                }
308478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy            }
308578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
308678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy            processReplacements(originalRecipients, replacements);
308778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy        }
308878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
308978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy        @Override
30901852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        protected Void doInBackground(Void... params) {
30911852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            if (mIndividualReplacements != null) {
30921852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                mIndividualReplacements.cancel(true);
30931852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
30941852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            // For each chip in the list, look up the matching contact.
30951852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            // If there is a match, replace that chip with the matching
30961852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            // chip.
3097194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            final ArrayList<DrawableRecipientChip> recipients =
3098194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                    new ArrayList<DrawableRecipientChip>();
3099194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            DrawableRecipientChip[] existingChips = getSortedRecipients();
3100ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            Collections.addAll(recipients, existingChips);
31011a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz            if (mHiddenSpans != null) {
31021a68eb2ed0497378aa39080e3ba54ddf5eff7214Ekin Oguz                recipients.addAll(mHiddenSpans);
31031852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
310403cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira            ArrayList<String> addresses = new ArrayList<String>();
3105ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            for (DrawableRecipientChip chip : recipients) {
3106f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira                if (chip != null) {
310703cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira                    addresses.add(createAddressText(chip.getEntry()));
3108f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira                }
31091852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
31107a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final BaseRecipientAdapter adapter = getAdapter();
31118af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            adapter.getMatchingRecipients(addresses, new RecipientMatchCallback() {
311216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        @Override
311394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                        public void matchesFound(Map<String, RecipientEntry> entries) {
3114194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                            final ArrayList<DrawableRecipientChip> replacements =
3115194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                                    new ArrayList<DrawableRecipientChip>();
3116194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                            for (final DrawableRecipientChip temp : recipients) {
311716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                RecipientEntry entry = null;
3118f0579ee4ab41c021f20f78b25ecf4c526b308ec6Alice Yang                                if (temp != null && RecipientEntry.isCreatedRecipient(
3119f0579ee4ab41c021f20f78b25ecf4c526b308ec6Alice Yang                                        temp.getEntry().getContactId())
312016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                        && getSpannable().getSpanStart(temp) != -1) {
312116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                    // Replace this.
312216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                    entry = createValidatedEntry(
312316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                            entries.get(tokenizeAddress(temp.getEntry()
312416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                                    .getDestination())));
312516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                }
312616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                if (entry != null) {
312716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                    replacements.add(createFreeChip(entry));
312816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                } else {
312916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                    replacements.add(null);
313016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                }
313116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                            }
313278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                            processReplacements(recipients, replacements);
31331852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                        }
313494fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
313594fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                        @Override
3136f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                        public void matchesNotFound(final Set<String> unfoundAddresses) {
3137194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                            final List<DrawableRecipientChip> replacements =
3138194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                                    new ArrayList<DrawableRecipientChip>(unfoundAddresses.size());
313994fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
3140194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                            for (final DrawableRecipientChip temp : recipients) {
3141ad8781e6062f086aa4160eab2799b6848911aa3dAlice Yang                                if (temp != null && RecipientEntry.isCreatedRecipient(
3142ad8781e6062f086aa4160eab2799b6848911aa3dAlice Yang                                        temp.getEntry().getContactId())
314394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                        && getSpannable().getSpanStart(temp) != -1) {
3144f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                                    if (unfoundAddresses.contains(
3145f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                                            temp.getEntry().getDestination())) {
314694fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                        replacements.add(createFreeChip(temp.getEntry()));
314794fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                    } else {
314894fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                        replacements.add(null);
314994fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                    }
315094fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                } else {
315194fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                    replacements.add(null);
315294fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                                }
315394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                            }
315494fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
315578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                            processReplacements(recipients, replacements);
315694fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                        }
315716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    });
31581852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            return null;
31591852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        }
316078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
3161194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        private void processReplacements(final List<DrawableRecipientChip> recipients,
3162194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                final List<DrawableRecipientChip> replacements) {
316378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy            if (replacements != null && replacements.size() > 0) {
316478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                final Runnable runnable = new Runnable() {
316578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                    @Override
316678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                    public void run() {
31670e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                        final Editable text = new SpannableStringBuilder(getText());
316878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                        int i = 0;
31690e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                        for (final DrawableRecipientChip chip : recipients) {
31700e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                            final DrawableRecipientChip replacement = replacements.get(i);
317178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                            if (replacement != null) {
317278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                final RecipientEntry oldEntry = chip.getEntry();
317378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                final RecipientEntry newEntry = replacement.getEntry();
317478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                final boolean isBetter =
317578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        RecipientAlternatesAdapter.getBetterRecipient(
317678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                                oldEntry, newEntry) == newEntry;
317778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
317878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                if (isBetter) {
317978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                    // Find the location of the chip in the text currently shown.
31800e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                                    final int start = text.getSpanStart(chip);
318178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                    if (start != -1) {
318278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        // Replacing the entirety of what the chip represented,
318378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        // including the extra space dividing it from other chips.
3184b023f69b1f35199bc17f1d2708acb492d3ae49b0Paul Westbrook                                        final int end =
3185b023f69b1f35199bc17f1d2708acb492d3ae49b0Paul Westbrook                                                Math.min(text.getSpanEnd(chip) + 1, text.length());
31860e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                                        text.removeSpan(chip);
318778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        // Make sure we always have just 1 space at the end to
318878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        // separate this chip from the next chip.
31890e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                                        final SpannableString displayText =
319078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                                new SpannableString(createAddressText(
31910e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                                                        replacement.getEntry()).trim() + " ");
319278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        displayText.setSpan(replacement, 0,
319378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                                displayText.length() - 1,
319478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
319578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        // Replace the old text we found with with the new display
319678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        // text, which now may also contain the display name of the
319778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        // recipient.
31980e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                                        text.replace(start, end, displayText);
319978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        replacement.setOriginalText(displayText.toString());
320078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        replacements.set(i, null);
320178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
320278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                        recipients.set(i, replacement);
320378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                    }
320478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                                }
320578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                            }
320678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                            i++;
320778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                        }
32080e128968f242866568bba0f833bb17ffda127f07Scott Kennedy                        setText(text);
320978f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                    }
321078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                };
321178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy
321278f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                if (Looper.myLooper() == Looper.getMainLooper()) {
321378f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                    runnable.run();
321478f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                } else {
321578f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                    mHandler.post(runnable);
321678f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy                }
321778f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy            }
321878f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy        }
32191852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira    }
32201852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
322178f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy    private class IndividualReplacementTask
3222194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            extends AsyncTask<ArrayList<DrawableRecipientChip>, Void, Void> {
32231852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        @Override
3224194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        protected Void doInBackground(ArrayList<DrawableRecipientChip>... params) {
32251852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            // For each chip in the list, look up the matching contact.
32261852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            // If there is a match, replace that chip with the matching
32271852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            // chip.
3228194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy            final ArrayList<DrawableRecipientChip> originalRecipients = params[0];
322903cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira            ArrayList<String> addresses = new ArrayList<String>();
3230ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao            for (DrawableRecipientChip chip : originalRecipients) {
3231f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira                if (chip != null) {
323203cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira                    addresses.add(createAddressText(chip.getEntry()));
3233f07e8b2db488aa308f0ca2916dd53cbd1b898c6fMindy Pereira                }
32341852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
32357a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final BaseRecipientAdapter adapter = getAdapter();
32368af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            adapter.getMatchingRecipients(addresses, new RecipientMatchCallback() {
323716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
323816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        @Override
323994fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                        public void matchesFound(Map<String, RecipientEntry> entries) {
3240194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy                            for (final DrawableRecipientChip temp : originalRecipients) {
324116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                if (RecipientEntry.isCreatedRecipient(temp.getEntry()
324216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                        .getContactId())
324316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                        && getSpannable().getSpanStart(temp) != -1) {
324416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                    // Replace this.
324576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                                    final RecipientEntry entry = createValidatedEntry(entries
324616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                            .get(tokenizeAddress(temp.getEntry().getDestination())
324716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                                    .toLowerCase()));
324876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                                    if (entry != null) {
324916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                        mHandler.post(new Runnable() {
325016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                            @Override
325116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                            public void run() {
325276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                                                replaceChip(temp, entry);
325316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                            }
325416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                        });
325516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                    }
325616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                                }
32571852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                            }
325816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        }
325916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
326094fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                        @Override
3261f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                        public void matchesNotFound(final Set<String> unfoundAddresses) {
326294fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                            // No action required
326394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy                        }
326416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    });
32651852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            return null;
32661852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        }
32671852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira    }
32681d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
3269e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira
3270e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira    /**
3271e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira     * MoreImageSpan is a simple class created for tracking the existence of a
3272e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira     * more chip across activity restarts/
3273e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira     */
32746c2a016fb00482339dc66b93822042f9f9e2424fJin Cao    private class MoreImageSpan extends ReplacementDrawableSpan {
3275e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        public MoreImageSpan(Drawable b) {
3276e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira            super(b);
32776c2a016fb00482339dc66b93822042f9f9e2424fJin Cao            setExtraMargin(mLineSpacingExtra);
3278e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira        }
3279e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira    }
3280e1bfc9218326c02ff51985dcb22ee9b0ae385807Mindy Pereira
32811d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    @Override
32821d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    public boolean onDown(MotionEvent e) {
32831d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        return false;
32841d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    }
32851d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
32861d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    @Override
32871d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
32881d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        // Do nothing.
32891d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        return false;
32901d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    }
32911d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
32921d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    @Override
32931d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    public void onLongPress(MotionEvent event) {
32941d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        if (mSelectedChip != null) {
32951d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira            return;
32961d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        }
32971d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        float x = event.getX();
32981d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        float y = event.getY();
32991650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        final int offset = putOffsetInRange(x, y);
3300194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        DrawableRecipientChip currentChip = findChip(offset);
33011d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        if (currentChip != null) {
3302e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            if (mDragEnabled) {
3303e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                // Start drag-and-drop for the selected chip.
3304e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                startDrag(currentChip);
3305e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            } else {
3306e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                // Copy the selected chip email address.
3307e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                showCopyDialog(currentChip.getEntry().getDestination());
3308e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            }
3309e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        }
3310e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    }
3311e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
33121650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    // The following methods are used to provide some functionality on older versions of Android
33131650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    // These methods were copied out of JB MR2's TextView
33141650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    /////////////////////////////////////////////////
33151650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    private int supportGetOffsetForPosition(float x, float y) {
33161650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        if (getLayout() == null) return -1;
33171650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        final int line = supportGetLineAtCoordinate(y);
3318ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        return supportGetOffsetAtCoordinate(line, x);
33191650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    }
33201650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy
33211650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    private float supportConvertToLocalHorizontalCoordinate(float x) {
33221650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        x -= getTotalPaddingLeft();
33231650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        // Clamp the position to inside of the view.
33241650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        x = Math.max(0.0f, x);
33251650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
33261650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        x += getScrollX();
33271650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        return x;
33281650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    }
33291650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy
33301650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    private int supportGetLineAtCoordinate(float y) {
33311650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        y -= getTotalPaddingLeft();
33321650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        // Clamp the position to inside of the view.
33331650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        y = Math.max(0.0f, y);
33341650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
33351650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        y += getScrollY();
33361650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        return getLayout().getLineForVertical((int) y);
33371650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    }
33381650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy
33391650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    private int supportGetOffsetAtCoordinate(int line, float x) {
33401650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        x = supportConvertToLocalHorizontalCoordinate(x);
33411650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy        return getLayout().getOffsetForHorizontal(line, x);
33421650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    }
33431650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy    /////////////////////////////////////////////////
33441650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedy
3345e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    /**
3346e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     * Enables drag-and-drop for chips.
3347e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     */
3348e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    public void enableDrag() {
3349e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        mDragEnabled = true;
3350e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    }
3351e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3352e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    /**
3353e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     * Starts drag-and-drop for the selected chip.
3354e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     */
3355194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy    private void startDrag(DrawableRecipientChip currentChip) {
3356e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        String address = currentChip.getEntry().getDestination();
3357e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        ClipData data = ClipData.newPlainText(address, address + COMMIT_CHAR_COMMA);
3358e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3359e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        // Start drag mode.
3360e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        startDrag(data, new RecipientChipShadow(currentChip), null, 0);
3361e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3362e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        // Remove the current chip, so drag-and-drop will result in a move.
3363e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        // TODO (phamm): consider readd this chip if it's dropped outside a target.
3364e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        removeChip(currentChip);
3365e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    }
3366e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3367e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    /**
3368e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     * Handles drag event.
3369e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     */
3370e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    @Override
3371ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao    public boolean onDragEvent(@NonNull DragEvent event) {
3372e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        switch (event.getAction()) {
3373e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            case DragEvent.ACTION_DRAG_STARTED:
3374e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                // Only handle plain text drag and drop.
3375e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                return event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
3376e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            case DragEvent.ACTION_DRAG_ENTERED:
3377e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                requestFocus();
3378e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                return true;
3379e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            case DragEvent.ACTION_DROP:
3380e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                handlePasteClip(event.getClipData());
3381e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham                return true;
3382e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        }
3383e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        return false;
3384e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    }
3385e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3386e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    /**
33871db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein     * Drag shadow for a {@link DrawableRecipientChip}.
3388e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham     */
3389e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham    private final class RecipientChipShadow extends DragShadowBuilder {
3390194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        private final DrawableRecipientChip mChip;
3391e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3392194d427ebcfc2133bda410e0e4c399250d9a6066Scott Kennedy        public RecipientChipShadow(DrawableRecipientChip chip) {
3393e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            mChip = chip;
3394e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        }
3395e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3396e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        @Override
3397ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        public void onProvideShadowMetrics(@NonNull Point shadowSize,
3398ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao                @NonNull Point shadowTouchPoint) {
3399f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            Rect rect = mChip.getBounds();
3400e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            shadowSize.set(rect.width(), rect.height());
3401e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham            shadowTouchPoint.set(rect.centerX(), rect.centerY());
3402e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        }
3403e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham
3404e51c7226dfb6420b0d4c1a7bfdf3217ee4e560a6Minh Pham        @Override
3405ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        public void onDrawShadow(@NonNull Canvas canvas) {
3406f30a42800318f6790d55421f8f6980eb38db4d3cmindyp            mChip.draw(canvas);
34071d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        }
34081d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    }
34091d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
34101d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    private void showCopyDialog(final String address) {
3411c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sapperstein        final Context context = getContext();
3412c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sapperstein        if (!mAttachedToWindow || context == null || !(context instanceof Activity)) {
3413fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler            return;
3414fbeca0eb27de966dcac8a48d1d0e888e565bf255Tony Mantler        }
3415c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sapperstein
3416c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sapperstein        final DialogFragment fragment = CopyDialog.newInstance(address);
3417c6e314b5a20a234a8207ef4cd67e2f042579a087Andrew Sapperstein        fragment.show(((Activity) context).getFragmentManager(), CopyDialog.TAG);
34181d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    }
34191d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
34201d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    @Override
34211d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
34221d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        // Do nothing.
34231d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        return false;
34241d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    }
34251d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
34261d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    @Override
34271d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    public void onShowPress(MotionEvent e) {
34281d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        // Do nothing.
34291d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    }
34301d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
34311d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    @Override
34321d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    public boolean onSingleTapUp(MotionEvent e) {
34331d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        // Do nothing.
34341d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira        return false;
34351d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira    }
34361d1ec857e5d050163cb87936e233b5f9cf321344Mindy Pereira
3437c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor    protected boolean isPhoneQuery() {
3438ca5227122474fb124a4fe573a2f07e14d7a33f3eMindy Pereira        return getAdapter() != null
3439858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy                && getAdapter().getQueryType() == BaseRecipientAdapter.QUERY_TYPE_PHONE;
3440858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy    }
3441858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy
3442858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy    @Override
3443858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy    public BaseRecipientAdapter getAdapter() {
3444858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy        return (BaseRecipientAdapter) super.getAdapter();
3445c7a87f0ad6a8f722ba93cb7c457ed1a1be5ab3b5Tom Taylor    }
3446972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang
3447972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang    /**
3448972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang     * Append a new {@link RecipientEntry} to the end of the recipient chips, leaving any
3449972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang     * unfinished text at the end.
3450972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang     */
3451972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang    public void appendRecipientEntry(final RecipientEntry entry) {
3452972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        clearComposingText();
3453972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang
3454972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        final Editable editable = getText();
3455972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        int chipInsertionPoint = 0;
3456972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang
3457972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        // Find the end of last chip and see if there's any unchipified text.
3458972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        final DrawableRecipientChip[] recips = getSortedRecipients();
3459972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        if (recips != null && recips.length > 0) {
3460972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang            final DrawableRecipientChip last = recips[recips.length - 1];
3461972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang            // The chip will be inserted at the end of last chip + 1. All the unfinished text after
3462972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang            // the insertion point will be kept untouched.
3463972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang            chipInsertionPoint = editable.getSpanEnd(last) + 1;
3464972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        }
3465972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang
3466ffc0111fa742c72f2457cecc1116e4ea3c6c8c6eJin Cao        final CharSequence chip = createChip(entry);
3467972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        if (chip != null) {
3468972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang            editable.insert(chipInsertionPoint, chip);
3469972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang        }
3470972def7a06f7d820313228c5b1311ccbe8096be0Kaikai Wang    }
347135e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao
3472cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang    /**
3473cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang     * Remove all chips matching the given RecipientEntry.
3474cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang     */
3475cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang    public void removeRecipientEntry(final RecipientEntry entry) {
3476cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang        final DrawableRecipientChip[] recips = getText()
3477cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang                .getSpans(0, getText().length(), DrawableRecipientChip.class);
3478cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang
3479cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang        for (final DrawableRecipientChip recipient : recips) {
3480cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang            final RecipientEntry existingEntry = recipient.getEntry();
3481cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang            if (existingEntry != null && existingEntry.isValid() &&
3482cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang                    existingEntry.isSamePerson(entry)) {
3483cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang                removeChip(recipient);
3484cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang            }
3485cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang        }
3486cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang    }
3487cd980d9f73bfa02155f7bb092ae1fb212ed6e8e2Kaikai Wang
34884de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao    public void setAlternatePopupAnchor(View v) {
34894de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao        mAlternatePopupAnchor = v;
34904de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao    }
34914de6a53de8959dc20bfb05e7b54cb47ccc5367ecJin Cao
3492654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy    @Override
3493654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy    public void setVisibility(int visibility) {
3494654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy        super.setVisibility(visibility);
3495654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy
3496654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy        if (visibility != GONE && mRequiresShrinkWhenNotGone) {
3497654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy            mRequiresShrinkWhenNotGone = false;
3498654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy            mHandler.post(mDelayedShrink);
3499654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy        }
3500654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy    }
3501654c967e9560ccb40a465a78e3296980f530ed55Scott Kennedy
350235e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    private static class ChipBitmapContainer {
350335e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        Bitmap bitmap;
350435e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        // information used for positioning the loaded icon
350535e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        boolean loadIcon = true;
350635e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        float left;
350735e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        float top;
350835e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        float right;
350935e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        float bottom;
3510e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        // information used for positioning the warning icon
3511e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        float warningIconLeft;
3512e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        float warningIconTop;
3513e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        float warningIconRight;
3514e2348bfcd0a710201ba98cd5642cc1e3b5fa6465Joseph Moghadam        float warningIconBottom;
351535e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    }
35161852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira}
3517