Разберёмся с уведомлениями о входящих звонках: начнём с самых простых и минималистичных, а закончим полноэкранными уведомлениями с внесистемным дизайном. Приступим!
Создание канала (api 26+)
Начиная с Android 8.0, у каждого уведомления должен быть указан канал уведомлений, которому оно принадлежит. До этой версии системы пользователь мог либо разрешить, либо запретить приложению показывать уведомления, не имея возможности выключить лишь определённую категорию, что было не очень удобно. С каналами же пользователь может выключить назойливые уведомления от приложения (реклама, лишние напоминания), оставив при этом только нужные ему (новые сообщения, звонки и так далее).
Если мы не укажем ID канала (воспользовавшись Deprecated билдером) или канал с таким ID не был нами создан, то уведомление попросту не отобразится на версиях Android выше 8.
Нам потребуется библиотека androidx.core (скорее всего, она уже у вас подключена). Мы пишем на Kotlin, поэтому используем версию библиотеки для этого языка:
Вся работа с уведомлениями происходит через системный сервис `NotificationManager`. Для обратной совместимости всегда лучше использовать `*Compat` версию android-классов при их наличии, поэтому мы воспользуемся `NotificationManagerCompat`. Для получения инстанса:
Создаём наш канал. Каналу можно задать великое множество параметров, например общий звук для уведомлений и паттерн вибрации. Мы зададим только основное, а полный список можно найти здесь.
Отображение уведомления
Чудесно, теперь мы можем приступить к созданию самого уведомления, начнём с самого простого примера:
Пока что мы только создали своего рода “описание” уведомления, но оно ещё не показано пользователю. Для его отображения снова обратимся к менеджеру:
`INCOMING_CALL_NOTIFICATION_ID` — это идентификатор уведомления, с помощью которого можно найти уже отображенное уведомление и взаимодействовать с ним.
Например, пользователь долго не отвечал на звонок, звонящему надоело ждать, и он отменил вызов. Тогда мы можем отменить уведомление:
Или, в случае приложения для конференций, если к вызывающему присоединилось несколько человек, мы можем обновить наше уведомление. Для этого достаточно создать новое уведомление и в вызове `notify` передать тот же id уведомления -- тогда старое уведомление просто будет обновлено с данными, без анимации появления нового уведомления. Для этого мы можем переиспользовать старый `notificationBuilder`, просто заменив в нём изменившуюся часть:
Действия по нажатию и кнопки
Простое уведомление о входящем звонке, после которого пользователю надо самому найти наше приложение и принять или отклонить звонок -- не очень полезная вещь. К счастью, мы можем добавить в наше уведомление кнопки действий!
Для этого, при создании уведомления мы добавить один или несколько `action`. Их создание выглядит примерно так:
Погодите-ка, что ещё за `PendingIntent`? Это очень широкая тема, достойная отдельной статьи, но упрощённо -- это описание того, как запустить элемент нашего приложения (например, activity или service). В самом простом виде он создаётся вот так:
Соответственно, нам необходимо обработать это действие в самом `acitivity`
Для этого в `onCreate()` (и в `onNewIntent()`, если вы используете флаг `FLAG_ACTIVITY_SINGLE_TOP` для своего activity) получаем `action` из `intent` и производим действие:
Теперь, когда для нашего действия всё готово, мы можем добавить его к нашему уведомлению через `Builder`
Помимо кнопок, мы можем назначить действие по нажатию на само уведомление, вне кнопок. Переход на экран входящего звонка кажется самым удачным решением -- для этого повторяем все шаги по созданию action, но вместо `ACTION_ACCEPT_CALL` используем другой id действия, а в `MainActivity.onCreate()` обрабатываем этот `action` навигацией
Для обработки событий также можно использовать `service` вместо `activity`.
Уведомления с собственным дизайном
Уведомления, сами по себе -- это часть системного интерфейса, поэтому и отображаются они в едином, системном стиле. Однако, если хочется выделиться, или стандартное расположение кнопок и других элементов уведомления вас не устраивает, можно придать нашим уведомлениям свой неповторимый стиль.
DISCLAIMER: из-за огромного количества разнообразных устройств Android с разными размерами и соотношениями сторон экранов в комбинации с ограниченными возможностями позиционирования элементов в уведомлениях (относительно обычных экранов приложения), Custom Content Notification значительно сложнее в поддержке
Отрисовываться это уведомление будет по-прежнему системой, то есть вне процесса нашего приложения, поэтому вместо обычного View нам необходимо использовать RemoteViews. Обратите внимание, что этот механизм поддерживает далеко не все привычные элементы, в частности, сильнее всего сказывается недоступность `ConstraintLayout`.
Простой пример -- custom notification с одной кнопкой "accept call".
Разметка готова, теперь необходимо создать инстанс `RemoteViews` и передать его в конструктор уведомления
Наш пример максимально упрощён и, конечно, несколько режет глаз. Обычно кастомизированное уведомление делается в стиле, похожем на системный, но в брендированной цветовой гамме, как, например, реализованы уведомления в Skype.
Помимо `.setCustomContentView` (обычное уведомление) мы можем отдельно указать разметку для expanded состояния (`.setCustomBigContentView`) и для head-up состояния (`.setCustomHeadsUpContentView`)
Полноэкранные уведомления
Теперь наши уведомления соответствуют дизайну внутри приложения, но это всё ещё небольшие уведомления, с небольшими кнопками. А что происходит, когда на телефон поступает обычный входящий вызов? Нашим глазам предстаёт красивый экран, занимающий всё свободное пространство. К счастью, и нам доступен этот функционал! Причем нам не грозят никакие ограничения, связанные с `RemoteViews` -- мы можем показать полноценную `activity`
В первую очередь, необходимо добавить разрешение в `AndroidManifest.xml`
Создав `activity` с желаемым дизайном и функционалом, инициализируем PendingIntent и добавляем его к уведомлению:
Да, и это всё! Несмотря на то, что эта функциональность так просто добавляется, по какой-то причине далеко не все приложения, связанные со звонками, её используют. А ведь у гигантов вроде Whatsapp и Telegram уведомления о входящих звонках реализованы именно так!
Итог
Уведомление о входящем звонке -- очень важная часть приложения. К нему масса требований: оно должно быть оперативным, привлекающим внимание, но не раздражающим. Сегодня мы узнали о доступных для достижения всех этих целей инструментах. Пусть ваши уведомления всегда будут прекрасны!
Комментарии