172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project/*
272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Copyright (C) 2008 Esmertec AG.
372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * you may not use this file except in compliance with the License.
772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * You may obtain a copy of the License at
872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
1072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
1172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * See the License for the specific language governing permissions and
1572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * limitations under the License.
1672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project */
1772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
1872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectpackage com.android.mms.ui;
1972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
20d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport java.util.ArrayList;
21d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport java.util.List;
2272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
2372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.content.Context;
24f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms;
251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.telephony.PhoneNumberUtils;
2672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.text.Annotation;
272f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Projectimport android.text.Editable;
2872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.text.Layout;
291d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.text.Spannable;
301d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.text.SpannableString;
3172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.text.Spanned;
3272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.text.TextUtils;
332f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Projectimport android.text.TextWatcher;
34cd775166502f602abfc41eee0cdc749a4b655867Tom Taylorimport android.text.util.Rfc822Token;
35cd775166502f602abfc41eee0cdc749a4b655867Tom Taylorimport android.text.util.Rfc822Tokenizer;
3672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.util.AttributeSet;
37d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.view.ContextMenu.ContextMenuInfo;
3872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.view.MotionEvent;
392d4b986e57a67eabacc0599219c726de8600836dTom Taylorimport android.view.View;
40d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.view.inputmethod.EditorInfo;
412d4b986e57a67eabacc0599219c726de8600836dTom Taylorimport android.widget.AdapterView;
4272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.widget.MultiAutoCompleteTextView;
4372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
44d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.ex.chips.RecipientEditTextView;
45d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.MmsConfig;
46d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.data.Contact;
47d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.data.ContactList;
4872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
4972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project/**
5072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Provide UI for editing the recipients of multi-media messages.
5172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project */
522d4b986e57a67eabacc0599219c726de8600836dTom Taylorpublic class RecipientsEditor extends RecipientEditTextView {
5372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private int mLongPressedPosition = -1;
5472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private final RecipientsEditorTokenizer mTokenizer;
55b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor    private char mLastSeparator = ',';
562d4b986e57a67eabacc0599219c726de8600836dTom Taylor    private Runnable mOnSelectChipRunnable;
578f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor    private final AddressValidator mInternalValidator;
588f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor
598f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor    /** A noop validator that does not munge invalid texts and claims any address is valid */
608f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor    private class AddressValidator implements Validator {
618f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor        public CharSequence fixText(CharSequence invalidText) {
628f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor            return invalidText;
638f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor        }
648f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor
658f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor        public boolean isValid(CharSequence text) {
668f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor            return true;
678f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor        }
688f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor    }
6972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
7072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public RecipientsEditor(Context context, AttributeSet attrs) {
712d4b986e57a67eabacc0599219c726de8600836dTom Taylor        super(context, attrs);
728f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor
73ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev        mTokenizer = new RecipientsEditorTokenizer();
7472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        setTokenizer(mTokenizer);
758f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor
768f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor        mInternalValidator = new AddressValidator();
778f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor        super.setValidator(mInternalValidator);
788f4333b0cd6bcacfcb4ffc9f1cc188f1ceb6170eTom Taylor
793f1a2ade1338a719ceb0fff053951577a21ebec4Amith Yamasani        // For the focus to move to the message body when soft Next is pressed
803f1a2ade1338a719ceb0fff053951577a21ebec4Amith Yamasani        setImeOptions(EditorInfo.IME_ACTION_NEXT);
812f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project
82c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor        setThreshold(1);    // pop-up the list after a single char is typed
83c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor
842f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project        /*
852f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         * The point of this TextWatcher is that when the user chooses
862f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         * an address completion from the AutoCompleteTextView menu, it
872f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         * is marked up with Annotation objects to tie it back to the
882f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         * address book entry that it came from.  If the user then goes
892f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         * back and edits that part of the text, it no longer corresponds
902f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         * to that address book entry and needs to have the Annotations
912f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         * claiming that it does removed.
922f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project         */
932f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project        addTextChangedListener(new TextWatcher() {
942f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project            private Annotation[] mAffected;
952f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project
96ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev            @Override
972f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project            public void beforeTextChanged(CharSequence s, int start,
98592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    int count, int after) {
992f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project                mAffected = ((Spanned) s).getSpans(start, start + count,
100592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                        Annotation.class);
1012f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project            }
1022f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project
103ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev            @Override
1042f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project            public void onTextChanged(CharSequence s, int start,
105592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    int before, int after) {
106592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                if (before == 0 && after == 1) {    // inserting a character
107592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    char c = s.charAt(start);
108592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    if (c == ',' || c == ';') {
109592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                        // Remember the delimiter the user typed to end this recipient. We'll
110592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                        // need it shortly in terminateToken().
111592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                        mLastSeparator = c;
112592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    }
113592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                }
1142f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project            }
1152f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project
116ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev            @Override
1172f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project            public void afterTextChanged(Editable s) {
1182f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project                if (mAffected != null) {
1192f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project                    for (Annotation a : mAffected) {
1202f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project                        s.removeSpan(a);
1212f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project                    }
1222f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project                }
1232f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project                mAffected = null;
1242f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project            }
1252f56791329cefc49ce79663b8d3aff50594cc1f3The Android Open Source Project        });
12672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
127592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
1284a289bdc49453d22851ce7a46a893604536a539dTom Taylor    @Override
1292d4b986e57a67eabacc0599219c726de8600836dTom Taylor    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1302d4b986e57a67eabacc0599219c726de8600836dTom Taylor        super.onItemClick(parent, view, position, id);
1312d4b986e57a67eabacc0599219c726de8600836dTom Taylor
1322d4b986e57a67eabacc0599219c726de8600836dTom Taylor        if (mOnSelectChipRunnable != null) {
1332d4b986e57a67eabacc0599219c726de8600836dTom Taylor            mOnSelectChipRunnable.run();
1342d4b986e57a67eabacc0599219c726de8600836dTom Taylor        }
1352d4b986e57a67eabacc0599219c726de8600836dTom Taylor    }
1362d4b986e57a67eabacc0599219c726de8600836dTom Taylor
1372d4b986e57a67eabacc0599219c726de8600836dTom Taylor    public void setOnSelectChipRunnable(Runnable onSelectChipRunnable) {
1382d4b986e57a67eabacc0599219c726de8600836dTom Taylor        mOnSelectChipRunnable = onSelectChipRunnable;
1392d4b986e57a67eabacc0599219c726de8600836dTom Taylor    }
1402d4b986e57a67eabacc0599219c726de8600836dTom Taylor
1412d4b986e57a67eabacc0599219c726de8600836dTom Taylor    @Override
1424a289bdc49453d22851ce7a46a893604536a539dTom Taylor    public boolean enoughToFilter() {
1435d31dfbc15ca3403b8c0a489256fc1e7fba38101Tom Taylor        if (!super.enoughToFilter()) {
1445d31dfbc15ca3403b8c0a489256fc1e7fba38101Tom Taylor            return false;
1455d31dfbc15ca3403b8c0a489256fc1e7fba38101Tom Taylor        }
146592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor        // If the user is in the middle of editing an existing recipient, don't offer the
1474a289bdc49453d22851ce7a46a893604536a539dTom Taylor        // auto-complete menu. Without this, when the user selects an auto-complete menu item,
1484a289bdc49453d22851ce7a46a893604536a539dTom Taylor        // it will get added to the list of recipients so we end up with the old before-editing
1494a289bdc49453d22851ce7a46a893604536a539dTom Taylor        // recipient and the new post-editing recipient. As a precedent, gmail does not show
1504a289bdc49453d22851ce7a46a893604536a539dTom Taylor        // the auto-complete menu when editing an existing recipient.
1514a289bdc49453d22851ce7a46a893604536a539dTom Taylor        int end = getSelectionEnd();
1524a289bdc49453d22851ce7a46a893604536a539dTom Taylor        int len = getText().length();
1534a289bdc49453d22851ce7a46a893604536a539dTom Taylor
1544a289bdc49453d22851ce7a46a893604536a539dTom Taylor        return end == len;
1555d31dfbc15ca3403b8c0a489256fc1e7fba38101Tom Taylor
1564a289bdc49453d22851ce7a46a893604536a539dTom Taylor    }
15772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
1581d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public int getRecipientCount() {
1591d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mTokenizer.getNumbers().size();
16072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
16172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
1621d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public List<String> getNumbers() {
1631d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mTokenizer.getNumbers();
1641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1651d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
166a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public ContactList constructContactsFromInput(boolean blocking) {
1671d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        List<String> numbers = mTokenizer.getNumbers();
1681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        ContactList list = new ContactList();
1691d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        for (String number : numbers) {
170a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            Contact contact = Contact.get(number, blocking);
17125b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang            contact.setNumber(number);
17225b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang            list.add(contact);
1731d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
1741d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return list;
1751d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1761d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
17778f81825431c8c52cad57bc4a363747533a701abWei Huang    private boolean isValidAddress(String number, boolean isMms) {
17878f81825431c8c52cad57bc4a363747533a701abWei Huang        if (isMms) {
17978f81825431c8c52cad57bc4a363747533a701abWei Huang            return MessageUtils.isValidMmsAddress(number);
18078f81825431c8c52cad57bc4a363747533a701abWei Huang        } else {
18149183c099b1ac79f3c2fe97368ed76caba4ca98eWei Huang            // TODO: PhoneNumberUtils.isWellFormedSmsAddress() only check if the number is a valid
18249183c099b1ac79f3c2fe97368ed76caba4ca98eWei Huang            // GSM SMS address. If the address contains a dialable char, it considers it a well
18349183c099b1ac79f3c2fe97368ed76caba4ca98eWei Huang            // formed SMS addr. CDMA doesn't work that way and has a different parser for SMS
18449183c099b1ac79f3c2fe97368ed76caba4ca98eWei Huang            // address (see CdmaSmsAddress.parse(String address)). We should definitely fix this!!!
18549183c099b1ac79f3c2fe97368ed76caba4ca98eWei Huang            return PhoneNumberUtils.isWellFormedSmsAddress(number)
18649183c099b1ac79f3c2fe97368ed76caba4ca98eWei Huang                    || Mms.isEmailAddress(number);
1871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
1881d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1891d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
19078f81825431c8c52cad57bc4a363747533a701abWei Huang    public boolean hasValidRecipient(boolean isMms) {
1911d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        for (String number : mTokenizer.getNumbers()) {
19278f81825431c8c52cad57bc4a363747533a701abWei Huang            if (isValidAddress(number, isMms))
1931d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                return true;
1941d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
1951d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return false;
1961d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1971d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
19878f81825431c8c52cad57bc4a363747533a701abWei Huang    public boolean hasInvalidRecipient(boolean isMms) {
1991d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        for (String number : mTokenizer.getNumbers()) {
20078f81825431c8c52cad57bc4a363747533a701abWei Huang            if (!isValidAddress(number, isMms)) {
201817eb982a66303a8f87fc3061e3493a232627e96Satish Roddom                if (MmsConfig.getEmailGateway() == null) {
202817eb982a66303a8f87fc3061e3493a232627e96Satish Roddom                    return true;
203817eb982a66303a8f87fc3061e3493a232627e96Satish Roddom                } else if (!MessageUtils.isAlias(number)) {
204817eb982a66303a8f87fc3061e3493a232627e96Satish Roddom                    return true;
205817eb982a66303a8f87fc3061e3493a232627e96Satish Roddom                }
206817eb982a66303a8f87fc3061e3493a232627e96Satish Roddom            }
2071d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2081d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return false;
2091d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2101d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
21178f81825431c8c52cad57bc4a363747533a701abWei Huang    public String formatInvalidNumbers(boolean isMms) {
2121d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        StringBuilder sb = new StringBuilder();
2131d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        for (String number : mTokenizer.getNumbers()) {
21478f81825431c8c52cad57bc4a363747533a701abWei Huang            if (!isValidAddress(number, isMms)) {
2151d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                if (sb.length() != 0) {
2161d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                    sb.append(", ");
2171d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                }
2181d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                sb.append(number);
2191d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
2201d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return sb.toString();
2221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2231d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2241d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public boolean containsEmail() {
2251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (TextUtils.indexOf(getText(), '@') == -1)
2261d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return false;
2271d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2281d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        List<String> numbers = mTokenizer.getNumbers();
2291d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        for (String number : numbers) {
2301d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            if (Mms.isEmailAddress(number))
2311d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                return true;
2321d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2331d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return false;
2341d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2351d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2361d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static CharSequence contactToToken(Contact c) {
2371d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        SpannableString s = new SpannableString(c.getNameAndNumber());
2381d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        int len = s.length();
2391d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2401d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (len == 0) {
2411d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return s;
2421d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2431d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2441d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        s.setSpan(new Annotation("number", c.getNumber()), 0, len,
2451d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2461d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2471d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return s;
2481d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2491d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2501d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public void populate(ContactList list) {
2512ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // Very tricky bug. In the recipient editor, we always leave a trailing
2522ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // comma to make it easy for users to add additional recipients. When a
2532ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // user types (or chooses from the dropdown) a new contact Mms has never
2542ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // seen before, the contact gets the correct trailing comma. But when the
2552ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // contact gets added to the mms's contacts table, contacts sends out an
2562ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // onUpdate to CMA. CMA would recompute the recipients and since the
2572ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // recipient editor was still visible, call mRecipientsEditor.populate(recipients).
2582ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // This would replace the recipient that had a comma with a recipient
2592ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // without a comma. When a user manually added a new comma to add another
2602ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // recipient, this would eliminate the span inside the text. The span contains the
2612ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // number part of "Fred Flinstone <123-1231>". Hence, the whole
2622ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // "Fred Flinstone <123-1231>" would be considered the number of
2632ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // the first recipient and get entered into the canonical_addresses table.
2642ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // The fix for this particular problem is very easy. All recipients have commas.
2652ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // TODO: However, the root problem remains. If a user enters the recipients editor
2662ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // and deletes chars into an address chosen from the suggestions, it'll cause
2672ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // the number annotation to get deleted and the whole address (name + number) will
2682ddc2c0585fec6fca59acc115e26dc51f69aacc5Tom Taylor        // be used as the number.
269452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor        if (list.size() == 0) {
270452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            // The base class RecipientEditTextView will ignore empty text. That's why we need
271452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            // this special case.
272b30a184c839d71adcc86dfd3e27a1e8bd59ff85dTom Taylor            setText(null);
273b30a184c839d71adcc86dfd3e27a1e8bd59ff85dTom Taylor        } else {
274452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            for (Contact c : list) {
275452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor                // Calling setText to set the recipients won't create chips,
276452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor                // but calling append() will.
27724da6ee1d535d5d7aa1d43e1a386bb37048fbbc6Tom Taylor                append(contactToToken(c) + ",");
278452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            }
279b30a184c839d71adcc86dfd3e27a1e8bd59ff85dTom Taylor        }
28072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
28172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
28272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private int pointToPosition(int x, int y) {
28372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        x -= getCompoundPaddingLeft();
28472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        y -= getExtendedPaddingTop();
28572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
28672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
28772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        x += getScrollX();
28872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        y += getScrollY();
28972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
29072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        Layout layout = getLayout();
29172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        if (layout == null) {
29272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            return -1;
29372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
29472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
29572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        int line = layout.getLineForVertical(y);
29672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        int off = layout.getOffsetForHorizontal(line, x);
29772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
29872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return off;
29972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
30072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
30172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    @Override
30272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public boolean onTouchEvent(MotionEvent ev) {
30372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        final int action = ev.getAction();
30472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        final int x = (int) ev.getX();
30572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        final int y = (int) ev.getY();
30672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
30772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        if (action == MotionEvent.ACTION_DOWN) {
30872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            mLongPressedPosition = pointToPosition(x, y);
30972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
31072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
31172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return super.onTouchEvent(ev);
31272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
31372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
31472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    @Override
31572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    protected ContextMenuInfo getContextMenuInfo() {
31672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        if ((mLongPressedPosition >= 0)) {
31772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            Spanned text = getText();
3183d2107af884e95720b72201e99d9ab4bce9bba3bTom Taylor            if (mLongPressedPosition <= text.length()) {
3193d2107af884e95720b72201e99d9ab4bce9bba3bTom Taylor                int start = mTokenizer.findTokenStart(text, mLongPressedPosition);
3203d2107af884e95720b72201e99d9ab4bce9bba3bTom Taylor                int end = mTokenizer.findTokenEnd(text, start);
32172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
3223d2107af884e95720b72201e99d9ab4bce9bba3bTom Taylor                if (end != start) {
3236be18bedb5b87dbbcdb54f37d5a0945bd0f71377Tom Taylor                    String number = getNumberAt(getText(), start, end, getContext());
324715e32f97bd9d8ce4b5ba650b97ba4b137150456Tom Taylor                    Contact c = Contact.get(number, false);
3251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                    return new RecipientContextMenuInfo(c);
3263d2107af884e95720b72201e99d9ab4bce9bba3bTom Taylor                }
32772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
32872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
32972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return null;
33072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
33172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
3321d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private static String getNumberAt(Spanned sp, int start, int end, Context context) {
3332d4b986e57a67eabacc0599219c726de8600836dTom Taylor        String number = getFieldAt("number", sp, start, end, context);
33449e599163acc3200e9bda4fb7825c041b67960f1jshin        number = PhoneNumberUtils.replaceUnicodeDigits(number);
335cd775166502f602abfc41eee0cdc749a4b655867Tom Taylor        if (!TextUtils.isEmpty(number)) {
336fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor            int pos = number.indexOf('<');
337fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor            if (pos >= 0 && pos < number.indexOf('>')) {
338fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor                // The number looks like an Rfc882 address, i.e. <fred flinstone> 891-7823
339fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor                Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(number);
340fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor                if (tokens.length == 0) {
341fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor                    return number;
342fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor                }
343fadeebc9df5e72f52438229b98ec6c2ee1938c51Tom Taylor                return tokens[0].getAddress();
344cd775166502f602abfc41eee0cdc749a4b655867Tom Taylor            }
3452d4b986e57a67eabacc0599219c726de8600836dTom Taylor        }
3462d4b986e57a67eabacc0599219c726de8600836dTom Taylor        return number;
347b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor    }
348b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor
349b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor    private static int getSpanLength(Spanned sp, int start, int end, Context context) {
350b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        // TODO: there's a situation where the span can lose its annotations:
351b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        //   - add an auto-complete contact
352b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        //   - add another auto-complete contact
353b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        //   - delete that second contact and keep deleting into the first
354b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        //   - we lose the annotation and can no longer get the span.
355b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        // Need to fix this case because it breaks auto-complete contacts with commas in the name.
35672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        Annotation[] a = sp.getSpans(start, end, Annotation.class);
357b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        if (a.length > 0) {
358b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor            return sp.getSpanEnd(a[0]);
359836945de572ac80fd6f6a7bf59036d4cce801410The Android Open Source Project        }
360b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        return 0;
361b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor    }
362b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor
363b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor    private static String getFieldAt(String field, Spanned sp, int start, int end,
364b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor            Context context) {
365b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        Annotation[] a = sp.getSpans(start, end, Annotation.class);
366b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        String fieldValue = getAnnotation(a, field);
367b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        if (TextUtils.isEmpty(fieldValue)) {
368b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor            fieldValue = TextUtils.substring(sp, start, end);
369b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        }
370b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor        return fieldValue;
371592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
37272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
37372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
37472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private static String getAnnotation(Annotation[] a, String key) {
37572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        for (int i = 0; i < a.length; i++) {
37672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (a[i].getKey().equals(key)) {
37772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                return a[i].getValue();
37872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
37972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
38072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
38172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return "";
38272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
38372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
38472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private class RecipientsEditorTokenizer
38572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            implements MultiAutoCompleteTextView.Tokenizer {
38672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
387ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev        @Override
388592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor        public int findTokenStart(CharSequence text, int cursor) {
389592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            int i = cursor;
390592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            char c;
391592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
392452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            // If we're sitting at a delimiter, back up so we find the previous token
393452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            if (i > 0 && ((c = text.charAt(i - 1)) == ',' || c == ';')) {
394452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor                --i;
395452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            }
396452c9fda4e3c9ef219bca695630faa8c802fd09bTom Taylor            // Now back up until the start or until we find the separator of the previous token
397592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            while (i > 0 && (c = text.charAt(i - 1)) != ',' && c != ';') {
398592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                i--;
399592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            }
400592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            while (i < cursor && text.charAt(i) == ' ') {
401592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                i++;
402592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            }
403592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
404592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            return i;
405592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor        }
406592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
407ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev        @Override
408592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor        public int findTokenEnd(CharSequence text, int cursor) {
409592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            int i = cursor;
410592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            int len = text.length();
411592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            char c;
412592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
413592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            while (i < len) {
414592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                if ((c = text.charAt(i)) == ',' || c == ';') {
415592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    return i;
416592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                } else {
417592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    i++;
418592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                }
419592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            }
420592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
421592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            return len;
422592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor        }
423592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
424ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev        @Override
425592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor        public CharSequence terminateToken(CharSequence text) {
426592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            int i = text.length();
427592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
428592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            while (i > 0 && text.charAt(i - 1) == ' ') {
429592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                i--;
430592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            }
431592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
432592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            char c;
433592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            if (i > 0 && ((c = text.charAt(i - 1)) == ',' || c == ';')) {
434592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                return text;
435592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            } else {
436592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                // Use the same delimiter the user just typed.
437592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                // This lets them have a mixture of commas and semicolons in their list.
438592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                String separator = mLastSeparator + " ";
439592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                if (text instanceof Spanned) {
440592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    SpannableString sp = new SpannableString(text + separator);
441592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
442592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                                            Object.class, sp, 0);
443592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    return sp;
444592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                } else {
445592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                    return text + separator;
446592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                }
447592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor            }
448592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor        }
449592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor
4501d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        public List<String> getNumbers() {
451ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev            Spanned sp = RecipientsEditor.this.getText();
45272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            int len = sp.length();
4531d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            List<String> list = new ArrayList<String>();
4544a289bdc49453d22851ce7a46a893604536a539dTom Taylor
45572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            int start = 0;
45672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            int i = 0;
45772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            while (i < len + 1) {
458592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                char c;
459592a22133822eaa0f54a1de1d56cea14f57c2677Tom Taylor                if ((i == len) || ((c = sp.charAt(i)) == ',') || (c == ';')) {
46072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    if (i > start) {
461ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev                        list.add(getNumberAt(sp, start, i, getContext()));
462b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor
463b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor                        // calculate the recipients total length. This is so if the name contains
464b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor                        // commas or semis, we'll skip over the whole name to the next
465b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor                        // recipient, rather than parsing this single name into multiple
466b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor                        // recipients.
467ba7455b4de3c892aed830763ca80d22c8c940e78Todor Kalaydjiev                        int spanLen = getSpanLength(sp, start, i, getContext());
468b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor                        if (spanLen > i) {
469b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor                            i = spanLen;
470b8bd3d53720ee9ebf9146a1d515a516c2068ff69Tom Taylor                        }
47172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    }
47272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
47372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    i++;
47472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
47572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    while ((i < len) && (sp.charAt(i) == ' ')) {
47672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                        i++;
47772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    }
47872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
47972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    start = i;
48072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                } else {
48172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    i++;
48272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                }
48372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
48472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
4851d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return list;
48672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
48772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
48872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
48972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    static class RecipientContextMenuInfo implements ContextMenuInfo {
4901d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        final Contact recipient;
49172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
4921d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        RecipientContextMenuInfo(Contact r) {
49372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            recipient = r;
49472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
49572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
49672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project}
497