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