From cded3beaf28a703e1ef8f71bbc6836e6806c3736 Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Fri, 9 Jun 2017 14:16:05 +0000 Subject: Revert "Update AOSP Dialer source from internal google3 repository at cl/158012278. am: 91ce7d2a47" This reverts commit c67d658e7daa453fe9ad9fd1a37f81eaf2048c44. Reason for revert: This CL broke the sailfish-userdebug_javac-all target on master. Change-Id: I9b54333a654c00154ca84f4ece84bea4f07cc19b --- .../contacts/common/compat/ContactsCompat.java | 16 ++ .../contacts/common/compat/PhoneAccountCompat.java | 55 ++++- .../common/compat/PhoneNumberUtilsCompat.java | 132 ++++++++++- .../common/compat/TelephonyManagerCompat.java | 71 +++++- .../compat/telecom/TelecomManagerCompat.java | 260 ++++++++++++++++++++- 5 files changed, 526 insertions(+), 8 deletions(-) (limited to 'java/com/android/contacts/common/compat') diff --git a/java/com/android/contacts/common/compat/ContactsCompat.java b/java/com/android/contacts/common/compat/ContactsCompat.java index e0c9b7e53..39d0b55d3 100644 --- a/java/com/android/contacts/common/compat/ContactsCompat.java +++ b/java/com/android/contacts/common/compat/ContactsCompat.java @@ -21,6 +21,7 @@ import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; +import com.android.dialer.compat.CompatUtils; /** Compatibility class for {@link ContactsContract.Contacts} */ public class ContactsCompat { @@ -28,6 +29,8 @@ public class ContactsCompat { // TODO: Use N APIs private static final Uri ENTERPRISE_CONTENT_FILTER_URI = Uri.withAppendedPath(Contacts.CONTENT_URI, "filter_enterprise"); + // Copied from ContactsContract.Contacts#ENTERPRISE_CONTACT_ID_BASE, which is hidden. + private static final long ENTERPRISE_CONTACT_ID_BASE = 1000000000; /** Not instantiable. */ private ContactsCompat() {} @@ -38,4 +41,17 @@ public class ContactsCompat { } return Contacts.CONTENT_FILTER_URI; } + + /** + * Return {@code true} if a contact ID is from the contacts provider on the enterprise profile. + */ + public static boolean isEnterpriseContactId(long contactId) { + if (CompatUtils.isLollipopCompatible()) { + return Contacts.isEnterpriseContactId(contactId); + } else { + // copied from ContactsContract.Contacts.isEnterpriseContactId + return (contactId >= ENTERPRISE_CONTACT_ID_BASE) + && (contactId < ContactsContract.Profile.MIN_ID); + } + } } diff --git a/java/com/android/contacts/common/compat/PhoneAccountCompat.java b/java/com/android/contacts/common/compat/PhoneAccountCompat.java index aa22c6861..6a24ec033 100644 --- a/java/com/android/contacts/common/compat/PhoneAccountCompat.java +++ b/java/com/android/contacts/common/compat/PhoneAccountCompat.java @@ -20,10 +20,33 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.support.annotation.Nullable; import android.telecom.PhoneAccount; +import android.util.Log; +import com.android.dialer.compat.CompatUtils; /** Compatiblity class for {@link android.telecom.PhoneAccount} */ public class PhoneAccountCompat { + private static final String TAG = PhoneAccountCompat.class.getSimpleName(); + + /** + * Gets the {@link Icon} associated with the given {@link PhoneAccount} + * + * @param phoneAccount the PhoneAccount from which to retrieve the Icon + * @return the Icon, or null + */ + @Nullable + public static Icon getIcon(@Nullable PhoneAccount phoneAccount) { + if (phoneAccount == null) { + return null; + } + + if (CompatUtils.isMarshmallowCompatible()) { + return phoneAccount.getIcon(); + } + + return null; + } + /** * Builds and returns an icon {@code Drawable} to represent this {@code PhoneAccount} in a user * interface. @@ -38,16 +61,44 @@ public class PhoneAccountCompat { if (phoneAccount == null || context == null) { return null; } - return createIconDrawableMarshmallow(phoneAccount, context); + + if (CompatUtils.isMarshmallowCompatible()) { + return createIconDrawableMarshmallow(phoneAccount, context); + } + + if (CompatUtils.isLollipopMr1Compatible()) { + return createIconDrawableLollipopMr1(phoneAccount, context); + } + return null; } @Nullable private static Drawable createIconDrawableMarshmallow( PhoneAccount phoneAccount, Context context) { - Icon accountIcon = phoneAccount.getIcon(); + Icon accountIcon = getIcon(phoneAccount); if (accountIcon == null) { return null; } return accountIcon.loadDrawable(context); } + + @Nullable + private static Drawable createIconDrawableLollipopMr1( + PhoneAccount phoneAccount, Context context) { + try { + return (Drawable) + PhoneAccount.class + .getMethod("createIconDrawable", Context.class) + .invoke(phoneAccount, context); + } catch (ReflectiveOperationException e) { + return null; + } catch (Throwable t) { + Log.e( + TAG, + "Unexpected exception when attempting to call " + + "android.telecom.PhoneAccount#createIconDrawable", + t); + return null; + } + } } diff --git a/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java index a6cfe07cd..960b340d8 100644 --- a/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java +++ b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java @@ -17,7 +17,13 @@ package com.android.contacts.common.compat; import android.telephony.PhoneNumberUtils; +import android.text.Spannable; +import android.text.TextUtils; import android.text.style.TtsSpan; +import com.android.dialer.compat.CompatUtils; +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; /** * This class contains static utility methods extracted from PhoneNumberUtils, and the methods were @@ -31,16 +37,138 @@ public class PhoneNumberUtilsCompat { /** Not instantiable. */ private PhoneNumberUtilsCompat() {} + public static String normalizeNumber(String phoneNumber) { + if (CompatUtils.isLollipopCompatible()) { + return PhoneNumberUtils.normalizeNumber(phoneNumber); + } else { + return normalizeNumberInternal(phoneNumber); + } + } + + /** Implementation copied from {@link PhoneNumberUtils#normalizeNumber} */ + private static String normalizeNumberInternal(String phoneNumber) { + if (TextUtils.isEmpty(phoneNumber)) { + return ""; + } + StringBuilder sb = new StringBuilder(); + int len = phoneNumber.length(); + for (int i = 0; i < len; i++) { + char c = phoneNumber.charAt(i); + // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.) + int digit = Character.digit(c, 10); + if (digit != -1) { + sb.append(digit); + } else if (sb.length() == 0 && c == '+') { + sb.append(c); + } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber)); + } + } + return sb.toString(); + } + public static String formatNumber( String phoneNumber, String phoneNumberE164, String defaultCountryIso) { + if (CompatUtils.isLollipopCompatible()) { return PhoneNumberUtils.formatNumber(phoneNumber, phoneNumberE164, defaultCountryIso); + } else { + // This method was deprecated in API level 21, so it's only used on pre-L SDKs. + return PhoneNumberUtils.formatNumber(phoneNumber); + } } public static CharSequence createTtsSpannable(CharSequence phoneNumber) { - return PhoneNumberUtils.createTtsSpannable(phoneNumber); + if (CompatUtils.isMarshmallowCompatible()) { + return PhoneNumberUtils.createTtsSpannable(phoneNumber); + } else { + return createTtsSpannableInternal(phoneNumber); + } } public static TtsSpan createTtsSpan(String phoneNumber) { - return PhoneNumberUtils.createTtsSpan(phoneNumber); + if (CompatUtils.isMarshmallowCompatible()) { + return PhoneNumberUtils.createTtsSpan(phoneNumber); + } else if (CompatUtils.isLollipopCompatible()) { + return createTtsSpanLollipop(phoneNumber); + } else { + return null; + } + } + + /** Copied from {@link PhoneNumberUtils#createTtsSpannable} */ + private static CharSequence createTtsSpannableInternal(CharSequence phoneNumber) { + if (phoneNumber == null) { + return null; + } + Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber); + addTtsSpanInternal(spannable, 0, spannable.length()); + return spannable; + } + + /** Compat method for addTtsSpan, see {@link PhoneNumberUtils#addTtsSpan} */ + public static void addTtsSpan(Spannable s, int start, int endExclusive) { + if (CompatUtils.isMarshmallowCompatible()) { + PhoneNumberUtils.addTtsSpan(s, start, endExclusive); + } else { + addTtsSpanInternal(s, start, endExclusive); + } + } + + /** Copied from {@link PhoneNumberUtils#addTtsSpan} */ + private static void addTtsSpanInternal(Spannable s, int start, int endExclusive) { + s.setSpan( + createTtsSpan(s.subSequence(start, endExclusive).toString()), + start, + endExclusive, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + /** Copied from {@link PhoneNumberUtils#createTtsSpan} */ + private static TtsSpan createTtsSpanLollipop(String phoneNumberString) { + if (phoneNumberString == null) { + return null; + } + + // Parse the phone number + final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + PhoneNumber phoneNumber = null; + try { + // Don't supply a defaultRegion so this fails for non-international numbers because + // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already + // present + phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null); + } catch (NumberParseException ignored) { + } + + // Build a telephone tts span + final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder(); + if (phoneNumber == null) { + // Strip separators otherwise TalkBack will be silent + // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel) + builder.setNumberParts(splitAtNonNumerics(phoneNumberString)); + } else { + if (phoneNumber.hasCountryCode()) { + builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode())); + } + builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber())); + } + return builder.build(); + } + + /** + * Split a phone number using spaces, ignoring anything that is not a digit + * + * @param number A {@code CharSequence} before splitting, e.g., "+20(123)-456#" + * @return A {@code String} after splitting, e.g., "20 123 456". + */ + private static String splitAtNonNumerics(CharSequence number) { + StringBuilder sb = new StringBuilder(number.length()); + for (int i = 0; i < number.length(); i++) { + sb.append(PhoneNumberUtils.isISODigit(number.charAt(i)) ? number.charAt(i) : " "); + } + // It is very important to remove extra spaces. At time of writing, any leading or trailing + // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS + // span to be non-functional! + return sb.toString().replaceAll(" +", " ").trim(); } } diff --git a/java/com/android/contacts/common/compat/TelephonyManagerCompat.java b/java/com/android/contacts/common/compat/TelephonyManagerCompat.java index 4a16fb855..7e4803ca5 100644 --- a/java/com/android/contacts/common/compat/TelephonyManagerCompat.java +++ b/java/com/android/contacts/common/compat/TelephonyManagerCompat.java @@ -27,6 +27,7 @@ import android.telecom.PhoneAccountHandle; import android.telephony.TelephonyManager; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.compat.CompatUtils; import java.lang.reflect.InvocationTargetException; public class TelephonyManagerCompat { @@ -47,6 +48,31 @@ public class TelephonyManagerCompat { private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE"; + /** + * @param telephonyManager The telephony manager instance to use for method calls. + * @return true if the current device is "voice capable". + *

"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. + *

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. + */ + public static boolean isVoiceCapable(@Nullable TelephonyManager telephonyManager) { + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isLollipopMr1Compatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isVoiceCapable")) { + // isVoiceCapable was unhidden in L-MR1 + return telephonyManager.isVoiceCapable(); + } + final int phoneType = telephonyManager.getPhoneType(); + return phoneType == TelephonyManager.PHONE_TYPE_CDMA + || phoneType == TelephonyManager.PHONE_TYPE_GSM; + } + /** * Returns the number of phones available. Returns 1 for Single standby mode (Single SIM * functionality) Returns 2 for Dual standby mode.(Dual SIM functionality) @@ -59,7 +85,31 @@ public class TelephonyManagerCompat { if (telephonyManager == null) { return 1; } - return telephonyManager.getPhoneCount(); + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "getPhoneCount")) { + return telephonyManager.getPhoneCount(); + } + return 1; + } + + /** + * Returns the unique device ID of a subscription, for example, the IMEI for GSM and the MEID for + * CDMA phones. Return null if device ID is not available. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param telephonyManager The telephony manager instance to use for method calls. + * @param slotId of which deviceID is returned + */ + public static String getDeviceId(@Nullable TelephonyManager telephonyManager, int slotId) { + if (telephonyManager == null) { + return null; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "getDeviceId", Integer.class)) { + return telephonyManager.getDeviceId(slotId); + } + return null; } /** @@ -69,7 +119,14 @@ public class TelephonyManagerCompat { * @return {@code true} if the device supports TTY mode, and {@code false} otherwise. */ public static boolean isTtyModeSupported(@Nullable TelephonyManager telephonyManager) { - return telephonyManager != null && telephonyManager.isTtyModeSupported(); + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isTtyModeSupported")) { + return telephonyManager.isTtyModeSupported(); + } + return false; } /** @@ -81,7 +138,15 @@ public class TelephonyManagerCompat { */ public static boolean isHearingAidCompatibilitySupported( @Nullable TelephonyManager telephonyManager) { - return telephonyManager != null && telephonyManager.isHearingAidCompatibilitySupported(); + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELEPHONY_MANAGER_CLASS, "isHearingAidCompatibilitySupported")) { + return telephonyManager.isHearingAidCompatibilitySupported(); + } + return false; } /** diff --git a/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java index 43eae589d..5687f6fbf 100644 --- a/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java +++ b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java @@ -15,18 +15,274 @@ */ package com.android.contacts.common.compat.telecom; +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; import android.support.annotation.Nullable; +import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import com.android.dialer.compat.CompatUtils; +import java.util.ArrayList; +import java.util.List; /** Compatibility class for {@link android.telecom.TelecomManager}. */ public class TelecomManagerCompat { + public static final String TELECOM_MANAGER_CLASS = "android.telecom.TelecomManager"; + // TODO: remove once this is available in android.telecom.Call // b/33779976 public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; + /** + * Places a new outgoing call to the provided address using the system telecom service with the + * specified intent. + * + * @param activity {@link Activity} used to start another activity for the given intent + * @param telecomManager the {@link TelecomManager} used to place a call, if possible + * @param intent the intent for the call + */ + public static void placeCall( + @Nullable Activity activity, + @Nullable TelecomManager telecomManager, + @Nullable Intent intent) { + if (activity == null || telecomManager == null || intent == null) { + return; + } + if (CompatUtils.isMarshmallowCompatible()) { + telecomManager.placeCall(intent.getData(), intent.getExtras()); + return; + } + activity.startActivityForResult(intent, 0); + } + + /** + * Get the URI for running an adn query. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @param accountHandle The handle for the account to derive an adn query URI for or {@code null} + * to return a URI which will use the default account. + * @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount} for + * the the content retrieve. + */ + public static Uri getAdnUriForPhoneAccount( + @Nullable TelecomManager telecomManager, PhoneAccountHandle accountHandle) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getAdnUriForPhoneAccount", PhoneAccountHandle.class))) { + return telecomManager.getAdnUriForPhoneAccount(accountHandle); + } + return Uri.parse("content://icc/adn"); + } + + /** + * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone + * calls. The returned list includes only those accounts which have been explicitly enabled by the + * user. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @return A list of PhoneAccountHandle objects. + */ + public static List getCallCapablePhoneAccounts( + @Nullable TelecomManager telecomManager) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getCallCapablePhoneAccounts"))) { + return telecomManager.getCallCapablePhoneAccounts(); + } + return new ArrayList<>(); + } + + /** + * Used to determine the currently selected default dialer package. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @return package name for the default dialer package or null if no package has been selected as + * the default dialer. + */ + @Nullable + public static String getDefaultDialerPackage(@Nullable TelecomManager telecomManager) { + if (telecomManager != null && CompatUtils.isDefaultDialerCompatible()) { + return telecomManager.getDefaultDialerPackage(); + } + return null; + } + + /** + * Return the {@link PhoneAccount} which will be used to place outgoing calls to addresses with + * the specified {@code uriScheme}. This PhoneAccount will always be a member of the list which is + * returned from invoking {@link TelecomManager#getCallCapablePhoneAccounts()}. The specific + * account returned depends on the following priorities: + * + *

1. If the user-selected default PhoneAccount supports the specified scheme, it will be + * returned. 2. If there exists only one PhoneAccount that supports the specified scheme, it will + * be returned. + * + *

If no PhoneAccount fits the criteria above, this method will return {@code null}. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @param uriScheme The URI scheme. + * @return The {@link PhoneAccountHandle} corresponding to the account to be used. + */ + @Nullable + public static PhoneAccountHandle getDefaultOutgoingPhoneAccount( + @Nullable TelecomManager telecomManager, @Nullable String uriScheme) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getDefaultOutgoingPhoneAccount", String.class))) { + return telecomManager.getDefaultOutgoingPhoneAccount(uriScheme); + } + return null; + } + + /** + * Return the line 1 phone number for given phone account. + * + * @param telecomManager the {@link TelecomManager} to use in the event that {@link + * TelecomManager#getLine1Number(PhoneAccountHandle)} is available + * @param telephonyManager the {@link TelephonyManager} to use if TelecomManager#getLine1Number is + * unavailable + * @param phoneAccountHandle the phoneAccountHandle upon which to check the line one number + * @return the line one number + */ + @Nullable + public static String getLine1Number( + @Nullable TelecomManager telecomManager, + @Nullable TelephonyManager telephonyManager, + @Nullable PhoneAccountHandle phoneAccountHandle) { + if (telecomManager != null && CompatUtils.isMarshmallowCompatible()) { + return telecomManager.getLine1Number(phoneAccountHandle); + } + if (telephonyManager != null) { + return telephonyManager.getLine1Number(); + } + return null; + } + + /** + * Return whether a given phone number is the configured voicemail number for a particular phone + * account. + * + * @param telecomManager the {@link TelecomManager} to use for checking the number. + * @param accountHandle The handle for the account to check the voicemail number against + * @param number The number to look up. + */ + public static boolean isVoiceMailNumber( + @Nullable TelecomManager telecomManager, + @Nullable PhoneAccountHandle accountHandle, + @Nullable String number) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, + "isVoiceMailNumber", + PhoneAccountHandle.class, + String.class))) { + return telecomManager.isVoiceMailNumber(accountHandle, number); + } + return PhoneNumberUtils.isVoiceMailNumber(number); + } + + /** + * Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes + * resources which can be used in a user interface. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @param account The {@link PhoneAccountHandle}. + * @return The {@link PhoneAccount} object or null if it doesn't exist. + */ + @Nullable + public static PhoneAccount getPhoneAccount( + @Nullable TelecomManager telecomManager, @Nullable PhoneAccountHandle accountHandle) { + if (telecomManager != null + && (CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getPhoneAccount", PhoneAccountHandle.class))) { + return telecomManager.getPhoneAccount(accountHandle); + } + return null; + } + + /** + * Return the voicemail number for a given phone account. + * + * @param telecomManager The {@link TelecomManager} object to use for retrieving the voicemail + * number if accountHandle is specified. + * @param telephonyManager The {@link TelephonyManager} object to use for retrieving the voicemail + * number if accountHandle is null. + * @param accountHandle The handle for the phone account. + * @return The voicemail number for the phone account, and {@code null} if one has not been + * configured. + */ + @Nullable + public static String getVoiceMailNumber( + @Nullable TelecomManager telecomManager, + @Nullable TelephonyManager telephonyManager, + @Nullable PhoneAccountHandle accountHandle) { + if (telecomManager != null + && (CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getVoiceMailNumber", PhoneAccountHandle.class))) { + return telecomManager.getVoiceMailNumber(accountHandle); + } else if (telephonyManager != null) { + return telephonyManager.getVoiceMailNumber(); + } + return null; + } + + /** + * Processes the specified dial string as an MMI code. MMI codes are any sequence of characters + * entered into the dialpad that contain a "*" or "#". Some of these sequences launch special + * behavior through handled by Telephony. + * + * @param telecomManager The {@link TelecomManager} object to use for handling MMI. + * @param dialString The digits to dial. + * @return {@code true} if the digits were processed as an MMI code, {@code false} otherwise. + */ + public static boolean handleMmi( + @Nullable TelecomManager telecomManager, + @Nullable String dialString, + @Nullable PhoneAccountHandle accountHandle) { + if (telecomManager == null || TextUtils.isEmpty(dialString)) { + return false; + } + if (CompatUtils.isMarshmallowCompatible()) { + return telecomManager.handleMmi(dialString, accountHandle); + } + + Object handleMmiResult = + CompatUtils.invokeMethod( + telecomManager, + "handleMmi", + new Class[] {PhoneAccountHandle.class, String.class}, + new Object[] {accountHandle, dialString}); + if (handleMmiResult != null) { + return (boolean) handleMmiResult; + } + + return telecomManager.handleMmi(dialString); + } + + /** + * Silences the ringer if a ringing call exists. Noop if {@link TelecomManager#silenceRinger()} is + * unavailable. + * + * @param telecomManager the TelecomManager to use to silence the ringer. + */ + public static void silenceRinger(@Nullable TelecomManager telecomManager) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "silenceRinger"))) { + telecomManager.silenceRinger(); + } + } + /** * Returns the current SIM call manager. Apps must be prepared for this method to return null, * indicating that there currently exists no registered SIM call manager. @@ -36,7 +292,9 @@ public class TelecomManagerCompat { */ @Nullable public static PhoneAccountHandle getSimCallManager(TelecomManager telecomManager) { - if (telecomManager != null) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "getSimCallManager"))) { return telecomManager.getSimCallManager(); } return null; -- cgit v1.2.3