Создать уведомление

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

Код на этой странице использует API NotificationCompat из библиотеки AndroidX. Эти API позволяют добавлять функции, доступные только в более новых версиях Android, сохраняя при этом совместимость с Android 9 (уровень API 28). Однако некоторые функции, такие как встроенное действие ответа, приводят к остановке работы в более ранних версиях.

Добавьте базовую библиотеку AndroidX

Хотя большинство проектов, созданных с помощью Android Studio, включают необходимые зависимости для использования NotificationCompat , убедитесь, что ваш файл build.gradle на уровне модуля содержит следующую зависимость:

классный

dependencies {
    implementation "androidx.core:core:2.2.0"
}

Котлин

dependencies {
    implementation("androidx.core:core-ktx:2.2.0")
}

Создайте базовое уведомление

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

Рисунок 1. Уведомление со значком, заголовком и текстом.

Дополнительные сведения о каждой части уведомления см. в разделе «Анатомия уведомления» .

Объявить разрешение времени выполнения

Android 13 (уровень API 33) и более поздних версий поддерживает разрешение среды выполнения для публикации неисключенных уведомлений (включая службы переднего плана (FGS)) из приложения.

Разрешение, которое вам необходимо объявить в файле манифеста вашего приложения, отображается в следующем фрагменте кода:

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>

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

Установите содержание уведомления

Для начала задайте содержимое и канал уведомления с помощью объекта NotificationCompat.Builder . В следующем примере показано, как создать уведомление со следующим:

  • Небольшой значок, установленный с помощью setSmallIcon() . Это единственный необходимый контент, видимый пользователю.

  • Заголовок, установленный с помощью setContentTitle() .

  • Основной текст, заданный с помощью setContentText() .

  • Приоритет уведомления, установленный setPriority() . Приоритет определяет, насколько навязчивым будет уведомление на Android 7.1 и более ранних версиях. Для Android 8.0 и более поздних версий вместо этого установите важность канала, как показано в следующем разделе.

Котлин

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle(textTitle)
        .setContentText(textContent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

Ява

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle(textTitle)
        .setContentText(textContent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

Конструктор NotificationCompat.Builder требует, чтобы вы указали идентификатор канала. Это необходимо для совместимости с Android 8.0 (уровень API 26) и более поздних версий, но игнорируется более ранними версиями.

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

Рисунок 2. Расширяемое уведомление в свернутом и развернутом виде.

Если вы хотите, чтобы ваше уведомление было длиннее, вы можете включить расширяемое уведомление, добавив шаблон стиля с помощью setStyle() . Например, следующий код создает большую текстовую область:

Котлин

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

Ява

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(new NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

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

Создайте канал и установите важность

Прежде чем вы сможете доставить уведомление на Android 8.0 и более поздних версиях, зарегистрируйте канал уведомлений вашего приложения в системе, передав экземпляр NotificationChannel в createNotificationChannel() . Следующий код заблокирован условием версии SDK_INT :

Котлин

private fun createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is not in the Support Library.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = getString(R.string.channel_name)
        val descriptionText = getString(R.string.channel_description)
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
            description = descriptionText
        }
        // Register the channel with the system.
        val notificationManager: NotificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

Ява

private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is not in the Support Library.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        // Register the channel with the system; you can't change the importance
        // or other notification behaviors after this.
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}

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

Конструктор NotificationChannel требует importance , используя одну из констант класса NotificationManager . Этот параметр определяет, как прерывать пользователя при любом уведомлении, принадлежащем этому каналу. Установите приоритет с помощью setPriority() для поддержки Android 7.1 и более ранних версий, как показано в предыдущем примере.

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

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

Установите действие касания уведомления

Каждое уведомление должно реагировать на нажатие, обычно для открытия действия в вашем приложении, соответствующего уведомлению. Для этого укажите намерение содержимого, определенное с помощью объекта PendingIntent , и передайте его в setContentIntent() .

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

Котлин

// Create an explicit intent for an Activity in your app.
val intent = Intent(this, AlertDetails::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)

val builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        // Set the intent that fires when the user taps the notification.
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)

Ява

// Create an explicit intent for an Activity in your app.
Intent intent = new Intent(this, AlertDetails.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        // Set the intent that fires when the user taps the notification.
        .setContentIntent(pendingIntent)
        .setAutoCancel(true);

Этот код вызывает setAutoCancel() , который автоматически удаляет уведомление, когда пользователь нажимает на него.

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

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

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

Дополнительные сведения о различных способах настройки назначения уведомления см. в разделе «Начать действие из уведомления» .

Показать уведомление

Чтобы уведомление появилось, вызовите NotificationManagerCompat.notify() , передав ему уникальный идентификатор уведомления и результат NotificationCompat.Builder.build() . Это показано в следующем примере:

Котлин

with(NotificationManagerCompat.from(this)) {
    if (ActivityCompat.checkSelfPermission(
            this@MainActivity,
            Manifest.permission.POST_NOTIFICATIONS
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        // TODO: Consider calling
        // ActivityCompat#requestPermissions
        // here to request the missing permissions, and then overriding
        // public fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>,
        //                                        grantResults: IntArray)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.

        return@with
    }
    // notificationId is a unique int for each notification that you must define.
    notify(NOTIFICATION_ID, builder.build())
}

Ява

with(NotificationManagerCompat.from(this)) {
   if (ActivityCompat.checkSelfPermission(
           this@MainActivity,
           Manifest.permission.POST_NOTIFICATIONS
       ) != PackageManager.PERMISSION_GRANTED
   ) {
       // TODO: Consider calling
       // ActivityCompat#requestPermissions
       // here to request the missing permissions, and then overriding
       // public void onRequestPermissionsResult(int requestCode, String[] permissions,
       //                                        int[] grantResults)
       // to handle the case where the user grants the permission. See the documentation
       // for ActivityCompat#requestPermissions for more details.

       return
   }
   // notificationId is a unique int for each notification that you must define.
   notify(NOTIFICATION_ID, builder.build())
}

Сохраните идентификатор уведомления, который вы передаете в NotificationManagerCompat.notify() , поскольку он понадобится вам, когда вы захотите обновить или удалить уведомление .

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

Добавьте кнопки действий

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

Рисунок 3. Уведомление с одной кнопкой действия.

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

Например, следующий код показывает, как отправить широковещательную рассылку определенному получателю:

Котлин

val ACTION_SNOOZE = "snooze"

val snoozeIntent = Intent(this, MyBroadcastReceiver::class.java).apply {
    action = ACTION_SNOOZE
    putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val snoozePendingIntent: PendingIntent =
    PendingIntent.getBroadcast(this, 0, snoozeIntent, 0)
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, getString(R.string.snooze),
                snoozePendingIntent)

Ява

String ACTION_SNOOZE = "snooze"

Intent snoozeIntent = new Intent(this, MyBroadcastReceiver.class);
snoozeIntent.setAction(ACTION_SNOOZE);
snoozeIntent.putExtra(EXTRA_NOTIFICATION_ID, 0);
PendingIntent snoozePendingIntent =
        PendingIntent.getBroadcast(this, 0, snoozeIntent, 0);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, getString(R.string.snooze),
                snoozePendingIntent);

Дополнительные сведения о создании BroadcastReceiver для выполнения фоновой работы см. в обзоре Broadcasts .

Если вместо этого вы пытаетесь создать уведомление с помощью кнопок воспроизведения мультимедиа, например приостановки и пропуска треков, узнайте, как создать уведомление с помощью элементов управления мультимедиа .

Добавить действие прямого ответа

Действие прямого ответа, представленное в Android 7.0 (уровень API 24), позволяет пользователям вводить текст непосредственно в уведомление. Затем текст доставляется в ваше приложение без открытия действия. Например, вы можете использовать действие прямого ответа, чтобы позволить пользователям отвечать на текстовые сообщения или обновлять списки задач из уведомления.

Рисунок 4. Нажатие кнопки «Ответить» открывает ввод текста.

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

Добавьте кнопку ответить

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

  1. Создайте экземпляр RemoteInput.Builder , который можно добавить к действию уведомления. Конструктор этого класса принимает строку, которую система использует в качестве ключа для ввода текста. Ваше приложение позже использует этот ключ для получения текста ввода.

    Котлин

      // Key for the string that's delivered in the action's intent.
      private val KEY_TEXT_REPLY = "key_text_reply"
      var replyLabel: String = resources.getString(R.string.reply_label)
      var remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
          setLabel(replyLabel)
          build()
      }
      

    Ява

      // Key for the string that's delivered in the action's intent.
      private static final String KEY_TEXT_REPLY = "key_text_reply";
    
      String replyLabel = getResources().getString(R.string.reply_label);
      RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
              .setLabel(replyLabel)
              .build();
      
  2. Создайте PendingIntent для действия ответа.

    Котлин

      // Build a PendingIntent for the reply action to trigger.
      var replyPendingIntent: PendingIntent =
          PendingIntent.getBroadcast(applicationContext,
              conversation.getConversationId(),
              getMessageReplyIntent(conversation.getConversationId()),
              PendingIntent.FLAG_UPDATE_CURRENT)
      

    Ява

      // Build a PendingIntent for the reply action to trigger.
      PendingIntent replyPendingIntent =
              PendingIntent.getBroadcast(getApplicationContext(),
                      conversation.getConversationId(),
                      getMessageReplyIntent(conversation.getConversationId()),
                      PendingIntent.FLAG_UPDATE_CURRENT);
      
  3. Прикрепите объект RemoteInput к действию с помощью addRemoteInput() .

    Котлин

      // Create the reply action and add the remote input.
      var action: NotificationCompat.Action =
          NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
              getString(R.string.label), replyPendingIntent)
              .addRemoteInput(remoteInput)
              .build()
      

    Ява

      // Create the reply action and add the remote input.
      NotificationCompat.Action action =
              new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
                      getString(R.string.label), replyPendingIntent)
                      .addRemoteInput(remoteInput)
                      .build();
      
  4. Примените действие к уведомлению и выдайте уведомление.

    Котлин

      // Build the notification and add the action.
      val newMessageNotification = Notification.Builder(context, CHANNEL_ID)
              .setSmallIcon(R.drawable.ic_message)
              .setContentTitle(getString(R.string.title))
              .setContentText(getString(R.string.content))
              .addAction(action)
              .build()
    
      // Issue the notification.
      with(NotificationManagerCompat.from(this)) {
          notificationManager.notify(notificationId, newMessageNotification)
      }
      

    Ява

      // Build the notification and add the action.
      Notification newMessageNotification = new Notification.Builder(context, CHANNEL_ID)
              .setSmallIcon(R.drawable.ic_message)
              .setContentTitle(getString(R.string.title))
              .setContentText(getString(R.string.content))
              .addAction(action)
              .build();
    
      // Issue the notification.
      NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
      notificationManager.notify(notificationId, newMessageNotification);
      

Система предлагает пользователю ввести ответ, когда он запускает действие уведомления, как показано на рисунке 4.

Получить ввод пользователя из ответа

Чтобы получить пользовательский ввод из пользовательского интерфейса ответа на уведомление, вызовите RemoteInput.getResultsFromIntent() , передав ему Intent полученное вашим BroadcastReceiver :

Котлин

private fun getMessageText(intent: Intent): CharSequence? {
    return RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY)
}

Ява

private CharSequence getMessageText(Intent intent) {
    Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
    if (remoteInput != null) {
        return remoteInput.getCharSequence(KEY_TEXT_REPLY);
    }
    return null;
 }

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

Котлин

// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
val repliedNotification = Notification.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentText(getString(R.string.replied))
        .build()

// Issue the new notification.
NotificationManagerCompat.from(this).apply {
    notificationManager.notify(notificationId, repliedNotification)
}

Ява

// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
Notification repliedNotification = new Notification.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentText(getString(R.string.replied))
        .build();

// Issue the new notification.
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(notificationId, repliedNotification);

При работе с этим новым уведомлением используйте контекст, переданный методу onReceive() получателя.

Добавьте ответ в нижнюю часть уведомления, вызвав setRemoteInputHistory() . Однако если вы создаете приложение для обмена сообщениями, создайте уведомление в стиле обмена сообщениями и добавьте новое сообщение в беседу.

Дополнительные советы по уведомлениям из приложений для обмена сообщениями см. в разделе «Рекомендации по использованию приложений для обмена сообщениями» .

Добавить индикатор выполнения

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

Рисунок 5. Индикатор выполнения во время операции.

Если вы можете оценить, какая часть операции завершена в любой момент, используйте «определенную» форму индикатора, как показано на рисунке 5, вызвав setProgress(max, progress, false) . Первый параметр — это «завершенное» значение, например 100. Второй — насколько завершено. Последнее указывает на то, что это определенный индикатор выполнения.

По мере выполнения операции постоянно вызывайте setProgress(max, progress, false) с обновленным значением progress и повторно отправляйте уведомление, как показано в следующем примере.

Котлин

val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
    setContentTitle("Picture Download")
    setContentText("Download in progress")
    setSmallIcon(R.drawable.ic_notification)
    setPriority(NotificationCompat.PRIORITY_LOW)
}
val PROGRESS_MAX = 100
val PROGRESS_CURRENT = 0
NotificationManagerCompat.from(this).apply {
    // Issue the initial notification with zero progress.
    builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false)
    notify(notificationId, builder.build())

    // Do the job that tracks the progress here.
    // Usually, this is in a worker thread.
    // To show progress, update PROGRESS_CURRENT and update the notification with:
    // builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
    // notificationManager.notify(notificationId, builder.build());

    // When done, update the notification once more to remove the progress bar.
    builder.setContentText("Download complete")
            .setProgress(0, 0, false)
    notify(notificationId, builder.build())
}

Ява

...
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
builder.setContentTitle("Picture Download")
        .setContentText("Download in progress")
        .setSmallIcon(R.drawable.ic_notification)
        .setPriority(NotificationCompat.PRIORITY_LOW);

// Issue the initial notification with zero progress.
int PROGRESS_MAX = 100;
int PROGRESS_CURRENT = 0;
builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
notificationManager.notify(notificationId, builder.build());

// Do the job that tracks the progress here.
// Usually, this is in a worker thread.
// To show progress, update PROGRESS_CURRENT and update the notification with:
// builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
// notificationManager.notify(notificationId, builder.build());

// When done, update the notification once more to remove the progress bar.
builder.setContentText("Download complete")
        .setProgress(0,0,false);
notificationManager.notify(notificationId, builder.build());

В конце операции progress должен равняться max . Вы можете оставить индикатор выполнения, чтобы показать, что операция выполнена, или удалить ее. В любом случае обновите текст уведомления, чтобы показать, что операция завершена. Чтобы удалить индикатор выполнения, вызовите setProgress(0, 0, false) .

Чтобы отобразить неопределенный индикатор выполнения (индикатор, который не указывает процент завершения), вызовите setProgress(0, 0, true) . Результатом является индикатор, имеющий тот же стиль, что и предыдущий индикатор выполнения, за исключением того, что это непрерывная анимация, не указывающая на завершение. Анимация прогресса выполняется до тех пор, пока вы не вызовете setProgress(0, 0, false) а затем обновите уведомление, чтобы удалить индикатор активности.

Не забудьте изменить текст уведомления, чтобы указать, что операция завершена.

Установить общесистемную категорию

Android использует предопределенные общесистемные категории, чтобы определить, следует ли беспокоить пользователя определенным уведомлением, когда пользователь включает режим «Не беспокоить» .

Если ваше уведомление попадает в одну из категорий уведомлений, определенных в NotificationCompat , например CATEGORY_ALARM , CATEGORY_REMINDER , CATEGORY_EVENT или CATEGORY_CALL , объявите его как таковое, передав соответствующую категорию в setCategory() :

Котлин

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setCategory(NotificationCompat.CATEGORY_MESSAGE)

Ява

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setCategory(NotificationCompat.CATEGORY_MESSAGE);

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

Показать срочное сообщение

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

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

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

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

Котлин

val fullScreenIntent = Intent(this, ImportantActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
    fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setFullScreenIntent(fullScreenPendingIntent, true)

Ява

Intent fullScreenIntent = new Intent(this, ImportantActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
        fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setFullScreenIntent(fullScreenPendingIntent, true);

Настройка видимости экрана блокировки

Чтобы контролировать уровень детализации, видимой в уведомлении с экрана блокировки, вызовите setVisibility() и укажите одно из следующих значений:

  • VISIBILITY_PUBLIC : полное содержимое уведомления отображается на экране блокировки.

  • VISIBILITY_SECRET : ни одна часть уведомления не отображается на экране блокировки.

  • VISIBILITY_PRIVATE : на экране блокировки отображается только основная информация, такая как значок уведомления и заголовок содержимого. Полное содержание уведомления не отображается.

Установив VISIBILITY_PRIVATE , вы также можете предоставить альтернативную версию содержимого уведомления, которая скрывает определенные детали. Например, приложение SMS может отображать уведомление «У вас 3 новых текстовых сообщения», но скрывает содержимое сообщения и отправителей. Чтобы предоставить это альтернативное уведомление, сначала создайте альтернативное уведомление с помощью NotificationCompat.Builder как обычно. Затем прикрепите альтернативное уведомление к обычному с помощью setPublicVersion() .

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

Обновить уведомление

Чтобы обновить уведомление после его отправки, снова вызовите NotificationManagerCompat.notify() , передав ему тот же идентификатор, который вы использовали ранее. Если предыдущее уведомление отклонено, вместо него создается новое уведомление.

При желании вы можете вызвать setOnlyAlertOnce() , чтобы ваше уведомление прерывало пользователя — с помощью звука, вибрации или визуальных подсказок — только при первом появлении уведомления, а не для последующих обновлений.

Удалить уведомление

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

  • Пользователь отклоняет уведомление.
  • Пользователь касается уведомления, если вы вызываете setAutoCancel() при создании уведомления.
  • Вы вызываете cancel() для определенного идентификатора уведомления. Этот метод также удаляет текущие уведомления.
  • Вы вызываете cancelAll() , который удаляет все отправленные вами ранее уведомления.
  • Указанная продолжительность истечет, если вы установите тайм-аут при создании уведомления с помощью setTimeoutAfter() . При необходимости вы можете отменить уведомление до истечения указанного времени ожидания.

Рекомендации по использованию приложений для обмена сообщениями

Примите во внимание перечисленные здесь рекомендации при создании уведомлений для приложений обмена сообщениями и чата.

Использовать стиль сообщений

Начиная с Android 7.0 (уровень API 24), Android предоставляет шаблон стиля уведомлений специально для содержимого сообщений. Используя класс NotificationCompat.MessagingStyle , вы можете изменить несколько меток, отображаемых в уведомлении, включая заголовок беседы, дополнительные сообщения и представление содержимого уведомления.

В следующем фрагменте кода показано, как настроить стиль уведомления с помощью класса MessagingStyle .

Котлин

val user = Person.Builder()
    .setIcon(userIcon)
    .setName(userName)
    .build()

val notification = NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("2 new messages with $sender")
    .setContentText(subject)
    .setSmallIcon(R.drawable.new_message)
    .setStyle(NotificationCompat.MessagingStyle(user)
        .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
        .addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
    )
    .build()

Ява

Person user = new Person.Builder()
    .setIcon(userIcon)
    .setName(userName)
    .build();

Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("2 new messages with " + sender)
    .setContentText(subject)
    .setSmallIcon(R.drawable.new_message)
    .setStyle(new NotificationCompat.MessagingStyle(user)
        .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
        .addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
    )
    .build();

Начиная с Android 9.0 (уровень API 28), также необходимо использовать класс Person , чтобы обеспечить оптимальную визуализацию уведомления и его аватаров.

При использовании NotificationCompat.MessagingStyle выполните следующие действия:

  • Вызовите MessagingStyle.setConversationTitle() чтобы задать заголовок для групповых чатов с участием более двух человек. Хорошим названием беседы может быть название группового чата или, если у него нет названия, список участников беседы. Без этого сообщение может быть ошибочно принято за принадлежащее индивидуальному разговору с отправителем самого последнего сообщения в разговоре.
  • Используйте метод MessagingStyle.setData() для включения мультимедийных сообщений, таких как изображения. Поддерживаются MIME-типы шаблона image/*.

Используйте прямой ответ

Прямой ответ позволяет пользователю отвечать на сообщение прямо в режиме реального времени.

  • После того как пользователь ответит встроенным действием ответа, используйте MessagingStyle.addMessage() чтобы обновить уведомление MessagingStyle , не отзывая и не отменяя уведомление. Если вы не отмените уведомление, пользователь сможет отправить несколько ответов на уведомление.
  • Чтобы сделать встроенное действие ответа совместимым с Wear OS, вызовите Action.WearableExtender.setHintDisplayInlineAction(true) .
  • Используйте метод addHistoricMessage() чтобы предоставить контекст для диалога с прямым ответом, добавив в уведомление исторические сообщения.

Включить умный ответ

  • Чтобы включить интеллектуальный ответ, вызовите setAllowGeneratedResponses(true) для действия ответа. Это приводит к тому, что ответы Smart Reply становятся доступными для пользователей, когда уведомление подключается к устройству Wear OS. Ответы Smart Reply генерируются полностью контролируемой моделью машинного обучения с использованием контекста, предоставленного уведомлением NotificationCompat.MessagingStyle , и никакие данные не загружаются в Интернет для создания ответов.

Добавить метаданные уведомления

  • Назначьте метаданные уведомлений, чтобы сообщить системе, как обрабатывать уведомления вашего приложения, когда устройство находится в Do Not Disturb mode . Например, используйте метод addPerson() или setCategory(Notification.CATEGORY_MESSAGE) чтобы переопределить режим «Не беспокоить».
,

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

Код на этой странице использует API NotificationCompat из библиотеки AndroidX. Эти API позволяют добавлять функции, доступные только в более новых версиях Android, сохраняя при этом совместимость с Android 9 (уровень API 28). Однако некоторые функции, такие как встроенное действие ответа, приводят к остановке работы в более ранних версиях.

Добавьте базовую библиотеку AndroidX

Хотя большинство проектов, созданных с помощью Android Studio, включают необходимые зависимости для использования NotificationCompat , убедитесь, что ваш файл build.gradle на уровне модуля содержит следующую зависимость:

классный

dependencies {
    implementation "androidx.core:core:2.2.0"
}

Котлин

dependencies {
    implementation("androidx.core:core-ktx:2.2.0")
}

Создайте базовое уведомление

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

Рисунок 1. Уведомление со значком, заголовком и текстом.

Дополнительные сведения о каждой части уведомления см. в разделе «Анатомия уведомления» .

Объявить разрешение времени выполнения

Android 13 (уровень API 33) и более поздних версий поддерживает разрешение среды выполнения для публикации неисключенных уведомлений (включая службы переднего плана (FGS)) из приложения.

Разрешение, которое вам необходимо объявить в файле манифеста вашего приложения, отображается в следующем фрагменте кода:

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>

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

Установите содержание уведомления

Для начала задайте содержимое и канал уведомления с помощью объекта NotificationCompat.Builder . В следующем примере показано, как создать уведомление со следующим:

  • Небольшой значок, установленный с помощью setSmallIcon() . Это единственный необходимый контент, видимый пользователю.

  • Заголовок, установленный с помощью setContentTitle() .

  • Основной текст, заданный с помощью setContentText() .

  • Приоритет уведомления, установленный setPriority() . Приоритет определяет, насколько навязчивым будет уведомление на Android 7.1 и более ранних версиях. Для Android 8.0 и более поздних версий вместо этого установите важность канала, как показано в следующем разделе.

Котлин

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle(textTitle)
        .setContentText(textContent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

Ява

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle(textTitle)
        .setContentText(textContent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

Конструктор NotificationCompat.Builder требует, чтобы вы указали идентификатор канала. Это необходимо для совместимости с Android 8.0 (уровень API 26) и более поздних версий, но игнорируется более ранними версиями.

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

Рисунок 2. Расширяемое уведомление в свернутом и развернутом виде.

Если вы хотите, чтобы ваше уведомление было длиннее, вы можете включить расширяемое уведомление, добавив шаблон стиля с помощью setStyle() . Например, следующий код создает большую текстовую область:

Котлин

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

Ява

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(new NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

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

Создайте канал и установите важность

Прежде чем вы сможете доставить уведомление на Android 8.0 и более поздних версиях, зарегистрируйте канал уведомлений вашего приложения в системе, передав экземпляр NotificationChannel в createNotificationChannel() . Следующий код заблокирован условием версии SDK_INT :

Котлин

private fun createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is not in the Support Library.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = getString(R.string.channel_name)
        val descriptionText = getString(R.string.channel_description)
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
            description = descriptionText
        }
        // Register the channel with the system.
        val notificationManager: NotificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

Ява

private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is not in the Support Library.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        // Register the channel with the system; you can't change the importance
        // or other notification behaviors after this.
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}

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

Конструктор NotificationChannel требует importance , используя одну из констант класса NotificationManager . Этот параметр определяет, как прерывать пользователя при любом уведомлении, принадлежащем этому каналу. Установите приоритет с помощью setPriority() для поддержки Android 7.1 и более ранних версий, как показано в предыдущем примере.

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

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

Установите действие касания уведомления

Каждое уведомление должно реагировать на нажатие, обычно для открытия действия в вашем приложении, соответствующего уведомлению. Для этого укажите намерение содержимого, определенное с помощью объекта PendingIntent , и передайте его в setContentIntent() .

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

Котлин

// Create an explicit intent for an Activity in your app.
val intent = Intent(this, AlertDetails::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)

val builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        // Set the intent that fires when the user taps the notification.
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)

Ява

// Create an explicit intent for an Activity in your app.
Intent intent = new Intent(this, AlertDetails.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        // Set the intent that fires when the user taps the notification.
        .setContentIntent(pendingIntent)
        .setAutoCancel(true);

Этот код вызывает setAutoCancel() , который автоматически удаляет уведомление, когда пользователь нажимает на него.

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

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

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

Дополнительные сведения о различных способах настройки назначения уведомления см. в разделе «Начать действие из уведомления» .

Показать уведомление

Чтобы уведомление появилось, вызовите NotificationManagerCompat.notify() , передав ему уникальный идентификатор уведомления и результат NotificationCompat.Builder.build() . Это показано в следующем примере:

Котлин

with(NotificationManagerCompat.from(this)) {
    if (ActivityCompat.checkSelfPermission(
            this@MainActivity,
            Manifest.permission.POST_NOTIFICATIONS
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        // TODO: Consider calling
        // ActivityCompat#requestPermissions
        // here to request the missing permissions, and then overriding
        // public fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>,
        //                                        grantResults: IntArray)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.

        return@with
    }
    // notificationId is a unique int for each notification that you must define.
    notify(NOTIFICATION_ID, builder.build())
}

Ява

with(NotificationManagerCompat.from(this)) {
   if (ActivityCompat.checkSelfPermission(
           this@MainActivity,
           Manifest.permission.POST_NOTIFICATIONS
       ) != PackageManager.PERMISSION_GRANTED
   ) {
       // TODO: Consider calling
       // ActivityCompat#requestPermissions
       // here to request the missing permissions, and then overriding
       // public void onRequestPermissionsResult(int requestCode, String[] permissions,
       //                                        int[] grantResults)
       // to handle the case where the user grants the permission. See the documentation
       // for ActivityCompat#requestPermissions for more details.

       return
   }
   // notificationId is a unique int for each notification that you must define.
   notify(NOTIFICATION_ID, builder.build())
}

Сохраните идентификатор уведомления, который вы передаете в NotificationManagerCompat.notify() , поскольку он понадобится вам, когда вы захотите обновить или удалить уведомление .

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

Добавьте кнопки действий

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

Рисунок 3. Уведомление с одной кнопкой действия.

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

Например, следующий код показывает, как отправить широковещательную рассылку определенному получателю:

Котлин

val ACTION_SNOOZE = "snooze"

val snoozeIntent = Intent(this, MyBroadcastReceiver::class.java).apply {
    action = ACTION_SNOOZE
    putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val snoozePendingIntent: PendingIntent =
    PendingIntent.getBroadcast(this, 0, snoozeIntent, 0)
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, getString(R.string.snooze),
                snoozePendingIntent)

Ява

String ACTION_SNOOZE = "snooze"

Intent snoozeIntent = new Intent(this, MyBroadcastReceiver.class);
snoozeIntent.setAction(ACTION_SNOOZE);
snoozeIntent.putExtra(EXTRA_NOTIFICATION_ID, 0);
PendingIntent snoozePendingIntent =
        PendingIntent.getBroadcast(this, 0, snoozeIntent, 0);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, getString(R.string.snooze),
                snoozePendingIntent);

Дополнительные сведения о создании BroadcastReceiver для выполнения фоновой работы см. в обзоре Broadcasts .

Если вместо этого вы пытаетесь создать уведомление с помощью кнопок воспроизведения мультимедиа, например приостановки и пропуска треков, узнайте, как создать уведомление с помощью элементов управления мультимедиа .

Добавить действие прямого ответа

Действие прямого ответа, представленное в Android 7.0 (уровень API 24), позволяет пользователям вводить текст непосредственно в уведомление. Затем текст доставляется в ваше приложение без открытия действия. Например, вы можете использовать действие прямого ответа, чтобы позволить пользователям отвечать на текстовые сообщения или обновлять списки задач из уведомления.

Рисунок 4. Нажатие кнопки «Ответить» открывает ввод текста.

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

Добавьте кнопку ответить

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

  1. Создайте экземпляр RemoteInput.Builder , который можно добавить к действию уведомления. Конструктор этого класса принимает строку, которую система использует в качестве ключа для ввода текста. Ваше приложение позже использует этот ключ для получения текста ввода.

    Котлин

      // Key for the string that's delivered in the action's intent.
      private val KEY_TEXT_REPLY = "key_text_reply"
      var replyLabel: String = resources.getString(R.string.reply_label)
      var remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
          setLabel(replyLabel)
          build()
      }
      

    Ява

      // Key for the string that's delivered in the action's intent.
      private static final String KEY_TEXT_REPLY = "key_text_reply";
    
      String replyLabel = getResources().getString(R.string.reply_label);
      RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
              .setLabel(replyLabel)
              .build();
      
  2. Создайте PendingIntent для действия ответа.

    Котлин

      // Build a PendingIntent for the reply action to trigger.
      var replyPendingIntent: PendingIntent =
          PendingIntent.getBroadcast(applicationContext,
              conversation.getConversationId(),
              getMessageReplyIntent(conversation.getConversationId()),
              PendingIntent.FLAG_UPDATE_CURRENT)
      

    Ява

      // Build a PendingIntent for the reply action to trigger.
      PendingIntent replyPendingIntent =
              PendingIntent.getBroadcast(getApplicationContext(),
                      conversation.getConversationId(),
                      getMessageReplyIntent(conversation.getConversationId()),
                      PendingIntent.FLAG_UPDATE_CURRENT);
      
  3. Прикрепите объект RemoteInput к действию с помощью addRemoteInput() .

    Котлин

      // Create the reply action and add the remote input.
      var action: NotificationCompat.Action =
          NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
              getString(R.string.label), replyPendingIntent)
              .addRemoteInput(remoteInput)
              .build()
      

    Ява

      // Create the reply action and add the remote input.
      NotificationCompat.Action action =
              new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
                      getString(R.string.label), replyPendingIntent)
                      .addRemoteInput(remoteInput)
                      .build();
      
  4. Примените действие к уведомлению и выдайте уведомление.

    Котлин

      // Build the notification and add the action.
      val newMessageNotification = Notification.Builder(context, CHANNEL_ID)
              .setSmallIcon(R.drawable.ic_message)
              .setContentTitle(getString(R.string.title))
              .setContentText(getString(R.string.content))
              .addAction(action)
              .build()
    
      // Issue the notification.
      with(NotificationManagerCompat.from(this)) {
          notificationManager.notify(notificationId, newMessageNotification)
      }
      

    Ява

      // Build the notification and add the action.
      Notification newMessageNotification = new Notification.Builder(context, CHANNEL_ID)
              .setSmallIcon(R.drawable.ic_message)
              .setContentTitle(getString(R.string.title))
              .setContentText(getString(R.string.content))
              .addAction(action)
              .build();
    
      // Issue the notification.
      NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
      notificationManager.notify(notificationId, newMessageNotification);
      

Система предлагает пользователю ввести ответ, когда он запускает действие уведомления, как показано на рисунке 4.

Получить ввод пользователя из ответа

Чтобы получить пользовательский ввод из пользовательского интерфейса ответа на уведомление, вызовите RemoteInput.getResultsFromIntent() , передав ему Intent полученное вашим BroadcastReceiver :

Котлин

private fun getMessageText(intent: Intent): CharSequence? {
    return RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY)
}

Ява

private CharSequence getMessageText(Intent intent) {
    Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
    if (remoteInput != null) {
        return remoteInput.getCharSequence(KEY_TEXT_REPLY);
    }
    return null;
 }

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

Котлин

// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
val repliedNotification = Notification.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentText(getString(R.string.replied))
        .build()

// Issue the new notification.
NotificationManagerCompat.from(this).apply {
    notificationManager.notify(notificationId, repliedNotification)
}

Ява

// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
Notification repliedNotification = new Notification.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentText(getString(R.string.replied))
        .build();

// Issue the new notification.
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(notificationId, repliedNotification);

При работе с этим новым уведомлением используйте контекст, переданный методу onReceive() получателя.

Добавьте ответ в нижнюю часть уведомления, вызвав setRemoteInputHistory() . Однако если вы создаете приложение для обмена сообщениями, создайте уведомление в стиле обмена сообщениями и добавьте новое сообщение в беседу.

Дополнительные советы по уведомлениям из приложений для обмена сообщениями см. в разделе «Рекомендации по использованию приложений для обмена сообщениями» .

Добавить индикатор выполнения

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

Рисунок 5. Индикатор выполнения во время операции.

Если вы можете оценить, какая часть операции завершена в любой момент, используйте «определенную» форму индикатора, как показано на рисунке 5, вызвав setProgress(max, progress, false) . Первый параметр — это «завершенное» значение, например 100. Второй — насколько завершено. Последнее указывает на то, что это определенный индикатор выполнения.

По мере выполнения операции постоянно вызывайте setProgress(max, progress, false) с обновленным значением progress и повторно отправляйте уведомление, как показано в следующем примере.

Котлин

val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
    setContentTitle("Picture Download")
    setContentText("Download in progress")
    setSmallIcon(R.drawable.ic_notification)
    setPriority(NotificationCompat.PRIORITY_LOW)
}
val PROGRESS_MAX = 100
val PROGRESS_CURRENT = 0
NotificationManagerCompat.from(this).apply {
    // Issue the initial notification with zero progress.
    builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false)
    notify(notificationId, builder.build())

    // Do the job that tracks the progress here.
    // Usually, this is in a worker thread.
    // To show progress, update PROGRESS_CURRENT and update the notification with:
    // builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
    // notificationManager.notify(notificationId, builder.build());

    // When done, update the notification once more to remove the progress bar.
    builder.setContentText("Download complete")
            .setProgress(0, 0, false)
    notify(notificationId, builder.build())
}

Ява

...
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
builder.setContentTitle("Picture Download")
        .setContentText("Download in progress")
        .setSmallIcon(R.drawable.ic_notification)
        .setPriority(NotificationCompat.PRIORITY_LOW);

// Issue the initial notification with zero progress.
int PROGRESS_MAX = 100;
int PROGRESS_CURRENT = 0;
builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
notificationManager.notify(notificationId, builder.build());

// Do the job that tracks the progress here.
// Usually, this is in a worker thread.
// To show progress, update PROGRESS_CURRENT and update the notification with:
// builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
// notificationManager.notify(notificationId, builder.build());

// When done, update the notification once more to remove the progress bar.
builder.setContentText("Download complete")
        .setProgress(0,0,false);
notificationManager.notify(notificationId, builder.build());

В конце операции progress должен равняться max . Вы можете оставить индикатор выполнения, чтобы показать, что операция выполнена, или удалить ее. В любом случае обновите текст уведомления, чтобы показать, что операция завершена. Чтобы удалить индикатор выполнения, вызовите setProgress(0, 0, false) .

Чтобы отобразить неопределенный индикатор выполнения (индикатор, который не указывает процент завершения), вызовите setProgress(0, 0, true) . Результатом является индикатор, имеющий тот же стиль, что и предыдущий индикатор выполнения, за исключением того, что это непрерывная анимация, не указывающая на завершение. Анимация прогресса выполняется до тех пор, пока вы не вызовете setProgress(0, 0, false) а затем обновите уведомление, чтобы удалить индикатор активности.

Не забудьте изменить текст уведомления, чтобы указать, что операция завершена.

Установить общесистемную категорию

Android использует предопределенные общесистемные категории, чтобы определить, следует ли беспокоить пользователя определенным уведомлением, когда пользователь включает режим «Не беспокоить» .

Если ваше уведомление попадает в одну из категорий уведомлений, определенных в NotificationCompat , например CATEGORY_ALARM , CATEGORY_REMINDER , CATEGORY_EVENT или CATEGORY_CALL , объявите его как таковое, передав соответствующую категорию в setCategory() :

Котлин

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setCategory(NotificationCompat.CATEGORY_MESSAGE)

Ява

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setCategory(NotificationCompat.CATEGORY_MESSAGE);

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

Показать срочное сообщение

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

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

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

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

Котлин

val fullScreenIntent = Intent(this, ImportantActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
    fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setFullScreenIntent(fullScreenPendingIntent, true)

Ява

Intent fullScreenIntent = new Intent(this, ImportantActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
        fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setFullScreenIntent(fullScreenPendingIntent, true);

Настройка видимости экрана блокировки

Чтобы контролировать уровень детализации, видимой в уведомлении с экрана блокировки, вызовите setVisibility() и укажите одно из следующих значений:

  • VISIBILITY_PUBLIC : полное содержимое уведомления отображается на экране блокировки.

  • VISIBILITY_SECRET : ни одна часть уведомления не отображается на экране блокировки.

  • VISIBILITY_PRIVATE : на экране блокировки отображается только основная информация, такая как значок уведомления и заголовок содержимого. Полное содержание уведомления не отображается.

Установив VISIBILITY_PRIVATE , вы также можете предоставить альтернативную версию содержимого уведомления, которая скрывает определенные детали. Например, приложение SMS может отображать уведомление «У вас 3 новых текстовых сообщения», но скрывает содержимое сообщения и отправителей. Чтобы предоставить это альтернативное уведомление, сначала создайте альтернативное уведомление с помощью NotificationCompat.Builder как обычно. Затем прикрепите альтернативное уведомление к обычному с помощью setPublicVersion() .

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

Обновить уведомление

Чтобы обновить уведомление после его отправки, снова вызовите NotificationManagerCompat.notify() , передав ему тот же идентификатор, который вы использовали ранее. Если предыдущее уведомление отклонено, вместо него создается новое уведомление.

При желании вы можете вызвать setOnlyAlertOnce() , чтобы ваше уведомление прерывало пользователя — с помощью звука, вибрации или визуальных подсказок — только при первом появлении уведомления, а не для последующих обновлений.

Удалить уведомление

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

  • Пользователь отклоняет уведомление.
  • Пользователь касается уведомления, если вы вызываете setAutoCancel() при создании уведомления.
  • Вы вызываете cancel() для определенного идентификатора уведомления. Этот метод также удаляет текущие уведомления.
  • Вы вызываете cancelAll() , который удаляет все отправленные вами ранее уведомления.
  • Указанная продолжительность истечет, если вы установите тайм-аут при создании уведомления с помощью setTimeoutAfter() . При необходимости вы можете отменить уведомление до истечения указанного времени ожидания.

Рекомендации по использованию приложений для обмена сообщениями

Примите во внимание перечисленные здесь рекомендации при создании уведомлений для приложений обмена сообщениями и чата.

Использовать стиль сообщений

Начиная с Android 7.0 (уровень API 24), Android предоставляет шаблон стиля уведомлений специально для содержимого сообщений. Используя класс NotificationCompat.MessagingStyle , вы можете изменить несколько меток, отображаемых в уведомлении, включая заголовок беседы, дополнительные сообщения и представление содержимого уведомления.

В следующем фрагменте кода показано, как настроить стиль уведомления с помощью класса MessagingStyle .

Котлин

val user = Person.Builder()
    .setIcon(userIcon)
    .setName(userName)
    .build()

val notification = NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("2 new messages with $sender")
    .setContentText(subject)
    .setSmallIcon(R.drawable.new_message)
    .setStyle(NotificationCompat.MessagingStyle(user)
        .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
        .addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
    )
    .build()

Ява

Person user = new Person.Builder()
    .setIcon(userIcon)
    .setName(userName)
    .build();

Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("2 new messages with " + sender)
    .setContentText(subject)
    .setSmallIcon(R.drawable.new_message)
    .setStyle(new NotificationCompat.MessagingStyle(user)
        .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
        .addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
    )
    .build();

Начиная с Android 9.0 (уровень API 28), также необходимо использовать класс Person , чтобы обеспечить оптимальную визуализацию уведомления и его аватаров.

При использовании NotificationCompat.MessagingStyle выполните следующие действия:

  • Вызовите MessagingStyle.setConversationTitle() чтобы задать заголовок для групповых чатов с участием более двух человек. Хорошим названием беседы может быть название группового чата или, если у него нет названия, список участников беседы. Без этого сообщение может быть ошибочно принято за принадлежащее индивидуальному разговору с отправителем самого последнего сообщения в разговоре.
  • Используйте метод MessagingStyle.setData() для включения мультимедийных сообщений, таких как изображения. Поддерживаются MIME-типы шаблона image/*.

Используйте прямой ответ

Прямой ответ позволяет пользователю отвечать на сообщение прямо в режиме реального времени.

  • После того как пользователь ответит встроенным действием ответа, используйте MessagingStyle.addMessage() чтобы обновить уведомление MessagingStyle , не отзывая и не отменяя уведомление. Если вы не отмените уведомление, пользователь сможет отправить несколько ответов на уведомление.
  • Чтобы сделать встроенное действие ответа совместимым с Wear OS, вызовите Action.WearableExtender.setHintDisplayInlineAction(true) .
  • Используйте метод addHistoricMessage() чтобы предоставить контекст для диалога с прямым ответом, добавив в уведомление исторические сообщения.

Включить умный ответ

  • Чтобы включить интеллектуальный ответ, вызовите setAllowGeneratedResponses(true) для действия ответа. Это приводит к тому, что ответы Smart Reply становятся доступными для пользователей, когда уведомление подключается к устройству Wear OS. Ответы Smart Reply генерируются полностью контролируемой моделью машинного обучения с использованием контекста, предоставленного уведомлением NotificationCompat.MessagingStyle , и никакие данные не загружаются в Интернет для создания ответов.

Добавить метаданные уведомления

  • Назначьте метаданные уведомлений, чтобы сообщить системе, как обрабатывать уведомления вашего приложения, когда устройство находится в Do Not Disturb mode . Например, используйте метод addPerson() или setCategory(Notification.CATEGORY_MESSAGE) чтобы переопределить режим «Не беспокоить».