diff options
Diffstat (limited to 'java/com/android/contacts/common/compat')
9 files changed, 1018 insertions, 0 deletions
diff --git a/java/com/android/contacts/common/compat/CallCompat.java b/java/com/android/contacts/common/compat/CallCompat.java new file mode 100644 index 000000000..641f7b1bd --- /dev/null +++ b/java/com/android/contacts/common/compat/CallCompat.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.common.compat; + +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.support.annotation.NonNull; +import android.telecom.Call; + +/** Compatibility utilities for android.telecom.Call */ +public class CallCompat { + + public static boolean canPullExternalCall(@NonNull android.telecom.Call call) { + return VERSION.SDK_INT >= VERSION_CODES.N_MR1 + && ((call.getDetails().getCallCapabilities() & Details.CAPABILITY_CAN_PULL_CALL) + == Details.CAPABILITY_CAN_PULL_CALL); + } + + /** android.telecom.Call.Details */ + public static class Details { + + public static final int PROPERTY_IS_EXTERNAL_CALL = Call.Details.PROPERTY_IS_EXTERNAL_CALL; + public static final int PROPERTY_ENTERPRISE_CALL = Call.Details.PROPERTY_ENTERPRISE_CALL; + public static final int CAPABILITY_CAN_PULL_CALL = Call.Details.CAPABILITY_CAN_PULL_CALL; + public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = + Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO; + + public static final String EXTRA_ANSWERING_DROPS_FOREGROUND_CALL = + "android.telecom.extra.ANSWERING_DROPS_FG_CALL"; + } +} diff --git a/java/com/android/contacts/common/compat/CallableCompat.java b/java/com/android/contacts/common/compat/CallableCompat.java new file mode 100644 index 000000000..5e86f518e --- /dev/null +++ b/java/com/android/contacts/common/compat/CallableCompat.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.common.compat; + +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.provider.ContactsContract.CommonDataKinds.Callable; + +public class CallableCompat { + + // TODO: Use N APIs + private static final Uri ENTERPRISE_CONTENT_FILTER_URI = + Uri.withAppendedPath(Callable.CONTENT_URI, "filter_enterprise"); + + public static Uri getContentFilterUri() { + if (VERSION.SDK_INT >= VERSION_CODES.N) { + return ENTERPRISE_CONTENT_FILTER_URI; + } + return Callable.CONTENT_FILTER_URI; + } +} diff --git a/java/com/android/contacts/common/compat/ContactsCompat.java b/java/com/android/contacts/common/compat/ContactsCompat.java new file mode 100644 index 000000000..39d0b55d3 --- /dev/null +++ b/java/com/android/contacts/common/compat/ContactsCompat.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.common.compat; + +import android.net.Uri; +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 { + + // 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() {} + + public static Uri getContentUri() { + if (VERSION.SDK_INT >= VERSION_CODES.N) { + return ENTERPRISE_CONTENT_FILTER_URI; + } + 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/DirectoryCompat.java b/java/com/android/contacts/common/compat/DirectoryCompat.java new file mode 100644 index 000000000..85f4a4202 --- /dev/null +++ b/java/com/android/contacts/common/compat/DirectoryCompat.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.common.compat; + +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.provider.ContactsContract.Directory; + +public class DirectoryCompat { + + public static Uri getContentUri() { + if (VERSION.SDK_INT >= VERSION_CODES.N) { + return Directory.ENTERPRISE_CONTENT_URI; + } + return Directory.CONTENT_URI; + } + + public static boolean isInvisibleDirectory(long directoryId) { + if (VERSION.SDK_INT >= VERSION_CODES.N) { + return (directoryId == Directory.LOCAL_INVISIBLE + || directoryId == Directory.ENTERPRISE_LOCAL_INVISIBLE); + } + return directoryId == Directory.LOCAL_INVISIBLE; + } + + public static boolean isRemoteDirectoryId(long directoryId) { + if (VERSION.SDK_INT >= VERSION_CODES.N) { + return Directory.isRemoteDirectoryId(directoryId); + } + return !(directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE); + } + + public static boolean isEnterpriseDirectoryId(long directoryId) { + return VERSION.SDK_INT >= VERSION_CODES.N && Directory.isEnterpriseDirectoryId(directoryId); + } +} diff --git a/java/com/android/contacts/common/compat/PhoneAccountCompat.java b/java/com/android/contacts/common/compat/PhoneAccountCompat.java new file mode 100644 index 000000000..6a24ec033 --- /dev/null +++ b/java/com/android/contacts/common/compat/PhoneAccountCompat.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.contacts.common.compat; + +import android.content.Context; +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. + * + * @param phoneAccount the PhoneAccount from which to build the icon. + * @param context A {@code Context} to use for loading Drawables. + * @return An icon for this PhoneAccount, or null + */ + @Nullable + public static Drawable createIconDrawable( + @Nullable PhoneAccount phoneAccount, @Nullable Context context) { + if (phoneAccount == null || context == null) { + return null; + } + + 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 = 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/PhoneCompat.java b/java/com/android/contacts/common/compat/PhoneCompat.java new file mode 100644 index 000000000..31db7b537 --- /dev/null +++ b/java/com/android/contacts/common/compat/PhoneCompat.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.common.compat; + +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.provider.ContactsContract.CommonDataKinds.Phone; + +public class PhoneCompat { + + // TODO: Use N APIs + private static final Uri ENTERPRISE_CONTENT_FILTER_URI = + Uri.withAppendedPath(Phone.CONTENT_URI, "filter_enterprise"); + + public static Uri getContentFilterUri() { + if (VERSION.SDK_INT >= VERSION_CODES.N) { + return ENTERPRISE_CONTENT_FILTER_URI; + } + return Phone.CONTENT_FILTER_URI; + } +} diff --git a/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java new file mode 100644 index 000000000..960b340d8 --- /dev/null +++ b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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 + * added in API level 23. In this way, we could enable the corresponding functionality for pre-M + * devices. We need maintain this class and keep it synced with PhoneNumberUtils. Another thing to + * keep in mind is that we use com.google.i18n rather than com.android.i18n in here, so we need make + * sure the application behavior is preserved. + */ +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) { + if (CompatUtils.isMarshmallowCompatible()) { + return PhoneNumberUtils.createTtsSpannable(phoneNumber); + } else { + return createTtsSpannableInternal(phoneNumber); + } + } + + public static TtsSpan createTtsSpan(String 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 new file mode 100644 index 000000000..c8665af51 --- /dev/null +++ b/java/com/android/contacts/common/compat/TelephonyManagerCompat.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.common.compat; + +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.support.annotation.Nullable; +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 { + + // TODO: Use public API for these constants when available + public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE = + "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE"; + public static final String EVENT_HANDOVER_TO_WIFI_FAILED = + "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED"; + public static final String EVENT_CALL_REMOTELY_HELD = "android.telecom.event.CALL_REMOTELY_HELD"; + public static final String EVENT_CALL_REMOTELY_UNHELD = + "android.telecom.event.CALL_REMOTELY_UNHELD"; + + public static final String TELEPHONY_MANAGER_CLASS = "android.telephony.TelephonyManager"; + + /** + * @param telephonyManager The telephony manager instance to use for method calls. + * @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. + */ + 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) + * + * <p>Returns 1 if the method or telephonyManager is not available. + * + * @param telephonyManager The telephony manager instance to use for method calls. + */ + public static int getPhoneCount(@Nullable TelephonyManager telephonyManager) { + if (telephonyManager == null) { + return 1; + } + 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. + * + * <p>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; + } + + /** + * Whether the phone supports TTY mode. + * + * @param telephonyManager The telephony manager instance to use for method calls. + * @return {@code true} if the device supports TTY mode, and {@code false} otherwise. + */ + public static boolean isTtyModeSupported(@Nullable TelephonyManager telephonyManager) { + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isTtyModeSupported")) { + return telephonyManager.isTtyModeSupported(); + } + return false; + } + + /** + * Whether the phone supports hearing aid compatibility. + * + * @param telephonyManager The telephony manager instance to use for method calls. + * @return {@code true} if the device supports hearing aid compatibility, and {@code false} + * otherwise. + */ + public static boolean isHearingAidCompatibilitySupported( + @Nullable TelephonyManager telephonyManager) { + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELEPHONY_MANAGER_CLASS, "isHearingAidCompatibilitySupported")) { + return telephonyManager.isHearingAidCompatibilitySupported(); + } + return false; + } + + /** + * Returns the URI for the per-account voicemail ringtone set in Phone settings. + * + * @param telephonyManager The telephony manager instance to use for method calls. + * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to + * retrieve the voicemail ringtone. + * @return The URI for the ringtone to play when receiving a voicemail from a specific + * PhoneAccount. + */ + @Nullable + public static Uri getVoicemailRingtoneUri( + TelephonyManager telephonyManager, PhoneAccountHandle accountHandle) { + if (VERSION.SDK_INT < VERSION_CODES.N) { + return null; + } + return telephonyManager.getVoicemailRingtoneUri(accountHandle); + } + + /** + * Returns whether vibration is set for voicemail notification in Phone settings. + * + * @param telephonyManager The telephony manager instance to use for method calls. + * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to + * retrieve the voicemail vibration setting. + * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise. + */ + public static boolean isVoicemailVibrationEnabled( + TelephonyManager telephonyManager, PhoneAccountHandle accountHandle) { + return VERSION.SDK_INT < VERSION_CODES.N + || telephonyManager.isVoicemailVibrationEnabled(accountHandle); + } + + /** + * This method uses a new system API to enable or disable visual voicemail. TODO: restrict + * to N MR1, not needed in future SDK. + */ + public static void setVisualVoicemailEnabled( + TelephonyManager telephonyManager, PhoneAccountHandle handle, boolean enabled) { + if (VERSION.SDK_INT < VERSION_CODES.N_MR1) { + Assert.fail("setVisualVoicemailEnabled called on pre-NMR1"); + } + try { + TelephonyManager.class + .getMethod("setVisualVoicemailEnabled", PhoneAccountHandle.class, boolean.class) + .invoke(telephonyManager, handle, enabled); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + LogUtil.e("TelephonyManagerCompat.setVisualVoicemailEnabled", "failed", e); + } + } + + /** + * This method uses a new system API to check if visual voicemail is enabled TODO: restrict + * to N MR1, not needed in future SDK. + */ + public static boolean isVisualVoicemailEnabled( + TelephonyManager telephonyManager, PhoneAccountHandle handle) { + if (VERSION.SDK_INT < VERSION_CODES.N_MR1) { + Assert.fail("isVisualVoicemailEnabled called on pre-NMR1"); + } + try { + return (boolean) + TelephonyManager.class + .getMethod("isVisualVoicemailEnabled", PhoneAccountHandle.class) + .invoke(telephonyManager, handle); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + LogUtil.e("TelephonyManagerCompat.setVisualVoicemailEnabled", "failed", e); + } + return false; + } +} diff --git a/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java new file mode 100644 index 000000000..5687f6fbf --- /dev/null +++ b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +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<PhoneAccountHandle> 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: + * + * <p>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. + * + * <p>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. + * + * @param telecomManager the {@link TelecomManager} to use to fetch the SIM call manager. + * @return The phone account handle of the current sim call manager. + */ + @Nullable + public static PhoneAccountHandle getSimCallManager(TelecomManager telecomManager) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "getSimCallManager"))) { + return telecomManager.getSimCallManager(); + } + return null; + } +} |