Использование уведомлений Android

Эта практическая работа входит в курс «Advanced Android in Kotlin». Вы получите максимальную пользу от этого курса, выполняя задания последовательно, но это не обязательно. Все практическая работа курса перечислены на целевой странице практической работы «Advanced Android in Kotlin» .

Введение

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

Типичное уведомление состоит из заголовка, описания и значка. Уведомление также может содержать кликабельные действия, быстрый ответ, расширяемый контент и изображения.

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

Уведомления — это полезный способ напомнить пользователям о важной задаче, сообщить им о каком-то событии или сообщить важную информацию, которая им нужна, пока ваше приложение работает в фоновом режиме. Используйте уведомления экономно. Это не только проявит уважение к пользователям, но и повысит вероятность того, что уведомления вашего приложения получат должное внимание.

В этой лабораторной работе вы узнаете, как создавать и использовать уведомления в приложении для Android.

Что вам уже следует знать

Вам должно быть знакомо:

  • Как создавать Android-приложения на Kotlin. В частности, работа с Android SDK.
  • Как проектировать приложения с использованием компонентов архитектуры и привязки данных.
  • Базовое понимание BroadcastReceivers
  • Базовое понимание AlarmManager

Чему вы научитесь

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

Что ты будешь делать?

  • Добавьте уведомление в стартовое приложение.
  • Отмените ранее отправленное вами уведомление.
  • Создавайте каналы для разных типов уведомлений.
  • Настройте уведомления в стартовом приложении.
  • Добавьте быстрые действия, чтобы сделать уведомление интерактивным.
  • Отключите значки уведомлений.

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

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

В идеале таймер для варки яиц должен выдавать уведомления об истечении времени. Пользователю действительно важно знать, что яйца готовы, немедленно, иначе они пережарятся! Уведомления визуальные, могут сопровождаться звуками и даже вибрировать — всё это для привлечения внимания! Так вы сможете добиться идеальной варки яиц и довольных, сытых пользователей.

Чтобы получить образец приложения, вы можете:

Клонируйте репозиторий с GitHub и переключитесь на стартовую ветку.

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications


Кроме того, вы можете загрузить репозиторий в виде ZIP-файла, распаковать его и открыть в Android Studio.

Скачать ZIP-архив

  1. Откройте и запустите приложение в Android Studio.

Вы увидите изображение яйца и раскрывающееся меню со списком предустановленных интервалов времени приготовления яйца. Нажмите на треугольник, чтобы открыть раскрывающееся меню « Всмятку» . Первый вариант в списке предназначен для тестирования и устанавливает сигнал всего на 10 секунд. Рядом со списком находится переключатель, запускающий таймер для варки яиц. Вы можете использовать этот переключатель для запуска и остановки таймера в любое время. Код запуска полностью функционален, то есть вы можете настроить таймер для варки яиц и наблюдать за его обратным отсчётом до 0. По завершении отсчёта таймера появится сообщение, как показано ниже.

  1. Проверьте исходный код. Стартовое приложение состоит из одной активности MainActivity . Оно содержит три подпакета: receiver , ui и util .

  • /receiver — пакет receiver содержит два приёмника широковещательных сообщений: AlarmReceiver и SnoozeReceiver . AlarmReceiver активируется AlarmManager для отправки уведомления по истечении заданного пользователем таймера. SnoozeReceiver обрабатывает нажатие кнопки пользователя для отсрочки уведомления.
  • /ui — содержит EggTimerFragment , который является частью пользовательского интерфейса приложения. EggTimerViewModel отвечает за запуск и отмену таймера, а также за другие задачи приложения, связанные с жизненным циклом.
  • /util — в этом пакете два файла. BindingUtils.kt содержит адаптеры привязки для обеспечения связывания данных между пользовательским интерфейсом приложения и ViewModel . NotificationUtils.kt содержит методы расширения для NotificationManager .

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

Шаг 1: Создайте базовое уведомление

В этой задаче вы создаете новое уведомление, устанавливаете сообщение для пользователя и отправляете уведомление .

  1. Откройте класс NotificationUtils.kt и найдите TODO: Step 1.1 . Соответствующие задачи вы найдёте в этой лабораторной работе и коде приложения.
  2. Изучите данную функцию sendNotification() . Вы будете расширять эту функцию до NotificationManager для отправки уведомлений.
//NotificationUtils.kt
// TODO: Step 1.1 extension function to send messages (GIVEN)
/**
 * Builds and delivers a notification.
 *
 * @param messageBody, notification text.
 * @param context, activity context.
 */
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
  1. Получите экземпляр конструктора уведомлений, передайте контекст приложения и идентификатор канала. Идентификатор канала — это строковое значение.

Каналы уведомлений — это способ группировки уведомлений. Объединяя уведомления схожих типов, разработчики и пользователи могут управлять всеми уведомлениями в канале. После создания канала его можно использовать для доставки любого количества уведомлений.

//NotificationUtils.kt
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
val builder = NotificationCompat.Builder(
        applicationContext,
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. Задайте значок уведомления, представляющий ваше приложение, заголовок и текст сообщения, которое вы хотите отправить пользователю. Дополнительные параметры настройки уведомления будут представлены далее в лабораторной работе, но это минимальный объём данных, необходимый для отправки уведомления.
//NotificationUtils.kt
   // TODO: Step 1.3 set title, text and icon to builder
   .setSmallIcon(R.drawable.cooked_egg)
   .setContentTitle(applicationContext.getString(R.string.notification_title))
   .setContentText(messageBody)
  1. Далее вам необходимо вызвать notify() с уникальным идентификатором вашего уведомления и объектом Notification из вашего конструктора.

Этот идентификатор представляет текущий экземпляр уведомления и необходим для его обновления или отмены. Поскольку в вашем приложении одновременно будет только одно активное уведомление, вы можете использовать один и тот же идентификатор для всех уведомлений. Для этой цели уже предусмотрена константа NOTIFICATION_ID в NotificationUtils.kt . Обратите внимание, что вы можете вызвать notify() напрямую, поскольку вызов выполняется из функции расширения того же класса.

//NotificationUtils.kt
   // TODO: Step 1.4 call notify to send the notification
    // Deliver the notification
    notify(NOTIFICATION_ID, builder.build())
  1. Откройте ui/EggTimerViewModel.kt и найдите функцию startTimer() . Эта функция создаёт сигнал с выбранным интервалом времени, когда пользователь включает таймер для варки яиц.
  2. В этой функции вы будете активировать уведомление, когда пользователь запустит таймер. Для вызова функции sendNotification() которую вы ранее реализовали, вам потребуется экземпляр NotificationManager . NotificationManager — это системная служба, предоставляющая все функции API уведомлений, включая добавленную вами функцию расширения. Каждый раз, когда вы хотите отправить, отменить или обновить уведомление, вам необходимо запросить экземпляр NotificationManager из системы. Вызовите функцию sendNotification()| с текстом уведомления и контекстом.
// EggTimerViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification

val notificationManager = ContextCompat.getSystemService(
    app, 
    NotificationManager::class.java
) as NotificationManager
                notificationManager.sendNotification(app.getString(R.string.timer_running), app)

Вы почти у цели. Однако, если вы запустите приложение сейчас и установите таймер, уведомление не придёт.

  1. Откройте logcat и найдите строку "No Channel found" . Вы должны увидеть сообщение об ошибке, указывающее на то, что egg_channel не существует. Далее вы узнаете больше о каналах уведомлений и сможете исправить эту ошибку.

Шаг 2: Каналы уведомлений

Начиная с API уровня 26, все уведомления должны быть назначены каналу. Если нажать и удерживать значок панели запуска приложений, выбрать информацию о приложении и нажать на уведомления, вы увидите список каналов уведомлений, связанных с приложением. Сейчас этот список пуст, поскольку ваше приложение не создало ни одного канала.

Каналы представляют собой «тип» уведомлений — например, ваш таймер для варки яиц может отправлять уведомление, когда яйцо будет готово, а также использовать другой канал для отправки ежедневных уведомлений, напоминающих вам о необходимости съесть яйца на завтрак. Все уведомления в канале сгруппированы, и пользователи могут настроить параметры уведомлений для всего канала. Это позволяет им персонализировать настройки уведомлений в зависимости от типа уведомлений, которые им интересны. Например, пользователи могут отключить уведомления о завтраке, но при этом по-прежнему получать уведомления от таймера.

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

На шаге 1.1 вы использовали egg_notification_channel_id в качестве канала уведомлений, поэтому теперь вам необходимо создать и настроить параметры уведомлений и поведение этого канала.

  1. Откройте EggTimerFragment.kt и найдите функцию createChannel() .
  2. Передайте уникальный идентификатор канала конструктору NotificationChannel .
  3. Передайте название канала уведомлений, которое пользователи также увидят на экране настроек .
  4. В качестве последнего параметра передайте уровень важности для канала уведомлений. Уровни важности будут рассмотрены далее в этой практической работе, поэтому сейчас вы можете er.IMPORTANCE_LOW NotificationManag .
  5. В объекте notificationChannel установите enableLights в значение true. Эта настройка включит подсветку при отображении уведомления.
  6. В объекте notificationChannel задайте для lightColor значение red, чтобы при отображении уведомления отображался красный свет.
  7. В объекте notificationChannel установите enableVibration на true, чтобы включить вибрацию.
  8. В объекте notificationChannel задайте описание канала как 'Time for breakf ».
  9. Получите экземпляр NotificationManager , вызвав getSystemService() .
  10. Вызовите createNotificationChannel() в NotificationManager и передайте объект notificationChannel , созданный на предыдущем шаге.
//EggTimerFragment.kt
private fun createChannel(channelId: String, channelName: String) {
    // TODO: Step 1.6 START create a channel
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val notificationChannel = NotificationChannel(
            channelId,
            channelName,
            // TODO: Step 2.4 change importance
            NotificationManager.IMPORTANCE_LOW
        )
        // TODO: Step 2.6 disable badges for this channel

        notificationChannel.enableLights(true)
        notificationChannel.lightColor = Color.RED
        notificationChannel.enableVibration(true)
        notificationChannel.description = "Time for breakfast"

        val notificationManager = requireActivity().getSystemService(
            NotificationManager::class.java
        )
        notificationManager.createNotificationChannel(notificationChannel)
    }
    // TODO: Step 1.6 END create channel
}
  1. Далее, чтобы создать канал, необходимо вызвать функцию createChannel() которую вы только что написали (шаг 1.7). Эта функция принимает два параметра: идентификатор канала и его название. Вам нужно найти идентификатор и название канала в строковых ресурсах, которые уже есть в вашем проекте.
// EggTimerFragment.kt
    // TODO: Step 1.7 call createChannel
    createChannel(
          getString(R.string.egg_notification_channel_id),
          getString(R.string.egg_notification_channel_name)
    )
  1. Вам необходимо передать идентификатор канала в конструктор уведомлений. Вы уже сделали это на шаге 1.2. Установка неверного значения идентификатора канала приведёт к ошибке уведомления. Откройте NotificationUtils.kt , чтобы проверить правильность ранее заданного идентификатора канала.
// NotificationUtils.kt
val builder = NotificationCompat.Builder(
        applicationContext,
       // TODO: Step 1.8 verify the notification channel name
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. Запустите приложение, и вы увидите, что оно отправляет уведомление каждый раз, когда вы запускаете таймер.
  2. Потяните строку состояния и убедитесь, что заголовок, содержимое и значок уведомления соответствуют тем, которые вы установили на предыдущих шагах.
  3. Чтобы проверить созданный канал, закройте приложение и найдите его значок. Нажмите на него и выберите «Информация о приложении» .

  1. Выберите «Уведомления» в списке настроек. Вы увидите новый канал под названием «Яйцо» прямо под настройкой «Показывать уведомления» .

Теперь при запуске приложения отображается уведомление. Вы, как разработчик приложения, и ваши пользователи можете настраивать параметры и поведение всех уведомлений, отправляемых по этому каналу. Поздравляем, вы создали уведомление!

Шаг 3: Добавьте уведомления в свое приложение

Пока что это демонстрирует базовое использование API уведомлений, но отправка уведомления сразу после запуска таймера не имеет особого смысла. Пользователи, вероятно, предпочли бы получать уведомления, когда яйцо будет готово. В следующей части работы вы исправите это и замените всплывающее сообщение на уведомление.

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

Ваше приложение использует AlarmManager для настройки будильника. Код, связанный с AlarmManager , уже присутствует в стартовом коде и используется для отображения всплывающего сообщения. AlarmManager отслеживает выбранное время и активирует функцию onReceive() файла AlarmReceiver.kt по истечении этого времени. Если открыть AlarmReceiver.kt и перейти к onReceive() , вы увидите всплывающее сообщение, которое отображается каждый раз при настройке таймера для варки яиц.

  1. Откройте AlarmReceiver.kt , экземпляр NotificationManager , и вызовите функцию sendNotification() с текстом сообщения и параметрами контекста.
// AlarmReceiver.kt
   // TODO: Step 1.9 add call to sendNotification
   val notificationManager = ContextCompat.getSystemService(
       context, 
       NotificationManager::class.java
   ) as NotificationManager
             
   notificationManager.sendNotification(
       context.getText(R.string.eggs_ready).toString(), 
       context
   )
  1. При желании вы можете удалить уведомление, так как ваше приложение отправит уведомление по истечении времени таймера.
// AlarmReceiver.kt
     // TODO: Step 1.10 [Optional] remove toast
//   Toast.makeText(
//       context, 
//       context.getText(R.string.eggs_ready),
//       Toast.LENGTH_SHORT
//   ).show()
  1. Запустите приложение. Вы увидите уведомление каждый раз при запуске таймера и по его истечении.

Это не идеальный вариант. Не стоит отправлять слишком много уведомлений пользователям. Вы можете удалить первое уведомление, которое отправляется при запуске таймера пользователем.

  1. Откройте EggTimerFragment.kt и удалите код уведомления для шага 1.5.
// EggTimeViewModel.kt

// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification
// val notificationManager = ContextCompat.getSystemService(
//      app,
//      NotificationManager::class.java
// ) as NotificationManager
// notificationManager.sendNotification(app.getString(R.string.eggs_ready), app)
  1. Запустите приложение еще раз.
  2. Установите таймер, переведите его в фоновый режим и дождитесь окончания времени. Вы увидите уведомление. Это гораздо более полезное уведомление.

Шаг 4: Добавьте намерение контента

  1. Запустите приложение еще раз, если оно еще не запущено.
  2. Нажимаю на уведомление. Ничего не происходит!

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

Intent — это объект сообщения, который можно использовать для запроса действия у другого компонента приложения. Intent может использоваться для запуска активности, сервиса или отправки рассылки. В данном случае вы используете Intent, чтобы сообщить системе открыть MainActivity при касании пользователем уведомления. Поскольку ваше приложение состоит только из одного представления, вариантов немного. Однако в более крупном приложении уведомление должно создавать плавный интерфейс, перенаправляя пользователя на экран, который имеет смысл при взаимодействии с уведомлением.

  1. Откройте NotificationUtils.kt и найдите функцию расширения sendNotification() .
  2. Создайте Intent с вашим applicationContext и действием, которое нужно запустить, MainActivity::class.java .
// NotificationUtils.kt

fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
    // Create the content intent for the notification, which launches
    // this activity
   // TODO: Step 1.11 create intent
    val contentIntent = Intent(applicationContext, MainActivity::class.java)

Вы создали намерение, но уведомление отображается за пределами вашего приложения. Чтобы намерение работало за пределами вашего приложения, необходимо создать новое PendingIntent .

PendingIntent предоставляет другому приложению или системе право выполнить операцию от имени вашего приложения. Сам PendingIntent — это просто ссылка на токен, поддерживаемый системой, описывающий исходные данные, использованные для его получения. Это означает, что даже если процесс приложения, в котором он был создан, будет завершен, сам PendingIntent останется доступным для других процессов, которым он был предоставлен. В этом случае система будет использовать PendingIntent для открытия приложения от вашего имени, независимо от того, запущено ли приложение-таймер.

  1. Создайте PendingIntent с applicationContext , NOTIFICATION_ID , contentIntent созданным на предыдущем шаге, и флагом PendingIntent . Флаг PendingIntent указывает, можно ли создать новый PendingIntent или использовать существующий. Необходимо установить флаг PendingIntent.FLAG_UPDATE_CURRENT , поскольку вы не хотите создавать новое уведомление при наличии существующего. Таким образом, вы измените текущий PendingIntent , связанный с предоставляемым вами намерением.
// NotificationUtils.kt
   // TODO: Step 1.12 create PendingIntent
    val contentPendingIntent = PendingIntent.getActivity(
        applicationContext, 
        NOTIFICATION_ID,
        contentIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
  1. Передайте PendingIntent вашему уведомлению. Это делается вызовом setContentIntent() в NotificationBuilder . Теперь при щелчке по уведомлению будет срабатывать PendingIntent , открывая MainActivity .
  2. Также установите для setAutoCancel() значение true , чтобы при нажатии пользователем уведомления оно автоматически закрывалось и пользователь переходил в приложение.
// NotificationUtils.kt
    // TODO: Step 1.13 set content intent
    .setContentIntent(contentPendingIntent)
    .setAutoCancel(true)
  1. Запустите приложение еще раз.
  2. Установите таймер, переведите приложение в фоновый режим и дождитесь появления уведомления.
  3. Как только вы увидите уведомление, нажмите на него, потянув вниз строку состояния, и наблюдайте, как приложение выводится на передний план.

Шаг 5: Отмените уведомление

У вас есть работающий таймер для варки яиц с уведомлениями, но есть небольшая проблема. Если вы установите таймер, получите уведомление и снова установите таймер, предыдущее уведомление останется в строке состояния, пока работает новый таймер. Это может сбить с толку пользователя, если приложение работает в фоновом режиме, и привести к тому, что яйца будут недоваренными.

Чтобы исправить это, необходимо очищать предыдущее уведомление при запуске нового таймера. Для начала создайте ещё одну функцию расширения в файле NotificationUtils.kt . NotificationManager имеет API для отмены всех активных уведомлений, называемое cancelAll () .

  1. Откройте NotificationsUtil.kt .
  2. Добавьте функцию расширения в NotificationManager , которая вызывает cancelAll() .
// NotificationUtils.kt

// TODO: Step 1.14 Cancel all notifications
/**
 * Cancels all notifications.
 *
 */
fun NotificationManager.cancelNotifications() {
    cancelAll()
}
  1. Откройте EggTimerViewModel.kt и перейдите к функции startTimer() .
  2. Внутри startTimer() получите экземпляр NotificationManager из системы и вызовите cancelNotifications() .
//  EggTimerViewModel.kt
   //TODO Step 1.15 call cancel notification
    val notificationManager =
       ContextCompat.getSystemService(
            app,
            NotificationManager::class.java
        ) as NotificationManager
    notificationManager.cancelNotifications()       
  1. Запустите приложение и запустите таймер.
  2. После того, как вы увидите уведомление, снова запустите таймер и наблюдайте, как наше приложение автоматически удаляет предыдущее уведомление из строки состояния.

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

Шаг 1: Оформите уведомление

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

NotificationCompat предлагает встроенные стили для:

  • BigTextStyle , который может отображать большой блок текста, например, отображать содержимое электронного письма в развернутом виде.
  • BigPictureStyle — показывает уведомления большого формата, содержащие прикрепленные изображения большого размера.
  • InboxStyle , который отображает текстовое содержимое в стиле беседы.
  • MediaStyle , который отображает элементы управления воспроизведением мультимедиа.
  • MessagingStyle — отображает крупноформатные уведомления, содержащие несколько сообщений между любым количеством людей.

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

  1. Откройте NotificationUtils.kt и найдите функцию sendNotification() .
  2. Начнем с загрузки изображения из resources с помощью BitmapFactory .
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
  1. Создайте новый BigPictureStyle и настройте изображение.
  2. Установите bigLargeIcon() в null , чтобы большой значок исчезал при развертывании уведомления.
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
        .bigPicture(eggImage)
        .bigLargeIcon(null)
  1. Установите стиль с помощью setStyle() на bigPicStyle .
  2. Установите большой значок с помощью setLargeIcon() для eggImage , чтобы изображение отображалось как маленький значок при свертывании уведомления.
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
  1. Запустите приложение и установите таймер. При первом отображении уведомление отображается в свёрнутом виде в панели уведомлений. Если развернуть уведомление, в расширенной области уведомлений появится большое изображение.

Шаг 2: Действия по уведомлению

Действия в уведомлениях — это ещё одна возможность настройки уведомлений. В настоящее время ваши уведомления перенаправляют пользователей в приложение при нажатии на них. Помимо этого действия по умолчанию, вы можете добавить кнопки действий, которые выполняют задачи, связанные с приложением, из уведомления.

Уведомление может содержать до трёх кнопок действий, позволяющих пользователю быстро отреагировать, например, отложить напоминание или ответить на SMS. Эти кнопки действий не должны дублировать действие, выполняемое при нажатии пользователем на уведомление.

Чтобы добавить кнопку действия, передайте PendingIntent в addAction() Функция в конструкторе. Это похоже на настройку действия по касанию уведомления по умолчанию путём вызова setContentIntent() , за исключением того, что вместо запуска действия можно выполнить ряд других действий, например, запустить BroadcastReceiver , который выполняет задачу в фоновом режиме, чтобы действие не прерывало работу уже открытого приложения.

В этой лабораторной работе вам уже предоставлен BoadcastReceiver с именем SnoozeReceiver . Вы будете использовать SnoozeReceiver для получения нажатия пользователем кнопки «Уведомление». Далее вы добавите код для откладывания уведомления о приготовлении яиц на 60 секунд при нажатии пользователем кнопки «Отложить». При нажатии кнопки «Отложить» SnoozeReceiver получит намерение и создаст новый будильник для отправки нового уведомления через 60 секунд.

  1. Откройте SnoozeReceiver.kt . Этот класс похож на AlarmReceiver , который вы использовали ранее. Далее вы добавите код, который вызовет функцию onReceive() класса SnoozeReceiver . Короче говоря, код в SnoozeReceiver создаст новый будильник для отправки нового уведомления через минуту. Прокрутите вниз до функции onReceive, получите экземпляр notificationManager из system и вызовите cancelAll.
// SnoozeReceiver.kt
        val notificationManager = ContextCompat.getSystemService(
            context,
            NotificationManager::class.java
        ) as NotificationManager
        notificationManager.cancelAll()
  1. Чтобы использовать SnoozeReceiver , откройте NotificationUtils.kt .
  2. Создайте новый Intent snoozeIntent для SnoozeReceiver сразу после стиля в функции sendNotification() .
  3. Создайте отложенное намерение, вызвав метод getBroadcast() объекта PendingIntent , который ожидает параметры, указанные в следующих шагах. Этот PendingIntent будет использоваться системой для настройки нового будильника, который будет публиковать новое уведомление через 60 секунд после нажатия пользователем кнопки отсрочки.
  4. Первый параметр — это контекст приложения, в котором PendingIntent должен начать действие.
  5. Второй параметр — это код запроса, который представляет собой код запроса для данного ожидающего намерения. Если вам нужно обновить или отменить это ожидающее намерение, вам нужно использовать этот код для доступа к нему.
  6. Затем добавьте объект snoozeIntent , который представляет собой цель запускаемой активности.
  7. Наконец, добавьте значение флага #FLAG_ONE_SHOT , поскольку намерение будет использовано только один раз. Быстрое действие и уведомление исчезнут после первого нажатия, поэтому намерение можно использовать только один раз.
// NotificationUtils.kt

// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
    applicationContext, 
    REQUEST_CODE, 
    snoozeIntent, 
    FLAGS
)
  1. Затем вызовите функцию addAction() в notificationBuilder . Эта функция ожидает значок и текст, описывающие ваше действие пользователю. Вам также необходимо добавить snoozeIntent . Это намерение будет использоваться для активации нужного boadcastReceiver при нажатии на ваше действие.
// NotificationUtils.kt
// TODO: Step 2.3 add snooze action
.addAction(
    R.drawable.egg_icon, 
    applicationContext.getString(R.string.snooze),
    snoozePendingIntent
)
  1. Запустите приложение-таймер для варки яиц, чтобы протестировать функцию повтора сигнала.
  2. Запустите таймер и переведите приложение в фоновый режим. Когда таймер истечёт, разверните уведомление, и вы увидите, что в нём появилась кнопка «Отложить», которая откладывает таймер ещё на минуту.

Шаг 3: Важность уведомления

Важность определяет, насколько сильно уведомление должно отвлекать пользователя визуально и аудиально. Чем выше важность, тем сильнее оно будет отвлекать пользователя.

Уровень важности необходимо указать в конструкторе NotificationChannel . Изначально для приложения-таймера для яиц был задан низкий уровень важности. Вы можете использовать один из пяти уровней важности: от IMPORTANCE_NONE(0) до IMPORTANCE_HIGH(4) . Уровень важности, назначенный каналу, применяется ко всем отправляемым в него уведомлениям.

Уровни важности каналов

Уровень важности, видимый пользователю

Важность (Android 8.0 и выше)

Приоритет (Android 7.1 и ниже)

Издает звук и появляется в виде всплывающего уведомления (вверху экрана)

ВАЖНОСТЬ_ВЫСОКАЯ

ПРИОРИТЕТ_ВЫСОКИЙ / ПРИОРИТЕТ_МАКС

Издает звук

IMPORTANCE_DEFAULT

ПРИОРИТЕТ_ПО УМОЛЧАНИЮ

Нет звука

ВАЖНОСТЬ_НИЗКАЯ

ПРИОРИТЕТ_НИЗКИЙ

Нет звука и не отображается в строке состояния.

ВАЖНОСТЬ_МИН.

ПРИОРИТЕТ_МИН

Информацию о выборе подходящего уровня приоритета см. в разделе «Уровни приоритета» в руководстве по дизайну уведомлений . Будьте внимательны при выборе уровня важности уведомлений в вашем приложении. Важность канала следует выбирать с учётом времени и внимания пользователя. Когда неважное уведомление маскируется под срочное, оно может вызывать ненужную тревогу и отвлекать. Пользователи полностью контролируют уровень важности своих уведомлений, поэтому, если вы создадите раздражающее уведомление, они могут полностью отключить ваш канал уведомлений.

При первом создании уведомления на шаге 1.6 таймер для варки яиц был настроен на отправку уведомлений с низким приоритетом, поскольку он был разработан таким образом, чтобы не беспокоить пользователя уведомлениями. Однако, возможно, стоит привлечь внимание пользователя до того, как яйцо пережарится. Чтобы изменить уровень важности уведомления, начните с настроек канала. Важность канала влияет на уровень прерывания всех уведомлений, отправляемых в канале, и должна быть указана в конструкторе NotificationChannel .

  1. Чтобы изменить уровень важности канала уведомлений вашего приложения, откройте EggTimerFragment.kt и перейдите к createChannel() . Измените уровень важности с IMPORTANCE_LOW на IMPORTANCE_HIGH .
// EggTimerFragment.kt
    val notificationChannel = NotificationChannel(
        channelId,
        channelName,
        // TODO: Step 2.4 change importance
        NotificationManager.IMPORTANCE_HIGH
    )

Для поддержки устройств под управлением Android 7.1 (уровень API 25) или ниже необходимо также вызывать setPriority() для каждого уведомления, используя константу приоритета из класса NotificationCompat .

  1. Откройте NotificationUtils.kt и добавьте следующее в объект конструктора уведомлений.
// NotificationUtils.kt
   .addAction(
       R.drawable.common_google_signin_btn_icon_dark,
       applicationContext.getString(R.string.snooze),
       snoozePendingIntent
    )
   // TODO: Step 2.5 set priority
    .setPriority(NotificationCompat.PRIORITY_HIGH)
  1. Перед запуском приложения нажмите и удерживайте значок приложения на устройстве или эмуляторе, а затем выберите «Удалить», чтобы удалить предыдущие настройки каналов. Если вы не удалите приложение, настройки приоритета каналов не изменятся, и это не приведёт к изменению поведения при публикации уведомления.
  2. Теперь снова запустите приложение и запустите таймер. На этот раз при получении уведомления вы увидите всплывающее окно в верхней части экрана, независимо от того, запущено ли приложение в активном или фоновом режиме.

Шаг 4: Значки уведомлений

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

Эти точки, называемые значками, отображаются по умолчанию, и вашему приложению не нужно ничего делать. Однако в некоторых ситуациях значки не имеют смысла для уведомлений, поэтому вы можете отключить их для каждого канала, вызвав метод setShowBadge(false) объекта NotificationChannel . Поскольку у таймера-яйца одновременно активна только одна метка, значок на значке приложения не особо полезен для пользователей. Далее вы отключите значок и будете отображать только уведомление для таймера-яйца.

  1. Добавьте setShowBadge(false) в код создания канала для таймера для яиц, чтобы отключить значки.
// EggTimerFragment.kt

    ).apply {
        // TODO: Step 2.6 disable badges for this channel
        setShowBadge(false)
    }
  1. Запустите приложение снова, запустите таймер и обратите внимание на значок приложения. На значке приложения не должно быть никаких значков.

Код решения находится в основной ветке загруженного вами кода .

  • Используйте класс NotificationManager для создания, отправки, обновления и отмены уведомлений.
  • Используйте объект NotificationChannel с методом createNotificationChannel , чтобы задать канал для уведомления.
  • Используйте addAction() для добавления быстрых действий к уведомлению.
  • Используйте setShowBadge() для включения или отключения значков.
  • Оформляйте уведомления, используя стили, которые расширяют Notification.Style
  • Установите уровень важности с помощью NotificationChannel.setImportance()

Курс Udacity:

Документация для разработчиков Android:

Ссылки на другие практические занятия по этому курсу см. на целевой странице практических занятий по курсу Advanced Android in Kotlin.