RecipientEditTextView.java revision f566dee91901e44db63df3bf393afb1d43a36f78
12d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira/*
22d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * Copyright (C) 2011 The Android Open Source Project
32d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira *
42d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
52d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * you may not use this file except in compliance with the License.
62d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * You may obtain a copy of the License at
72d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira *
82d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
92d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira *
102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * Unless required by applicable law or agreed to in writing, software
112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
122d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * See the License for the specific language governing permissions and
142d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * limitations under the License.
152d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira */
162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
172d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereirapackage com.android.ex.chips;
182d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.content.Context;
202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Bitmap;
212d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Canvas;
222d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Paint;
232d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.Rect;
242d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.drawable.BitmapDrawable;
252d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.graphics.drawable.Drawable;
262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.os.Handler;
272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Editable;
282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Layout;
292d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Selection;
302d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Spannable;
312d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.SpannableString;
322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.Spanned;
332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.TextPaint;
342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.TextUtils;
352d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.method.QwertyKeyListener;
362d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.text.style.ImageSpan;
372d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.util.AttributeSet;
38078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereiraimport android.util.Log;
392d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.view.KeyEvent;
402d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.view.MotionEvent;
412d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.view.View;
422d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.AdapterView;
432d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.AdapterView.OnItemClickListener;
442d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.PopupWindow.OnDismissListener;
452d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.ListPopupWindow;
462d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereiraimport android.widget.MultiAutoCompleteTextView;
472d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
482d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira/**
492d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * RecipientEditTextView is an auto complete text view for use with applications
502d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira * that use the new Chips UI for addressing a message to recipients.
512d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira */
522d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereirapublic class RecipientEditTextView extends MultiAutoCompleteTextView
532d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    implements OnItemClickListener {
542d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
55078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    private static final String TAG = "RecipientEditTextView";
562d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
57078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    private Drawable mChipBackground = null;
582d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
59078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    private int mChipPadding;
602d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
612d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    private Tokenizer mTokenizer;
622d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
632d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    private final Handler mHandler;
642d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
652d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    private Runnable mDelayedSelectionMode = new Runnable() {
662d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        @Override
672d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void run() {
682d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            setSelection(getText().length());
692d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    };
712d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    public RecipientEditTextView(Context context, AttributeSet attrs) {
732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        super(context, attrs);
742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        mHandler = new Handler();
752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        setOnItemClickListener(this);
762d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
772d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
78f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    public RecipientChip constructChipSpan(RecipientEntry contact, int offset, boolean pressed)
79078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        throws NullPointerException {
80078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        if (mChipBackground == null) {
81078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            throw new NullPointerException
82078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                ("Unable to render any chips as setChipDimensions was not called.");
83078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        }
84f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        String text = contact.getDisplayName();
852d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        Layout layout = getLayout();
862d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        int line = layout.getLineForOffset(offset);
872d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        int lineTop = layout.getLineTop(line);
882d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
892d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        TextPaint paint = getPaint();
902d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        float defaultSize = paint.getTextSize();
912d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
922d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Reduce the size of the text slightly so that we can get the "look" of
932d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // padding.
942d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        paint.setTextSize((float) (paint.getTextSize() * .9));
952d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
962d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Ellipsize the text so that it takes AT MOST the entire width of the
972d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // autocomplete text entry area. Make sure to leave space for padding
982d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // on the sides.
992d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        CharSequence ellipsizedText = TextUtils.ellipsize(text, paint, calculateAvailableWidth(),
1002d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                TextUtils.TruncateAt.END);
1012d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1022d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        int height = getLineHeight();
1032d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        int width = (int) Math.floor(paint.measureText(ellipsizedText, 0, ellipsizedText.length()))
104078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                + (mChipPadding * 2);
1052d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1062d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Create the background of the chip.
1072d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        Bitmap tmpBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1082d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        Canvas canvas = new Canvas(tmpBitmap);
1092d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        if (mChipBackground != null) {
1102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mChipBackground.setBounds(0, 0, width, height);
1112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mChipBackground.draw(canvas);
1122d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        } else {
113078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            Log.w(TAG,
114078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                    "Unable to draw a background for the chips as it was never set");
1152d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
1162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1172d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Align the display text with where the user enters text.
118078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), mChipPadding, height
1192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                - layout.getLineDescent(line), paint);
1202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1212d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Get the location of the widget so we can properly offset
1222d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // the anchor for each chip.
1232d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        int[] xy = new int[2];
1242d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        getLocationOnScreen(xy);
1252d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Pass the full text, un-ellipsized, to the chip.
1262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        Drawable result = new BitmapDrawable(getResources(), tmpBitmap);
1272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        result.setBounds(0, 0, width, height);
128f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        Rect bounds = new Rect(xy[0] + offset, xy[1] + lineTop, xy[0] + width,
129f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                calculateLineBottom(xy[1], line));
130f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        RecipientChip recipientChip = new RecipientChip(result, contact, offset, bounds);
1312d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Return text to the original size.
1332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        paint.setTextSize(defaultSize);
1342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1352d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        return recipientChip;
1362d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
1372d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
138f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    // The bottom of the line the chip will be located on is calculated by 4 factors:
139f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    // 1) which line the chip appears on
140f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    // 2) the height of a line in the autocomplete view
141f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    // 3) padding built into the edit text view will move the bottom position
142f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    // 4) the position of the autocomplete view on the screen, taking into account
143f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    // that any top padding will move this down visually
144f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    private int calculateLineBottom(int yOffset, int line) {
145f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        int bottomPadding = 0;
146f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        if (line == getLineCount() - 1) {
147f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            bottomPadding += getPaddingBottom();
148f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        }
149f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        return ((line + 1) * getLineHeight()) + (yOffset + getPaddingTop()) + bottomPadding;
150f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    }
151f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira
1522d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    // Get the max amount of space a chip can take up. The formula takes into
1532d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    // account the width of the EditTextView, any view padding, and padding
1542d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    // that will be added to the chip.
1552d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    private float calculateAvailableWidth() {
156078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        return getWidth() - getPaddingLeft() - getPaddingRight() - (mChipPadding * 2);
1572d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
1582d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
159078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    /**
160078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira     * Set all chip dimensions and resources. This has to be done from the application
161078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira     * as this is a static library.
162078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira     * @param chipBackground drawable
163078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira     * @param padding Padding around the text in a chip
164078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira     * @param offset Offset between the chip and the dropdown of alternates
165078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira     */
166f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    public void setChipDimensions(Drawable chipBackground, float padding) {
167078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        mChipBackground = chipBackground;
168078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        mChipPadding = (int) padding;
1692d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
1702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1712d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    @Override
1722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    public void setTokenizer(Tokenizer tokenizer) {
1732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        mTokenizer = tokenizer;
1742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        super.setTokenizer(mTokenizer);
1752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
1762d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1772d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    // We want to handle replacing text in the onItemClickListener
1782d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    // so we can get all the associated contact information including
1792d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    // display text, address, and id.
1802d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    @Override
1812d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    protected void replaceText(CharSequence text) {
1822d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        return;
1832d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
1842d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
1852d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    @Override
1862d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    public boolean onKeyUp(int keyCode, KeyEvent event) {
1872d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        switch (keyCode) {
1882d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            case KeyEvent.KEYCODE_ENTER:
1892d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            case KeyEvent.KEYCODE_DPAD_CENTER:
1902d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            case KeyEvent.KEYCODE_TAB:
1912d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                if (event.hasNoModifiers()) {
192078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                    if (isPopupShowing()) {
193078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                        // choose the first entry.
194078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                        submitItemAtPosition(0);
195078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                        dismissDropDown();
1962d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        return true;
1972d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                    } else {
1982d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        int end = getSelectionEnd();
1992d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        int start = mTokenizer.findTokenStart(getText(), end);
2002d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        String text = getText().toString().substring(start, end);
2012d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        clearComposingText();
2022d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
2032d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        Editable editable = getText();
204f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                        RecipientEntry entry = RecipientEntry.constructFakeEntry(text);
2052d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        QwertyKeyListener.markAsReplaced(editable, start, end, "");
206f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                        editable.replace(start, end, createChip(entry));
2072d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                        dismissDropDown();
2082d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                    }
2092d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                }
2102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
2112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        return super.onKeyUp(keyCode, event);
2122d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
2132d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
2142d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    public void onChipChanged() {
2152d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // Must be posted so that the previous span
2162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // is correctly replaced with the previous selection points.
2172d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        mHandler.post(mDelayedSelectionMode);
2182d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
2192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
2202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    @Override
2212d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    public boolean onKeyDown(int keyCode, KeyEvent event) {
2222d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        int start = getSelectionStart();
2232d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        int end = getSelectionEnd();
2242d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        Spannable span = getSpannable();
2252d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
2262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        RecipientChip[] chips = span.getSpans(start, end, RecipientChip.class);
2272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        if (chips != null) {
2282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            for (RecipientChip chip : chips) {
2292d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                chip.onKeyDown(keyCode, event);
2302d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            }
2312d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
2322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
2332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        if (keyCode == KeyEvent.KEYCODE_ENTER && event.hasNoModifiers()) {
2342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            return true;
2352d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
2362d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
2372d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        return super.onKeyDown(keyCode, event);
2382d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
2392d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
240078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    private Spannable getSpannable() {
2412d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        return (Spannable) getText();
2422d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
2432d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
244078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
245078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    @Override
246078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    public boolean onTouchEvent(MotionEvent event) {
247078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        int action = event.getAction();
248078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
249078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            Spannable span = getSpannable();
250078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            int offset = getOffsetForPosition(event.getX(), event.getY());
251078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            int start = offset;
252078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            int end = span.length();
253078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            RecipientChip[] chips = span.getSpans(start, end, RecipientChip.class);
254078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            if (chips != null && chips.length > 0) {
255078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                // Get the first chip that matched.
256078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                final RecipientChip currentChip = chips[0];
257078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
258078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                if (action == MotionEvent.ACTION_UP) {
259078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                    currentChip.onClick(this);
260078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                } else if (action == MotionEvent.ACTION_DOWN) {
261078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                    Selection.setSelection(getSpannable(), currentChip.getChipStart(), currentChip
262078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                            .getChipEnd());
263078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                }
264078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                return true;
265078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            }
266078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        }
267078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
268078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        return super.onTouchEvent(event);
269078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    }
270078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
271f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira    private CharSequence createChip(RecipientEntry entry) {
272078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        // We want to override the tokenizer behavior with our own ending
273078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        // token, space.
274f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        SpannableString chipText = new SpannableString(mTokenizer.terminateToken(entry
275f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                .getDisplayName()));
276078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        int end = getSelectionEnd();
277078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        int start = mTokenizer.findTokenStart(getText(), end);
278078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        try {
279f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            chipText.setSpan(constructChipSpan(entry, start, false), 0, entry.getDisplayName()
280f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    .length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
281078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        } catch (NullPointerException e) {
282078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            Log.e(TAG, e.getMessage());
283078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            return null;
284078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        }
285078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
286078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        return chipText;
287078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    }
288078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
289078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    @Override
290078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
291078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        submitItemAtPosition(position);
292078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    }
293078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
294078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    private void submitItemAtPosition(int position) {
295f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        RecipientEntry entry = (RecipientEntry) getAdapter().getItem(position);
296078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        clearComposingText();
297078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
298078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        int end = getSelectionEnd();
299078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        int start = mTokenizer.findTokenStart(getText(), end);
300078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
301078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        Editable editable = getText();
302f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        editable.replace(start, end, createChip(entry));
303078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira        QwertyKeyListener.markAsReplaced(editable, start, end, "");
304078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira    }
305078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira
3062d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    /**
3072d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira     * RecipientChip defines an ImageSpan that contains information relevant to
3082d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira     * a particular recipient.
3092d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira     */
3102d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    public class RecipientChip extends ImageSpan implements OnItemClickListener, OnDismissListener {
3112d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private final CharSequence mDisplay;
3122d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3132d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private final CharSequence mValue;
3142d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3152d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private final int mOffset;
3162d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3172d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private ListPopupWindow mPopup;
3182d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3192d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private View mAnchorView;
3202d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3212d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private int mLeft;
3222d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3232d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private int mId = -1;
3242d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
325f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira        public RecipientChip(Drawable drawable, RecipientEntry entry, int offset, Rect bounds) {
3262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            super(drawable);
327f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            mDisplay = entry.getDisplayName();
328f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            mValue = entry.getDestination();
329f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            mId = entry.getContactId();
3302d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mOffset = offset;
331f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira
3322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mAnchorView = new View(getContext());
3332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mAnchorView.setLeft(bounds.left);
3342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mAnchorView.setRight(bounds.left);
335f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            mAnchorView.setTop(bounds.bottom);
336f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            mAnchorView.setBottom(bounds.bottom);
3372d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mAnchorView.setVisibility(View.GONE);
3382d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3392d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3402d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void onKeyDown(int keyCode, KeyEvent event) {
3412d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            if (keyCode == KeyEvent.KEYCODE_DEL) {
3422d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                if (mPopup != null && mPopup.isShowing()) {
3432d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                    mPopup.dismiss();
3442d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                }
3452d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                removeChip();
3462d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            }
3472d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3482d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3492d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public boolean isCompletedContact() {
3502d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            return mId != -1;
3512d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3522d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3532d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        private void replace(RecipientChip newChip) {
3542d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            Spannable spannable = getSpannable();
3552d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            int spanStart = getChipStart();
3562d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            int spanEnd = getChipEnd();
3572d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            QwertyKeyListener.markAsReplaced(getText(), spanStart, spanEnd, "");
3582d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            spannable.removeSpan(this);
3592d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            spannable.setSpan(newChip, spanStart, spanEnd, 0);
3602d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3612d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3622d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void removeChip() {
3632d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            Spannable spannable = getSpannable();
3642d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            int spanStart = getChipStart();
3652d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            int spanEnd = getChipEnd();
3662d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            QwertyKeyListener.markAsReplaced(getText(), spanStart, spanEnd, "");
3672d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            spannable.removeSpan(this);
3682d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            spannable.setSpan(null, spanStart, spanEnd, 0);
3692d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            onChipChanged();
3702d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3712d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3722d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public int getChipStart() {
3732d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            return getSpannable().getSpanStart(this);
3742d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3752d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3762d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public int getChipEnd() {
3772d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            return getSpannable().getSpanEnd(this);
3782d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3792d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3802d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void replaceChip(String text) {
3812d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            clearComposingText();
3822d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
383078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            RecipientChip newChipSpan = null;
384078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            try {
385f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                newChipSpan = constructChipSpan(RecipientEntry.constructFakeEntry(text),
386f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                        mOffset, false);
387078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            } catch (NullPointerException e) {
388078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                Log.e(TAG, e.getMessage());
389078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira                return;
390078509f1fd42ec04b46565ecc26f4f527b277c5cMindy Pereira            }
3912d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            replace(newChipSpan);
3922d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            if (mPopup != null && mPopup.isShowing()) {
3932d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                mPopup.dismiss();
3942d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            }
3952d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            onChipChanged();
3962d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
3972d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
3982d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public CharSequence getDisplay() {
3992d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            return mDisplay;
4002d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
4012d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
4022d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public CharSequence getValue() {
4032d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            return mValue;
4042d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
4052d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
4062d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void onClick(View widget) {
407f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            if (isCompletedContact()) {
408f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                mPopup = new ListPopupWindow(widget.getContext());
409f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira
410f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                if (!mPopup.isShowing()) {
411f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mAnchorView.setLeft(mLeft);
412f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mAnchorView.setRight(mLeft);
413f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mPopup.setAnchorView(mAnchorView);
414f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mPopup.setAdapter(getAdapter());
415f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    // TODO: get width from dimen.xml.
416f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mPopup.setWidth(getWidth());
417f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mPopup.setOnItemClickListener(this);
418f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mPopup.setOnDismissListener(this);
419f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                    mPopup.show();
420f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                }
421f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            } else {
422f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                // TODO: move the cursor to the end of the view. Add the text
423f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira                // that was in this span to the end of the view as well.
4242d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            }
4252d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
4262d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
4272d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        @Override
4282d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
4292d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira                int y, int bottom, Paint paint) {
4302d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mLeft = (int) x;
4312d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            super.draw(canvas, text, start, end, x, top, y, bottom, paint);
4322d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
4332d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
4342d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        @Override
4352d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) {
4362d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mPopup.dismiss();
4372d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            clearComposingText();
438f566dee91901e44db63df3bf393afb1d43a36f78Mindy Pereira            RecipientEntry entry = (RecipientEntry) adapterView.getItemAtPosition(position);
4392d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            replaceChip(entry.getDisplayName());
4402d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
4412d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira
4422d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        // When the popup dialog is dismissed, return the cursor to the end.
4432d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        @Override
4442d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        public void onDismiss() {
4452d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira            mHandler.post(mDelayedSelectionMode);
4462d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira        }
4472d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira    }
4482d4ee907769ccfc94dc315e932ff235198958c69Mindy Pereira}
449