Notifications. Action кнопки. Reply.

В Android 4.1 з'явилася можливість додавати кнопки в повідомлення.

Для цього використовується метод addAction.

Intent deleteIntent = new Intent(this, MyService.class);
deleteIntent.setAction("ru.startandroid.notifications.action_delete");
PendingIntent deletePendingIntent = PendingIntent.getService(this, 0, deleteIntent, 0);
 
NotificationCompat.Builder builder =
       new NotificationCompat.Builder(this)
               .setSmallIcon(R.mipmap.ic_launcher)
               .setContentTitle("Title")
               .setContentText("Notification text")
               .addAction(android.R.drawable.ic_delete, "Delete", deletePendingIntent);

Спочатку створюємо PendingIntent, який буде викликаний після натискання на кнопку. Потім передаємо його в метод addAction, а разом із ним іконку і текст для кнопки.

Під час розкриття повідомлення буде відображена кнопка.

Після натискання на кнопку повідомлення саме не закриється. Якщо вам необхідно його закрити, використовуйте cancel в обробнику натискання.

Ви можете додати до трьох Action кнопок. Кнопки не повинні дублювати дію, яка відбувається після натискання на повідомлення.

На останніх версіях Android чомусь не відображається іконка кнопки, тільки текст.

Reply

Починаючи з API 24 з'явилася можливість додати в повідомлення рядок введення. Це може бути зручно, наприклад, у чат-додатках. Користувач зможе відповісти на повідомлення прямо з нотифікації.

Розглянемо приклад реалізації:

// id
int itemId = ...;
 
// Intent
Intent intent = new Intent(this, MyService.class);
intent.setAction(ACTION_REPLY);
intent.putExtra(EXTRA_ITEM_ID, itemId);
 
// PendingIntent
PendingIntent replyPendingIntent =
       PendingIntent.getService(getApplicationContext(),
               itemId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
 
// RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_TEXT_REPLY)
       .setLabel("Type message")
       .build();
 
// Action
NotificationCompat.Action action =
       new NotificationCompat.Action.Builder(android.R.drawable.ic_menu_send,
               "Reply", replyPendingIntent)
               .addRemoteInput(remoteInput)
               .build();
 
// Notification builder
NotificationCompat.Builder builder =
       new NotificationCompat.Builder(this)
               .setSmallIcon(R.mipmap.ic_launcher)
               .setContentTitle("Title")
               .setContentText("Notification text")
               .addAction(action);
 
// Notification
Notification notification = builder.build();
 
// Show notification
NotificationManager notificationManager =
       (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(itemId, notification);

Розбираємо код по порядку.

У нас є якийсь itemId. Це може бути, наприклад, id чату, в який прийшло нове повідомлення.

Створюємо Intent і PendingIntent. Тут нічого нового. Ми будемо викликати сервіс MyService і передавати йому itemId. У PendingIntent використовуємо itemId як requestCode.

Далі створюємо RemoteInput. Тут налаштовуємо все, що стосується поля введення, яке буде відображено в повідомленні. У конструкторі білдера необхідно вказати ключ, який ми надалі будемо використовувати, щоб із Bundle дістати текст, який введе користувач. У метод setLabel можна передати текст, який буде використаний як hint (підказка) у полі введення.

Створюємо Action кнопку за допомогою білдера. Передаємо туди стандартний набір: іконку, текст і PendingIntent. А в метод addRemoteInput передаємо раніше створений RemoteInput. Це буде Action кнопка Reply, після натискання на яку з'являтиметься рядок введення.

Далі використовуємо створений Action у білдері сповіщення, створюємо сповіщення і відображаємо його.

У методі notify використовуємо itemId. Відповідно, знаючи id чату, ми завжди зможемо оновити або видалити повідомлення.

Зверніть увагу, що PendingIntent, який ми створюємо і використовуємо в Action кнопці Reply, буде використаний не після натискання на сповіщення, і навіть не після натискання на Reply. Він буде використаний, коли користувач натисне на кнопку надсилання тексту.

У цьому прикладі, до речі, після натискання на сповіщення, нічого не станеться, тому що в білдері сповіщення я не використовував метод setContentIntent, щоб не ускладнювати приклад.

Запускаємо

У повідомленні створюється Action кнопка Reply. Вона відкриває рядок введення.

Після натискання на кнопку надсилання тексту система запускає MyService, який ми вказували в PedningIntent, і відображає прогресбар. Але він буде крутитися нескінченно, поки ми програмно не оновимо або не видалимо повідомлення.

Давайте подивимося, як у MyService ми можемо отримати введений користувачем текст і прибрати прогрессбар з повідомлення:

if (ACTION_REPLY.equals(intent.getAction())) {
 
   // Get reply text
   CharSequence replyText = null;
   Bundle results = RemoteInput.getResultsFromIntent(intent);
   if (results != null) {
       replyText = results.getCharSequence(EXTRA_TEXT_REPLY);
   }
 
   // Get itemId
   int itemId = intent.getIntExtra(EXTRA_ITEM_ID, 0);
 
   // Perform operations with replyText and itemId
   ...
 
   // Create new notification
   Notification repliedNotification =
           new NotificationCompat.Builder(getBaseContext())
                   .setSmallIcon(R.mipmap.ic_launcher)
                   .setContentText("Replied")
                   .build();
 
   // Update notification
   NotificationManager mNotificationManager =
           (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
   mNotificationManager.notify(itemId, repliedNotification);
 
}

Методом RemoteInput.getResultsFromIntent дістаємо Bundle з Intent. З цього Bundle можемо дістати текст, який вводив користувач у повідомленні. Для цього використовуємо ключ EXTRA_TEXT_REPLY (який раніше використовували в білдері RemoteInput).

Далі з Intent дістаємо itemId.

Тепер у нас є id чату і текст, який ввів користувач. Можемо зберегти його в БД, відправити на сервер або зробити ще щось. Це залежить від логіки програми.

Далі нам необхідно розібратися з повідомленням. Нагадаю, що після надсилання тексту воно відображає прогрессбар. У цьому прикладі ми створюємо просте сповіщення з текстом Replied і замінюємо ним (використовуючи той самий itemId у методі notify) те сповіщення, з якого було надіслано текст.

Пробуємо ще раз надіслати текст із сповіщення Цього разу ми в обробнику оновили сповіщення і прогресбар зник.

Що ви будете робити з повідомленням після надсилання тексту - це ваше рішення. Наприклад, ви можете просто видалити його. Або, якщо ви в повідомленні відображаєте останні повідомлення чату, ви можете оновити це повідомлення з урахуванням нового повідомлення і знову зробити там кнопку Reply.