Фреймворк Android Telecom (также известный как Telecom) управляет аудио- и видеовызовами на устройствах Android. К ним относятся звонки по SIM-карте, например вызовы, в которых используется фреймворк телефонии, и VoIP-звонки, в которых реализована библиотека Jetpack Core-Telecom.
Основные компоненты, которыми управляет Telecom, – это ConnectionService и InCallService.
Реализация ConnectionService основана на таких технологиях, как ТфОП, для подключения вызовов к другим абонентам. Самая распространенная реализация ConnectionService на телефоне – это телефонная ConnectionService. Оно соединяет звонки оператора.
Реализация InCallService предоставляет пользовательский интерфейс для звонков, управляемых Telecom, и позволяет пользователю управлять этими звонками и взаимодействовать с ними. Самый распространенный пример InCallService – приложение "Телефон", которое поставляется вместе с устройством.
Телекоммуникационная компания выступает в качестве коммутатора. Он направляет вызовы, которые предоставляют реализации ConnectionService, в пользовательские интерфейсы вызовов, которые предоставляют реализации InCallService.
Вот почему стоит внедрить Telecom API:
- Для замены системного приложения "Телефон".
- Для интеграции решения для звонков в Android.
Создание приложения "Телефон" для замены существующего
Чтобы создать замену для приложения "Телефон" по умолчанию на устройстве Android, реализуйте API InCallService. Реализация должна соответствовать следующим требованиям:
- Оно не должно иметь никаких функций вызова и должно состоять исключительно из пользовательского интерфейса для вызова.
- Оно должно обрабатывать все вызовы, известные фреймворку Telecom, и не делать предположений об их характере. Например, не следует предполагать, что вызовы осуществляются через SIM-карту, или применять ограничения, связанные с определенным классом
ConnectionService(например, ограничения для телефонных звонков не должны распространяться на видеовызовы).
Дополнительную информацию вы найдете в статье InCallService.
Интегрируйте решение для звонков
Чтобы интегрировать решение для звонков в Android, вы можете:
Используйте библиотеку Core-Telecom Jetpack с самостоятельным управлением. Этот вариант идеально подходит для разработчиков отдельных приложений для звонков, которые не хотят, чтобы их звонки показывались в приложении "Телефон" по умолчанию, а другие звонки – в их пользовательском интерфейсе.
Интеграция с библиотекой
Core-TelecomJetpack позволяет приложению взаимодействовать не только с системной телефонией на устройстве, но и с другими отдельными приложениями для звонков, интегрированными с Telecom. БиблиотекаCore-Telecomтакже управляет маршрутизацией и фокусом аудио. Подробнее о том, как создать приложение для звонков…Реализовать управляемый ConnectionService API. Этот вариант упрощает разработку решения для звонков, которое использует существующее приложение для телефона на устройстве, чтобы предоставить пользовательский интерфейс для звонков. Примеры: сторонние реализации SIP- и VoIP-телефонии. Подробную информацию можно найти в статье
getDefaultDialerPackage().ConnectionService– это лишь средство связи. У него нет пользовательского интерфейса.Реализуйте API InCallService и ConnectionService. Этот вариант идеально подходит, если вы хотите создать собственное решение для звонков на основе
ConnectionServiceс собственным пользовательским интерфейсом, а также показывать все остальные звонки Android в том же пользовательском интерфейсе. При таком подходе реализацияInCallServiceне должна делать никаких предположений об источниках вызовов, которые она отображает. Кроме того, ваша реализацияConnectionServiceдолжна работать, даже если в качестве приложения "Телефон" по умолчанию не выбрано ваше приложениеInCallService.
Информация о входящих вызовах
На устройствах с Android 10 (уровень API 29) или более поздней версией ОС приложение может определять звонки с номеров, которых нет в адресной книге пользователя, как потенциальный спам. Пользователи могут настроить автоматическое отклонение спам-звонков. Чтобы пользователи знали, почему они пропустили звонок, информация о заблокированных вызовах сохраняется в журнале звонков. При использовании Android 10 API не требуется получать разрешение READ_CALL_LOG от пользователя, чтобы предоставлять функции фильтра звонков и идентификатора абонента.
Вы используете реализацию CallScreeningService для фильтрации звонков. Вызовите функцию onScreenCall() для всех новых входящих или исходящих вызовов, если номера нет в списке контактов пользователя. Информацию о вызове можно найти в объекте Call.Details. В частности, функция getCallerNumberVerificationStatus() включает информацию от оператора связи о другом номере.
Если статус проверки не подтвержден, то, скорее всего, звонок поступает с недействительного номера или является спам-звонком.
Kotlin
class ScreeningService : CallScreeningService() { // This function is called when an ingoing or outgoing call // is from a number not in the user's contacts list override fun onScreenCall(callDetails: Call.Details) { // Can check the direction of the call val isIncoming = callDetails.callDirection == Call.Details.DIRECTION_INCOMING if (isIncoming) { // the handle (e.g. phone number) that the Call is currently connected to val handle: Uri = callDetails.handle // determine if you want to allow or reject the call when (callDetails.callerNumberVerificationStatus) { Connection.VERIFICATION_STATUS_FAILED -> { // Network verification failed, likely an invalid/spam call. } Connection.VERIFICATION_STATUS_PASSED -> { // Network verification passed, likely a valid call. } else -> { // Network could not perform verification. // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED. } } } } }
Java
class ScreeningService extends CallScreeningService { @Override public void onScreenCall(@NonNull Call.Details callDetails) { boolean isIncoming = callDetails.getCallDirection() == Call.Details.DIRECTION_INCOMING; if (isIncoming) { Uri handle = callDetails.getHandle(); switch (callDetails.getCallerNumberVerificationStatus()) { case Connection.VERIFICATION_STATUS_FAILED: // Network verification failed, likely an invalid/spam call. break; case Connection.VERIFICATION_STATUS_PASSED: // Network verification passed, likely a valid call. break; default: // Network could not perform verification. // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED } } } }
Задайте функцию onScreenCall() для вызова respondToCall(), чтобы указать системе, как реагировать на новый вызов. Эта функция принимает параметр CallResponse, который позволяет заблокировать вызов, отклонить его, как если бы это сделал пользователь, или отключить звук. Вы также можете указать, что звонок не нужно добавлять в список вызовов на устройстве.
Kotlin
// Tell the system how to respond to the incoming call // and if it should notify the user of the call. val response = CallResponse.Builder() // Sets whether the incoming call should be blocked. .setDisallowCall(false) // Sets whether the incoming call should be rejected as if the user did so manually. .setRejectCall(false) // Sets whether ringing should be silenced for the incoming call. .setSilenceCall(false) // Sets whether the incoming call should not be displayed in the call log. .setSkipCallLog(false) // Sets whether a missed call notification should not be shown for the incoming call. .setSkipNotification(false) .build() // Call this function to provide your screening response. respondToCall(callDetails, response)
Java
// Tell the system how to respond to the incoming call // and if it should notify the user of the call. CallResponse.Builder response = new CallResponse.Builder(); // Sets whether the incoming call should be blocked. response.setDisallowCall(false); // Sets whether the incoming call should be rejected as if the user did so manually. response.setRejectCall(false); // Sets whether ringing should be silenced for the incoming call. response.setSilenceCall(false); // Sets whether the incoming call should not be displayed in the call log. response.setSkipCallLog(false); // Sets whether a missed call notification should not be shown for the incoming call. response.setSkipNotification(false); // Call this function to provide your screening response. respondToCall(callDetails, response.build());
Чтобы система могла правильно запускать реализацию CallScreeningService, ее нужно зарегистрировать в файле манифеста с подходящим фильтром интентов и разрешением.
<service
android:name=".ScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService" />
</intent-filter>
</service>
Как перенаправить вызов
Устройства с Android 10 или более поздней версией ОС обрабатывают намерения вызова иначе, чем устройства с Android 9 или более ранней версией ОС. В Android 10 и более поздних версиях трансляция ACTION_NEW_OUTGOING_CALL устарела и заменена на API CallRedirectionService. CallRedirectionService предоставляет интерфейсы, которые позволяют изменять исходящие вызовы, выполняемые платформой Android. Например, сторонние приложения могут отменять звонки и перенаправлять их через VoIP.
Kotlin
class RedirectionService : CallRedirectionService() { override fun onPlaceCall( handle: Uri, initialPhoneAccount: PhoneAccountHandle, allowInteractiveResponse: Boolean ) { // Determine if the call should proceed, be redirected, or cancelled. val callShouldProceed = true val callShouldRedirect = false when { callShouldProceed -> { placeCallUnmodified() } callShouldRedirect -> { // Update the URI to point to a different phone number or modify the // PhoneAccountHandle and redirect. redirectCall(handle, initialPhoneAccount, true) } else -> { cancelCall() } } } }
Java
class RedirectionService extends CallRedirectionService { @Override public void onPlaceCall( @NonNull Uri handle, @NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse ) { // Determine if the call should proceed, be redirected, or cancelled. // Your app should implement this logic to determine the redirection. boolean callShouldProceed = true; boolean callShouldRedirect = false; if (callShouldProceed) { placeCallUnmodified(); } else if (callShouldRedirect) { // Update the URI to point to a different phone number or modify the // PhoneAccountHandle and redirect. redirectCall(handle, initialPhoneAccount, true); } else { cancelCall(); } } }
Чтобы система могла правильно запустить сервис, его нужно зарегистрировать в манифесте.
<service
android:name=".RedirectionService"
android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallRedirectionService"/>
</intent-filter>
</service>
Чтобы использовать сервис переадресации, приложение должно запросить роль переадресации вызовов у RoleManager. Пользователю будет предложено разрешить вашему приложению обрабатывать переадресацию вызовов. Если приложение не получит эту роль, ваш сервис переадресации не будет использоваться.
При запуске приложения проверяйте, назначена ли ему эта роль, чтобы при необходимости запросить ее. Вы запускаете намерение, созданное RoleManager, поэтому не забудьте переопределить функцию onActivityResult(), чтобы обработать выбор пользователя.
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Tell the system that you want your app to handle call redirects. This // is done by using the RoleManager to register your app to handle redirects. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { val roleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager // Check if the app needs to register call redirection role. val shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) && !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION) if (shouldRequestRole) { val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION) startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE) } } } companion object { private const val REDIRECT_ROLE_REQUEST_CODE = 1 } }
Java
class MainActivity extends AppCompatActivity { private static final int REDIRECT_ROLE_REQUEST_CODE = 0; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Tell the system that you want your app to handle call redirects. This // is done by using the RoleManager to register your app to handle redirects. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE); // Check if the app needs to register call redirection role. boolean shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) && !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION); if (shouldRequestRole) { Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION); startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE); } } } }