Создайте телефонное приложение по умолчанию

Телефонное приложение по умолчанию позволяет платформе Android Telecom информировать ваше приложение о состоянии вызова с помощью диспетчера ролей и службы обработки вызовов, чтобы создать замену телефонному приложению по умолчанию на устройстве Android, реализовать API InCallService. Ваша реализация должна отвечать следующим требованиям:

Он не должен иметь возможности вызова и должен состоять исключительно из пользовательского интерфейса для вызова. Он должен обрабатывать все вызовы, о которых знает платформа Telecom, и не делать предположений о характере вызовов. Например, он не должен предполагать, что вызовы являются телефонными вызовами на основе SIM-карты, а также не должен реализовывать ограничения вызовов, основанные на какой-либо одной службе ConnectionService, например, применять ограничения телефонии для видеовызовов.

Приложение для звонков позволяет пользователям принимать или совершать аудио- или видеовызовы на своем устройстве. Приложения для вызовов используют для вызовов собственный пользовательский интерфейс вместо интерфейса приложения «Телефон» по умолчанию, как показано на следующем снимке экрана.

Пример приложения для звонков
Пример вызывающего приложения, использующего собственный пользовательский интерфейс

Платформа Android включает пакет android.telecom , который содержит классы, которые помогут вам создать приложение для звонков в соответствии с телекоммуникационной платформой. Создание вашего приложения в соответствии с телекоммуникационной структурой дает следующие преимущества:

  • Ваше приложение правильно взаимодействует со встроенной телекоммуникационной подсистемой устройства.
  • Ваше приложение правильно взаимодействует с другими приложениями для вызовов, которые также соответствуют этой платформе.
  • Платформа помогает вашему приложению управлять маршрутизацией аудио и видео.
  • Платформа помогает вашему приложению определить, имеют ли его вызовы фокус.

Манифестные декларации и разрешения

В манифесте приложения объявите, что ваше приложение использует разрешение MANAGE_OWN_CALLS , как показано в следующем примере:

<manifest … >
    <uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
</manifest>

Дополнительные сведения об объявлении разрешений приложения см. в разделе Разрешения .

Вы должны объявить службу, определяющую класс, реализующий класс ConnectionService в вашем приложении. Телекоммуникационная подсистема требует, чтобы служба объявила разрешение BIND_TELECOM_CONNECTION_SERVICE для возможности привязки к ней. В следующем примере показано, как объявить службу в манифесте приложения:

<service android:name="com.example.MyConnectionService"
    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.ConnectionService" />
    </intent-filter>
</service>

Дополнительные сведения об объявлении компонентов приложения, включая службы, см. в разделе Компоненты приложения .

Реализовать службу подключения

Ваше вызывающее приложение должно предоставить реализацию класса ConnectionService , к которому может привязаться телекоммуникационная подсистема. Ваша реализация ConnectionService должна переопределить следующие методы:

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

Подсистема связи вызывает этот метод в ответ на вызов вашего приложения placeCall(Uri, Bundle) для создания нового исходящего вызова. Ваше приложение возвращает новый экземпляр реализации класса Connection (дополнительную информацию см. в разделе Реализация соединения ) для представления нового исходящего вызова. Вы можете дополнительно настроить исходящее соединение, выполнив следующие действия:

  • Ваше приложение должно вызвать метод setConnectionProperties(int) с константой PROPERTY_SELF_MANAGED в качестве аргумента, чтобы указать, что соединение возникло из вызывающего приложения.
  • Если ваше приложение поддерживает удержание вызовов, вызовите метод setConnectionCapabilities(int) и установите в качестве аргумента значение битовой маски констант CAPABILITY_HOLD и CAPABILITY_SUPPORT_HOLD .
  • Чтобы задать имя вызывающего абонента, используйте метод setCallerDisplayName(String, int) , передав константу PRESENTATION_ALLOWED в качестве параметра int , чтобы указать, что имя вызывающего абонента должно быть показано.
  • Чтобы убедиться, что исходящий вызов имеет соответствующее состояние видео, вызовите метод setVideoState(int) объекта Connection и отправьте значение, возвращаемое методом getVideoState() объекта ConnectionRequest .
onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

Подсистема телекоммуникаций вызывает этот метод, когда ваше приложение вызывает метод placeCall(Uri, Bundle) и исходящий вызов не может быть выполнен. В ответ на эту ситуацию ваше приложение должно проинформировать пользователя (например, с помощью окна оповещения или всплывающего уведомления), что исходящий вызов не может быть выполнен. Ваше приложение может не иметь возможности совершить вызов, если есть текущий вызов службы экстренной помощи или если в другом приложении есть текущий вызов, который нельзя удержать перед совершением вызова.

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

Подсистема телекоммуникаций вызывает этот метод, когда ваше приложение вызывает метод addNewIncomingCall(PhoneAccountHandle, Bundle) чтобы сообщить системе о новом входящем вызове в вашем приложении. Ваше приложение возвращает новый экземпляр реализации Connection (дополнительную информацию см. в разделе Реализация соединения ) для представления нового входящего вызова. Вы можете дополнительно настроить входящее соединение, выполнив следующие действия:

  • Ваше приложение должно вызвать метод setConnectionProperties(int) с константой PROPERTY_SELF_MANAGED в качестве аргумента, чтобы указать, что соединение возникло из вызывающего приложения.
  • Если ваше приложение поддерживает удержание вызовов, вызовите метод setConnectionCapabilities(int) и установите в качестве аргумента значение битовой маски констант CAPABILITY_HOLD и CAPABILITY_SUPPORT_HOLD .
  • Чтобы задать имя вызывающего абонента, используйте метод setCallerDisplayName(String, int) , передав константу PRESENTATION_ALLOWED в качестве параметра int , чтобы указать, что имя вызывающего абонента должно быть показано.
  • Чтобы указать номер телефона или адрес входящего звонка, используйте метод setAddress(Uri, int) объекта Connection .
  • Чтобы убедиться, что исходящий вызов имеет соответствующее состояние видео, вызовите метод setVideoState(int) объекта Connection и отправьте значение, возвращаемое методом getVideoState() объекта ConnectionRequest .
onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

Подсистема телекоммуникаций вызывает этот метод, когда ваше приложение вызывает метод addNewIncomingCall(PhoneAccountHandle, Bundle) чтобы сообщить Telecom о новом входящем вызове, но входящий вызов не разрешен (дополнительную информацию см. в разделе ограничения вызовов ). Ваше приложение должно молча отклонять входящий вызов, при необходимости публикуя уведомление, информирующее пользователя о пропущенном вызове.

Реализуйте соединение

Ваше приложение должно создать подкласс Connection для представления вызовов в вашем приложении. В вашей реализации следует переопределить следующие методы:

onShowIncomingCallUi()

Подсистема телекоммуникаций вызывает этот метод, когда вы добавляете новый входящий вызов, и ваше приложение должно отображать пользовательский интерфейс входящего вызова.

onCallAudioStateChanged(CallAudioState)

Телекоммуникационная подсистема вызывает этот метод, чтобы сообщить вашему приложению, что текущий аудиомаршрут или режим изменились. Это вызывается в ответ на то, что ваше приложение меняет режим звука с помощью метода setAudioRoute(int) . Этот метод также может быть вызван, если система меняет маршрут аудио (например, при отключении Bluetooth-гарнитуры).

onHold()

Подсистема связи вызывает этот метод, когда хочет удержать вызов. В ответ на этот запрос ваше приложение должно удержать вызов, а затем вызвать метод setOnHold() , чтобы сообщить системе о том, что вызов удерживается. Подсистема связи может вызвать этот метод, когда служба вызова, например Android Auto, которая показывает ваш вызов, хочет передать запрос пользователя на удержание вызова. Подсистема связи также вызывает этот метод, если пользователь активирует вызов в другом приложении. Дополнительные сведения об услугах вызова см. в InCallService .

onUnhold()

Подсистема связи вызывает этот метод, когда хочет возобновить вызов, который был поставлен на удержание. Как только ваше приложение возобновит вызов, оно должно вызвать метод setActive() , чтобы сообщить системе, что вызов больше не находится на удержании. Подсистема связи может вызвать этот метод, когда служба вызова, например Android Auto, которая показывает ваш вызов, хочет передать запрос на возобновление вызова. Дополнительные сведения об услугах вызова см. в InCallService .

onAnswer()

Подсистема связи вызывает этот метод, чтобы сообщить вашему приложению, что на входящий вызов необходимо ответить. Как только ваше приложение ответит на вызов, оно должно вызвать метод setActive() , чтобы сообщить системе, что на вызов получен ответ. Подсистема связи может вызвать этот метод, когда ваше приложение добавляет новый входящий вызов, а в другом приложении уже есть текущий вызов, который нельзя поставить на удержание. В этих случаях подсистема связи отображает пользовательский интерфейс входящего вызова от имени вашего приложения. Платформа предоставляет перегруженный метод, который обеспечивает поддержку указания состояния видео, в котором можно ответить на вызов. Дополнительные сведения см. в onAnswer(int) .

onReject()

Подсистема связи вызывает этот метод, когда хочет отклонить входящий вызов. Как только ваше приложение отклонит вызов, оно должно вызвать setDisconnected(DisconnectCause) и указать REJECTED в качестве параметра. Затем ваше приложение должно вызвать метод destroy() , чтобы сообщить системе, что приложение обработало вызов. Подсистема связи вызывает этот метод, когда пользователь отклоняет входящий вызов из вашего приложения.

onDisconnect()

Подсистема связи вызывает этот метод, когда хочет отключить вызов. После завершения вызова ваше приложение должно вызвать метод setDisconnected(DisconnectCause) и указать LOCAL в качестве параметра, чтобы указать, что запрос пользователя привел к отключению вызова. Затем ваше приложение должно вызвать метод destroy() , чтобы сообщить подсистеме связи о том, что приложение обработало вызов. Система может вызвать этот метод, когда пользователь отключил вызов через другую службу вызова, например Android Auto. Система также вызывает этот метод, когда ваш вызов необходимо отключить, чтобы разрешить другой вызов, например, если пользователь хочет сделать экстренный вызов. Дополнительные сведения об услугах вызова см. в InCallService .

Обработка распространенных сценариев вызовов

Использование API ConnectionService в потоке вызовов предполагает взаимодействие с другими классами пакета android.telecom . В следующих разделах описываются распространенные сценарии вызовов и то, как ваше приложение должно использовать API для их обработки.

Отвечать на входящие звонки

Поток обработки входящих вызовов меняется независимо от того, есть вызовы в других приложениях или нет. Причина разницы в потоках заключается в том, что телекоммуникационная платформа должна устанавливать некоторые ограничения при наличии активных вызовов в других приложениях, чтобы обеспечить стабильную среду для всех вызывающих приложений на устройстве. Дополнительные сведения см. в разделе Ограничения вызовов .

Нет активных звонков в других приложениях

Чтобы ответить на входящие вызовы, когда в других приложениях нет активных вызовов, выполните следующие действия:

  1. Ваше приложение получает новый входящий вызов, используя свои обычные механизмы.
  2. Используйте метод addNewIncomingCall(PhoneAccountHandle, Bundle) чтобы сообщить подсистеме связи о новом входящем вызове.
  3. Подсистема телекоммуникаций привязывается к реализации ConnectionService вашего приложения и запрашивает новый экземпляр класса Connection , представляющий новый входящий вызов, с помощью метода onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) .
  4. Подсистема связи сообщает вашему приложению, что оно должно показать пользовательский интерфейс входящего вызова с помощью метода onShowIncomingCallUi() .
  5. Ваше приложение отображает входящий пользовательский интерфейс с помощью уведомления с соответствующим намерением полноэкранного режима. Дополнительные сведения см. в onShowIncomingCallUi() .
  6. Вызовите метод setActive() , если пользователь принимает входящий вызов, или setDisconnected(DisconnectCause) указав REJECTED в качестве параметра, за которым следует вызов метода destroy() , если пользователь отклоняет входящий вызов.

Активные вызовы в других приложениях, которые нельзя поставить на удержание

Чтобы ответить на входящие вызовы, когда в других приложениях есть активные вызовы, которые нельзя поставить на удержание, выполните следующие действия:

  1. Ваше приложение получает новый входящий вызов, используя свои обычные механизмы.
  2. Используйте метод addNewIncomingCall(PhoneAccountHandle, Bundle) чтобы сообщить подсистеме связи о новом входящем вызове.
  3. Подсистема телекоммуникаций привязывается к реализации ConnectionService вашего приложения и запрашивает новый экземпляр объекта Connection , представляющего новый входящий вызов, с помощью метода onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) .
  4. Подсистема связи отображает пользовательский интерфейс входящего вызова для вашего входящего вызова.
  5. Если пользователь принимает вызов, подсистема связи вызывает метод onAnswer() . Вам следует вызвать метод setActive() чтобы указать подсистеме связи, что вызов теперь подключен.
  6. Если пользователь отклоняет вызов, подсистема связи вызывает метод onReject() . Вам следует вызвать метод setDisconnected(DisconnectCause) , указав REJECTED в качестве параметра, а затем вызвать метод destroy() .

Совершать исходящие звонки

Поток исходящего вызова включает в себя обработку возможности того, что вызов не может быть выполнен из-за ограничений, налагаемых телекоммуникационной структурой. Дополнительные сведения см. в разделе Ограничения вызовов .

Чтобы совершить исходящий вызов, выполните следующие действия:

  1. Пользователь инициирует исходящий звонок в вашем приложении.
  2. Используйте метод placeCall(Uri, Bundle) , чтобы сообщить подсистеме связи о новом исходящем вызове. Примите во внимание следующие параметры метода:
    • Параметр Uri представляет адрес, по которому осуществляется вызов. Для обычных телефонных номеров используйте схему URI tel: :.
    • Параметр Bundle позволяет вам предоставить информацию о вашем вызывающем приложении, добавив объект PhoneAccountHandle вашего приложения в дополнительный EXTRA_PHONE_ACCOUNT_HANDLE . Ваше приложение должно предоставлять объект PhoneAccountHandle для каждого исходящего вызова.
    • Параметр Bundle также позволяет указать, включает ли исходящий вызов видео, указав значение STATE_BIDIRECTIONAL в дополнительном параметре EXTRA_START_CALL_WITH_VIDEO_STATE . Учтите, что по умолчанию подсистема связи направляет видеовызовы на громкую связь.
  3. Телекоммуникационная подсистема привязывается к реализации ConnectionService вашего приложения.
  4. Если ваше приложение не может выполнить исходящий вызов, подсистема телекоммуникаций вызывает метод onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) чтобы сообщить вашему приложению, что вызов не может быть выполнен в текущий момент. Ваше приложение должно сообщить пользователю, что звонок не может быть совершен.
  5. Если ваше приложение может выполнить исходящий вызов, подсистема связи вызывает метод onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest) . Ваше приложение должно вернуть экземпляр класса Connection для представления нового исходящего вызова. Дополнительные сведения о свойствах, которые следует задать в соединении, см. в разделе Реализация службы подключения .
  6. Когда исходящий вызов подключен, вызовите метод setActive() , чтобы сообщить подсистеме связи о том, что вызов активен.

Завершить вызов

Чтобы завершить вызов, выполните следующие действия:

  1. Вызовите setDisconnected(DisconnectCause) , отправив LOCAL в качестве параметра, если пользователь завершил вызов, или отправьте REMOTE в качестве параметра, если другая сторона завершила вызов.
  2. Вызовите метод destroy() .

Ограничения вызова

Чтобы обеспечить единообразие и простоту звонков для ваших пользователей, телекоммуникационная платформа налагает некоторые ограничения на управление вызовами на устройстве. Например, предположим, что пользователь установил два вызывающих приложения, которые реализуют самоуправляемый API ConnectionService : FooTalk и BarTalk. В этом случае применяются следующие ограничения:

  • На устройствах, работающих на уровне API 27 или ниже, только одно приложение может поддерживать текущий вызов в любой момент времени. Это ограничение означает, что, пока у пользователя есть текущий вызов с помощью приложения FooTalk, приложение BarTalk не может инициировать или принять новый вызов.

    На устройствах, работающих на уровне API 28 или выше, если и FooTalk, и BarTalk объявляют разрешения CAPABILITY_SUPPORT_HOLD и CAPABILITY_HOLD , то пользователь может поддерживать более одного текущего вызова, переключаясь между приложениями, чтобы инициировать или ответить на другой вызов.

  • Если пользователь участвует в обычных управляемых вызовах (например, с помощью встроенного приложения «Телефон» или «Дозвонщик»), он не может участвовать в вызовах, исходящих из вызывающих приложений. Это означает, что если пользователь совершает обычный вызов через своего оператора мобильной связи, он не может одновременно участвовать в вызове FooTalk или BarTalk.

  • Подсистема связи отключает вызовы вашего приложения, если пользователь набирает экстренный вызов.

  • Ваше приложение не может принимать или совершать вызовы, пока пользователь выполняет экстренный вызов.

  • Если в другом вызывающем приложении происходит текущий вызов, когда ваше приложение получает входящий вызов, ответ на входящий вызов завершает все текущие вызовы в другом приложении. Ваше приложение не должно отображать обычный пользовательский интерфейс входящего вызова. Платформа телекоммуникаций отображает пользовательский интерфейс входящего вызова и информирует пользователя о том, что ответ на новый вызов завершит его текущий вызов(ы). Это означает, что если пользователь находится в вызове FooTalk и приложение BarTalk получает входящий вызов, телекоммуникационная платформа информирует пользователя о том, что у него есть новый входящий вызов BarTalk и что ответ на вызов BarTalk завершит его вызов FooTalk.

Становление телефонным приложением по умолчанию

Приложение для набора номера/телефона по умолчанию предоставляет пользовательский интерфейс во время разговора, пока устройство находится в состоянии вызова. Он также предоставляет пользователю возможность инициировать вызовы и просматривать историю вызовов на своем устройстве. В комплект поставки устройства входит стандартное приложение для набора номера/телефона, предоставляемое системой. Пользователь может выбрать одно приложение, которое возьмет на себя эту роль от системного приложения. Приложение, которое желает выполнить эту роль, использует RoleManager , чтобы запросить выполнение роли RoleManager.ROLE_DIALER .

Приложение телефона по умолчанию предоставляет пользовательский интерфейс, пока устройство находится в режиме вызова и не находится в режиме автомобиля (т. е. UiModeManager#getCurrentModeType() не является Configuration.UI_MODE_TYPE_CAR ).

Чтобы выполнить роль RoleManager.ROLE_DIALER , приложение должно соответствовать ряду требований:

  • Он должен обрабатывать намерение Intent#ACTION_DIAL . Это означает, что приложение должно предоставлять пользовательский интерфейс панели набора номера, чтобы пользователь мог инициировать исходящие вызовы.
  • Он должен полностью реализовать API InCallService и предоставлять как пользовательский интерфейс входящего вызова, так и пользовательский интерфейс текущего вызова.

Примечание. Если приложение, заполняющее RoleManager.ROLE_DIALER возвращает null InCallService во время привязки, платформа Telecom автоматически вернется к использованию приложения дозвона, предварительно загруженного на устройство. Система отобразит пользователю уведомление, сообщающее ему, что его звонок был продолжен с использованием предварительно загруженного приложения для набора номера. Ваше приложение никогда не должно возвращать null привязку; это означает, что он не соответствует требованиям RoleManager.ROLE_DIALER .

Примечание. Если ваше приложение заполняет RoleManager.ROLE_DIALER и вносит изменения во время выполнения, из-за которых оно больше не соответствует требованиям этой роли, RoleManager автоматически удалит ваше приложение из роли и закроет ваше приложение. Например, если вы используете PackageManager.setComponentEnabledSetting(ComponentName, int, int) для программного отключения InCallService , объявленного вашим приложением в своем манифесте, ваше приложение больше не будет выполнять требования, ожидаемые от RoleManager.ROLE_DIALER .

Предварительно загруженный номеронабиратель ВСЕГДА будет использоваться, когда пользователь совершает экстренный вызов, даже если ваше приложение выполняет роль RoleManager.ROLE_DIALER . Чтобы обеспечить оптимальную работу при совершении экстренного вызова, номеронабиратель по умолчанию должен ВСЕГДА использовать TelecomManager.placeCall(Uri, Bundle) для совершения вызовов (включая экстренные вызовы). Это гарантирует, что платформа сможет проверить, поступил ли запрос от программы дозвона по умолчанию. Если приложение дозвона без предварительной загрузки использует Intent#ACTION_CALL для совершения экстренного вызова, он будет передан предварительно загруженному приложению дозвона с использованием Intent#ACTION_DIAL для подтверждения; это неоптимальный пользовательский опыт.

Ниже приведен пример регистрации манифеста для InCallService . Метаданные TelecomManager#METADATA_IN_CALL_SERVICE_UI указывают, что эта конкретная реализация InCallService предназначена для замены встроенного пользовательского интерфейса при вызове. Метаданные TelecomManager#METADATA_IN_CALL_SERVICE_RINGING указывают, что этот InCallService будет воспроизводить мелодию звонка для входящих вызовов. Ниже приведена дополнительная информация об отображении пользовательского интерфейса входящего вызова и воспроизведении мелодии звонка в вашем приложении.

 <service android:name="your.package.YourInCallServiceImplementation"
          android:permission="android.permission.BIND_INCALL_SERVICE"
          android:exported="true">
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
          android:value="true" />
      <intent-filter>
          <action android:name="android.telecom.InCallService"/>
      </intent-filter>
 </service>

Примечание. НЕ следует отмечать InCallService атрибутом android:exported="false" ; это может привести к сбою привязки к вашей реализации во время вызовов.

Помимо реализации API InCallService , вы также должны объявить в своем манифесте действие, которое обрабатывает намерение Intent#ACTION_DIAL . Пример ниже иллюстрирует, как это делается:

 <activity android:name="your.package.YourDialerActivity"
           android:label="@string/yourDialerActivityLabel">
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
           <data android:scheme="tel" />
      </intent-filter>
 </activity>

Когда пользователь устанавливает ваше приложение и запускает его в первый раз, вы должны использовать RoleManager , чтобы предложить пользователю узнать, хотят ли они, чтобы ваше приложение было новым телефонным приложением по умолчанию.

В приведенном ниже коде показано, как ваше приложение может запросить возможность стать приложением для телефона/звонилки по умолчанию:

 private static final int REQUEST_ID = 1;

 public void requestRole() {
     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
     Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
     startActivityForResult(intent, REQUEST_ID);
 }

 public void onActivityResult(int requestCode, int resultCode, Intent data) {
     if (requestCode == REQUEST_ID) {
         if (resultCode == android.app.Activity.RESULT_OK) {
             // Your app is now the default dialer app
         } else {
             // Your app is not the default dialer app
         }
     }
 }

Доступ к InCallService для носимых устройств

    Если ваше приложение является сторонним приложением-компаньоном и хочет получить доступ к API InCallService, ваше приложение может сделать следующее:

    1. Объявите разрешение MANAGE_ONGOING_CALLS в своем манифесте.
    2. Связывайтесь с физическим носимым устройством через API CompanionDeviceManager в качестве сопутствующего приложения. См.: https://developer.android.com/guide/topics/connectivity/companion-device-pairing.
    3. Реализуйте этот InCallService с разрешением BIND_INCALL_SERVICE.

Отображение уведомления о входящем вызове

Когда ваше приложение получает новый входящий вызов через InCallService#onCallAdded(Call) , оно отвечает за отображение пользовательского интерфейса входящего вызова. Это следует сделать с помощью API-интерфейсов NotificationManager для публикации нового уведомления о входящем вызове.

Если ваше приложение объявляет метаданные TelecomManager#METADATA_IN_CALL_SERVICE_RINGING , оно отвечает за воспроизведение мелодии звонка для входящих вызовов. Ваше приложение должно создать NotificationChannel , в котором указывается желаемая мелодия звонка. Например:

 NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
          NotificationManager.IMPORTANCE_MAX);
 // other channel setup stuff goes here.

 // We'll use the default system ringtone for our incoming call notification channel.  You can
 // use your own audio resource here.
 Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
 channel.setSound(ringtoneUri, new AudioAttributes.Builder()
          // Setting the AudioAttributes is important as it identifies the purpose of your
          // notification sound.
          .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
      .build());

 NotificationManager mgr = getSystemService(NotificationManager.class);
 mgr.createNotificationChannel(channel);

Когда ваше приложение получает новый входящий вызов, оно создает Notification для входящего вызова и связывает его с вашим каналом уведомления о входящем вызове. Вы можете указать PendingIntent в уведомлении, которое запустит полноэкранный пользовательский интерфейс входящего вызова. Платформа диспетчера уведомлений будет отображать ваше уведомление как уведомление, если пользователь активно использует телефон. Когда пользователь не использует телефон, вместо него используется полноэкранный пользовательский интерфейс входящего вызова. Например:

 // Create an intent which triggers your fullscreen incoming call user interface.
 Intent intent = new Intent(Intent.ACTION_MAIN, null);
 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
 intent.setClass(context, YourIncomingCallActivity.class);
 PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
 // Build the notification as an ongoing high priority item; this ensures it will show as
 // a heads up notification which slides down over top of the current content.
 final Notification.Builder builder = new Notification.Builder(context);
 builder.setOngoing(true);
 builder.setPriority(Notification.PRIORITY_HIGH);
 // Set notification content intent to take user to the fullscreen UI if user taps on the
 // notification body.
 builder.setContentIntent(pendingIntent);
 // Set full screen intent to trigger display of the fullscreen UI when the notification
 // manager deems it appropriate.
 builder.setFullScreenIntent(pendingIntent, true);
 // Setup notification content.
 builder.setSmallIcon( yourIconResourceId );
 builder.setContentTitle("Your notification title");
 builder.setContentText("Your notification content.");
 // Use builder.addAction(..) to add buttons to answer or reject the call.
 NotificationManager notificationManager = mContext.getSystemService(
     NotificationManager.class);
 notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
```