diff options
Diffstat (limited to 'telephony/java/android')
6 files changed, 309 insertions, 87 deletions
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java index 8a47339..ffabb7b 100644 --- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java +++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java @@ -16,83 +16,200 @@ package android.telephony; +import com.google.i18n.phonenumbers.AsYouTypeFormatter; +import com.google.i18n.phonenumbers.PhoneNumberUtil; + +import android.telephony.PhoneNumberUtils; import android.text.Editable; import android.text.Selection; import android.text.TextWatcher; -import android.widget.TextView; import java.util.Locale; /** - * Watches a {@link TextView} and if a phone number is entered will format it using - * {@link PhoneNumberUtils#formatNumber(Editable, int)}. The formatting is based on - * the current system locale when this object is created and future locale changes - * may not take effect on this instance. + * Watches a {@link android.widget.TextView} and if a phone number is entered + * will format it. + * <p> + * Stop formatting when the user + * <ul> + * <li>Inputs non-dialable characters</li> + * <li>Removes the separator in the middle of string.</li> + * </ul> + * <p> + * The formatting will be restarted once the text is cleared. */ public class PhoneNumberFormattingTextWatcher implements TextWatcher { + /** + * One or more characters were removed from the end. + */ + private final static int STATE_REMOVE_LAST = 0; + + /** + * One or more characters were appended. + */ + private final static int STATE_APPEND = 1; + + /** + * One or more digits were changed in the beginning or the middle of text. + */ + private final static int STATE_MODIFY_DIGITS = 2; + + /** + * The changes other than the above. + */ + private final static int STATE_OTHER = 3; - static private int sFormatType; - static private Locale sCachedLocale; - private boolean mFormatting; - private boolean mDeletingHyphen; - private int mHyphenStart; - private boolean mDeletingBackward; + /** + * The state of this change could be one value of the above + */ + private int mState; + /** + * Indicates the change was caused by ourselves. + */ + private boolean mSelfChange = false; + + /** + * Indicates the formatting has been stopped. + */ + private boolean mStopFormatting; + + private AsYouTypeFormatter mFormatter; + + /** + * The formatting is based on the current system locale and future locale changes + * may not take effect on this instance. + */ public PhoneNumberFormattingTextWatcher() { - if (sCachedLocale == null || sCachedLocale != Locale.getDefault()) { - sCachedLocale = Locale.getDefault(); - sFormatType = PhoneNumberUtils.getFormatTypeForLocale(sCachedLocale); + this(Locale.getDefault().getCountry()); + } + + /** + * The formatting is based on the given <code>countryCode</code>. + * + * @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region + * where the phone number is being entered. + * + * @hide + */ + public PhoneNumberFormattingTextWatcher(String countryCode) { + if (countryCode == null) throw new IllegalArgumentException(); + mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode); + } + + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + if (mSelfChange || mStopFormatting) { + return; + } + if (count == 0 && s.length() == start) { + // Append one or more new chars + mState = STATE_APPEND; + } else if (after == 0 && start + count == s.length() && count > 0) { + // Remove one or more chars from the end of string. + mState = STATE_REMOVE_LAST; + } else if (count > 0 && !hasSeparator(s, start, count)) { + // Remove the dialable chars in the begin or middle of text. + mState = STATE_MODIFY_DIGITS; + } else { + mState = STATE_OTHER; } } - public synchronized void afterTextChanged(Editable text) { - // Make sure to ignore calls to afterTextChanged caused by the work done below - if (!mFormatting) { - mFormatting = true; - - // If deleting the hyphen, also delete the char before or after that - if (mDeletingHyphen && mHyphenStart > 0) { - if (mDeletingBackward) { - if (mHyphenStart - 1 < text.length()) { - text.delete(mHyphenStart - 1, mHyphenStart); - } - } else if (mHyphenStart < text.length()) { - text.delete(mHyphenStart, mHyphenStart + 1); - } + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (mSelfChange || mStopFormatting) { + return; + } + if (mState == STATE_OTHER) { + if (count > 0 && !hasSeparator(s, start, count)) { + // User inserted the dialable characters in the middle of text. + mState = STATE_MODIFY_DIGITS; } + } + // Check whether we should stop formatting. + if (mState == STATE_APPEND && count > 0 && hasSeparator(s, start, count)) { + // User appended the non-dialable character, stop formatting. + stopFormatting(); + } else if (mState == STATE_OTHER) { + // User must insert or remove the non-dialable characters in the begin or middle of + // number, stop formatting. + stopFormatting(); + } + } - PhoneNumberUtils.formatNumber(text, sFormatType); - - mFormatting = false; + public synchronized void afterTextChanged(Editable s) { + if (mStopFormatting) { + // Restart the formatting when all texts were clear. + mStopFormatting = !(s.length() == 0); + return; + } + if (mSelfChange) { + // Ignore the change caused by s.replace(). + return; + } + String formatted = reformat(s, Selection.getSelectionEnd(s)); + if (formatted != null) { + int rememberedPos = mFormatter.getRememberedPosition(); + mSelfChange = true; + s.replace(0, s.length(), formatted, 0, formatted.length()); + // The text could be changed by other TextWatcher after we changed it. If we found the + // text is not the one we were expecting, just give up calling setSelection(). + if (formatted.equals(s.toString())) { + Selection.setSelection(s, rememberedPos); + } + mSelfChange = false; } } - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // Check if the user is deleting a hyphen - if (!mFormatting) { - // Make sure user is deleting one char, without a selection - final int selStart = Selection.getSelectionStart(s); - final int selEnd = Selection.getSelectionEnd(s); - if (s.length() > 1 // Can delete another character - && count == 1 // Deleting only one character - && after == 0 // Deleting - && s.charAt(start) == '-' // a hyphen - && selStart == selEnd) { // no selection - mDeletingHyphen = true; - mHyphenStart = start; - // Check if the user is deleting forward or backward - if (selStart == start + 1) { - mDeletingBackward = true; - } else { - mDeletingBackward = false; + /** + * Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the + * nearest dialable char to the left. For instance, if the number is (650) 123-45678 and '4' is + * removed then the cursor should be behind '3' instead of '-'. + */ + private String reformat(CharSequence s, int cursor) { + // The index of char to the leftward of the cursor. + int curIndex = cursor - 1; + String formatted = null; + mFormatter.clear(); + char lastNonSeparator = 0; + boolean hasCursor = false; + int len = s.length(); + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if (PhoneNumberUtils.isNonSeparator(c)) { + if (lastNonSeparator != 0) { + formatted = getFormattedNumber(lastNonSeparator, hasCursor); + hasCursor = false; } - } else { - mDeletingHyphen = false; + lastNonSeparator = c; } + if (i == curIndex) { + hasCursor = true; + } + } + if (lastNonSeparator != 0) { + formatted = getFormattedNumber(lastNonSeparator, hasCursor); } + return formatted; } - public void onTextChanged(CharSequence s, int start, int before, int count) { - // Does nothing + private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) { + return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator) + : mFormatter.inputDigit(lastNonSeparator); + } + + private void stopFormatting() { + mStopFormatting = true; + mFormatter.clear(); + } + + private boolean hasSeparator(final CharSequence s, final int start, final int count) { + for (int i = start; i < start + count; i++) { + char c = s.charAt(i); + if (!PhoneNumberUtils.isNonSeparator(c)) { + return true; + } + } + return false; } } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 7829006..b0fa0f5 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -16,6 +16,11 @@ package android.telephony; +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; + import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -617,7 +622,7 @@ public class PhoneNumberUtils } } else { // In the US, 1-650-555-1234 must be equal to 650-555-1234, - // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan. + // while 090-1234-1234 must not be equal to 90-1234-1234 in Japan. // This request exists just in US (with 1 trunk (NDD) prefix). // In addition, "011 11 7005554141" must not equal to "+17005554141", // while "011 1 7005554141" must equal to "+17005554141" @@ -779,10 +784,10 @@ public class PhoneNumberUtils if (prependPlus) { // This is an "international number" and should have // a plus prepended to the dialing number. But there - // can also be Gsm MMI codes as defined in TS 22.030 6.5.2 + // can also be GSM MMI codes as defined in TS 22.030 6.5.2 // so we need to handle those also. // - // http://web.telia.com/~u47904776/gsmkode.htm is a + // http://web.telia.com/~u47904776/gsmkode.htm // has a nice list of some of these GSM codes. // // Examples are: @@ -870,10 +875,10 @@ public class PhoneNumberUtils // FIXME(mkf) TS 23.040 9.1.2.3 says // "if a mobile receives 1111 in a position prior to - // the last semi-octet then processing shall commense with + // the last semi-octet then processing shall commence with // the next semi-octet and the intervening // semi-octet shall be ignored" - // How does this jive with 24,008 10.5.4.7 + // How does this jive with 24.008 10.5.4.7 b = (byte)((bytes[i] >> 4) & 0xf); @@ -1004,7 +1009,7 @@ public class PhoneNumberUtils * Convert a dialing number to BCD byte array * * @param number dialing number string - * if the dialing number starts with '+', set to internationl TOA + * if the dialing number starts with '+', set to international TOA * @return BCD byte array */ public static byte[] @@ -1108,10 +1113,10 @@ public class PhoneNumberUtils * * @param source the phone number to format * @param defaultFormattingType The default formatting rules to apply if the number does - * not begin with +<country_code> + * not begin with +[country_code] * @return The phone number formatted with the given formatting type. * - * @hide TODO:Shuold be unhidden. + * @hide TODO: Should be unhidden. */ public static String formatNumber(String source, int defaultFormattingType) { SpannableStringBuilder text = new SpannableStringBuilder(source); @@ -1138,7 +1143,7 @@ public class PhoneNumberUtils * * @param text The number to be formatted, will be modified with the formatting * @param defaultFormattingType The default formatting rules to apply if the number does - * not begin with +<country_code> + * not begin with +[country_code] */ public static void formatNumber(Editable text, int defaultFormattingType) { int formatType = defaultFormattingType; @@ -1319,6 +1324,88 @@ public class PhoneNumberUtils } } + /** + * Format the given phoneNumber to the E.164 representation. + * <p> + * The given phone number must have an area code and could have a country + * code. + * <p> + * The defaultCountryIso is used to validate the given number and generate + * the E.164 phone number if the given number doesn't have a country code. + * + * @param phoneNumber + * the phone number to format + * @param defaultCountryIso + * the ISO 3166-1 two letters country code + * @return the E.164 representation, or null if the given phone number is + * not valid. + * + * @hide + */ + public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + String result = null; + try { + PhoneNumber pn = util.parse(phoneNumber, defaultCountryIso); + if (util.isValidNumber(pn)) { + result = util.format(pn, PhoneNumberFormat.E164); + } + } catch (NumberParseException e) { + } + return result; + } + + /** + * Format a phone number. + * <p> + * If the given number doesn't have the country code, the phone will be + * formatted to the default country's convention. + * + * @param phoneNumber + * the number to be formatted. + * @param defaultCountryIso + * the ISO 3166-1 two letters country code whose convention will + * be used if the given number doesn't have the country code. + * @return the formatted number, or null if the given number is not valid. + * + * @hide + */ + public static String formatNumber(String phoneNumber, String defaultCountryIso) { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + String result = null; + try { + PhoneNumber pn = util.parse(phoneNumber, defaultCountryIso); + result = util.formatInOriginalFormat(pn, defaultCountryIso); + } catch (NumberParseException e) { + } + return result; + } + + /** + * Normalize a phone number by removing the characters other than digits. If + * the given number has keypad letters, the letters will be converted to + * digits first. + * + * @param phoneNumber + * the number to be normalized. + * @return the normalized number. + * + * @hide + */ + public static String normalizeNumber(String phoneNumber) { + StringBuilder sb = new StringBuilder(); + int len = phoneNumber.length(); + for (int i = 0; i < len; i++) { + char c = phoneNumber.charAt(i); + if ((i == 0 && c == '+') || PhoneNumberUtils.isISODigit(c)) { + sb.append(c); + } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber)); + } + } + return sb.toString(); + } + // Three and four digit phone numbers for either special services, // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should // not match. @@ -1546,7 +1633,7 @@ public class PhoneNumberUtils * @hide */ public static String - cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) { + cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) { String retStr = dialStr; // Checks if the plus sign character is in the passed-in dial string @@ -1554,7 +1641,7 @@ public class PhoneNumberUtils dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) { // Format the string based on the rules for the country the number is from, // and the current country the phone is camped on. - if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) { + if ((currFormat == defaultFormat) && (currFormat == FORMAT_NANP)) { // Handle case where default and current telephone numbering plans are NANP. String postDialStr = null; String tempDialStr = dialStr; @@ -1741,7 +1828,7 @@ public class PhoneNumberUtils return -1; } - // This function appends the non-diablable P/W character to the original + // This function appends the non-dialable P/W character to the original // dial string based on the dialable index passed in private static String appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) { @@ -1761,7 +1848,7 @@ public class PhoneNumberUtils return retStr; } - //===== Begining of utility methods used in compareLoosely() ===== + //===== Beginning of utility methods used in compareLoosely() ===== /** * Phone numbers are stored in "lookup" form in the database @@ -1883,12 +1970,12 @@ public class PhoneNumberUtils //===== End of utility methods used only in compareLoosely() ===== - //===== Beggining of utility methods used only in compareStrictly() ==== + //===== Beginning of utility methods used only in compareStrictly() ==== /* * If true, the number is country calling code. */ - private static final boolean COUNTLY_CALLING_CALL[] = { + private static final boolean COUNTRY_CALLING_CALL[] = { true, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, true, true, false, @@ -1900,18 +1987,18 @@ public class PhoneNumberUtils false, true, true, true, true, false, true, false, false, true, true, true, true, true, true, true, false, false, true, false, }; - private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length; + private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length; /** * @return true when input is valid Country Calling Code. */ private static boolean isCountryCallingCode(int countryCallingCodeCandidate) { return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH && - COUNTLY_CALLING_CALL[countryCallingCodeCandidate]; + COUNTRY_CALLING_CALL[countryCallingCodeCandidate]; } /** - * Returns interger corresponding to the input if input "ch" is + * Returns integer corresponding to the input if input "ch" is * ISO-LATIN characters 0-9. * Returns -1 otherwise */ @@ -2046,7 +2133,7 @@ public class PhoneNumberUtils /** * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means - * that "str" has only one digit and separater characters. The one digit is + * that "str" has only one digit and separator characters. The one digit is * assumed to be trunk prefix. */ private static boolean checkPrefixIsIgnorable(final String str, diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 830af47..38f44d8 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -25,6 +25,7 @@ import android.telephony.CellLocation; import android.util.Log; import com.android.internal.telephony.IPhoneStateListener; +import com.android.internal.telephony.Phone; /** * A listener class for monitoring changes in specific telephony states @@ -284,7 +285,7 @@ public class PhoneStateListener { } public void onDataConnectionStateChanged(int state, int networkType) { - Message.obtain(mHandler, LISTEN_DATA_CONNECTION_STATE, state, networkType, null). + Message.obtain(mHandler, LISTEN_DATA_CONNECTION_STATE, state, networkType). sendToTarget(); } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index f5e9751..953696b 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -21,7 +21,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; -import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.ISms; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.SmsRawData; @@ -33,7 +32,7 @@ import java.util.List; /* * TODO(code review): Curious question... Why are a lot of these * methods not declared as static, since they do not seem to require - * any local object state? Assumedly this cannot be changed without + * any local object state? Presumably this cannot be changed without * interfering with the API... */ @@ -42,7 +41,8 @@ import java.util.List; * Get this object by calling the static method SmsManager.getDefault(). */ public final class SmsManager { - private static SmsManager sInstance; + /** Singleton object constructed during class initialization. */ + private static final SmsManager sInstance = new SmsManager(); /** * Send a text based SMS. @@ -52,8 +52,8 @@ public final class SmsManager { * the current default SMSC * @param text the body of the message to send * @param sentIntent if not NULL this <code>PendingIntent</code> is - * broadcast when the message is sucessfully sent, or failed. - * The result code will be <code>Activity.RESULT_OK<code> for success, + * broadcast when the message is successfully sent, or failed. + * The result code will be <code>Activity.RESULT_OK</code> for success, * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> * <code>RESULT_ERROR_RADIO_OFF</code><br> @@ -116,7 +116,7 @@ public final class SmsManager { * @param sentIntents if not null, an <code>ArrayList</code> of * <code>PendingIntent</code>s (one for each message part) that is * broadcast when the corresponding message part has been sent. - * The result code will be <code>Activity.RESULT_OK<code> for success, + * The result code will be <code>Activity.RESULT_OK</code> for success, * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> * <code>RESULT_ERROR_RADIO_OFF</code><br> @@ -125,7 +125,7 @@ public final class SmsManager { * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.<br> * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applicaitons, + * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an <code>ArrayList</code> of * <code>PendingIntent</code>s (one for each message part) that is @@ -178,8 +178,8 @@ public final class SmsManager { * @param destinationPort the port to deliver the message to * @param data the body of the message to send * @param sentIntent if not NULL this <code>PendingIntent</code> is - * broadcast when the message is sucessfully sent, or failed. - * The result code will be <code>Activity.RESULT_OK<code> for success, + * broadcast when the message is successfully sent, or failed. + * The result code will be <code>Activity.RESULT_OK</code> for success, * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> * <code>RESULT_ERROR_RADIO_OFF</code><br> @@ -188,7 +188,7 @@ public final class SmsManager { * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.<br> * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applicaitons, + * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is delivered to the recipient. The @@ -224,9 +224,6 @@ public final class SmsManager { * @return the default instance of the SmsManager */ public static SmsManager getDefault() { - if (sInstance == null) { - sInstance = new SmsManager(); - } return sInstance; } diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index a284ea5..d899430 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -20,7 +20,6 @@ import android.os.Parcel; import android.util.Log; import com.android.internal.telephony.GsmAlphabet; -import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 27e08d4..2370add 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -912,4 +912,25 @@ public class TelephonyManager { return null; } } + + /** + * @return true if the current device is "voice capable". + * <p> + * "Voice capable" means that this device supports circuit-switched + * (i.e. voice) phone calls over the telephony network, and is allowed + * to display the in-call UI while a cellular voice call is active. + * This will be false on "data only" devices which can't make voice + * calls and don't support any in-call UI. + * <p> + * Note: the meaning of this flag is subtly different from the + * PackageManager.FEATURE_TELEPHONY system feature, which is available + * on any device with a telephony radio, even if the device is + * data-only. + * + * @hide pending API review + */ + public boolean isVoiceCapable() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); + } } |