Show notifications что это
Android Notifications. Оповещения через Status Bar
Добрый день, хабровчане. Давно занимаюсь разработкой под Android и хотелось бы рассказать сообществу о правильном подходе к созданию уведомлений.
Ниже, помимо описанного ранее, мы рассмотрим добавление прогрессбара, обработку события по нажатию на уведомлений, различные варианты состояний уведомлений. Рассмотрим добавленный на днях в Compatibility library Notification.Builder. А также поговорим о рекомендациям по UI (design guidlines), которые гугл рекомендует соблюдать при создании уведомлений.
Guidlines
Как советуют разработчики Android в официальном гайдлайне
Когда показывать уведомления:
Когда не стоит показывать уведомления:
Хорошая практика:
Архитектура:
В качестве утилитки, отвечающей за уведомления, я в своих приложениях использую singleton, к которому можно обратиться из любого класса приложения, нужно лишь иметь ссылку на context.
В ней всегда хранятся ссылки на все созданные во время работы приложения уведомления, которые ещё отображены в статус-баре.
А для присвоения новому уведомлению уникального id используется нехитрый механизм обращения к приватному целочисленному полю, которое каждый раз увеличивается на единицу.
private static NotificationUtils instance;
private static Context context;
private NotificationManager manager; // Системная утилита, упарляющая уведомлениями
private int lastId = 0; //постоянно увеличивающееся поле, уникальный номер каждого уведомления
private HashMap notifications; //массив ключ-значение на все отображаемые пользователю уведомления
Создание уведомления с помощью NotificationCompat.Builder:
Для того чтобы воспользоваться классами, входящими в библиотеку поддержки прошлых версий (Compatibility library), нужно добавить в проект библиотеку из папки /extras/android/support/v4/android-support-v4.jar
Если же проект нацелен на Android 3.0 и выше, то добавлять ничего не нужно достаточно обратиться к Notification.Builder
public int createInfoNotification( String message) <
Intent notificationIntent = new Intent(context, HomeActivity. class ); // по клику на уведомлении откроется HomeActivity
NotificationCompat.Builder nb = new NotificationCompat.Builder(context)
//NotificationCompat.Builder nb = new NotificationBuilder(context) //для версии Android > 3.0
.setSmallIcon(R.drawable.ic_action_picture) //иконка уведомления
.setAutoCancel( true ) //уведомление закроется по клику на него
.setTicker(message) //текст, который отобразится вверху статус-бара при создании уведомления
.setContentText(message) // Основной текст уведомления
.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
.setWhen(System.currentTimeMillis()) //отображаемое время уведомления
.setContentTitle( «AppName» ) //заголовок уведомления
.setDefaults(Notification.DEFAULT_ALL); // звук, вибро и диодный индикатор выставляются по умолчанию
Notification notification = nb.getNotification(); //генерируем уведомление
manager.notify(lastId, notification); // отображаем его пользователю.
notifications.put(lastId, notification); //теперь мы можем обращаться к нему по id
return lastId++;
>
Создание уведомления с произвольным отображением (Custom layout):
/**
* генерация уведомления с ProgressBar, иконкой и заголовком
*
* @param text заголовок уведомления
* @param topMessage сообщение, уотображаемое в закрытом статус-баре при появлении уведомления
* @return View уведомления.
*/
private RemoteViews createProgressNotification( String text, String topMessage) <
Notification notification = new Notification(R.drawable.ic_stat_example, topMessage, System.currentTimeMillis());
RemoteViews contentView = new RemoteViews(context.getPackageName(), R.layout.notification_download_layout);
contentView.setProgressBar(R.id.notification_download_layout_progressbar, 100, 0, false );
contentView.setTextViewText(R.id.notification_download_layout_title, text);
notification.contentView = contentView;
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_ONLY_ALERT_ONCE;
Intent notificationIntent = new Intent(context, NotificationUtils. class );
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
manager.notify(lastId, notification);
notifications.put(lastId, notification);
return contentView;
>
LinearLayout
android:layout_width =»fill_parent»
android:layout_height =»wrap_content»
android:orientation =»horizontal» >
ImageView
android:id =»@+id/notification_download_layout_image»
android:layout_width =»wrap_content»
android:layout_height =»wrap_content»
android:src =»@drawable/ic_stat_example»
android:layout_gravity =»center_vertical» />
TextView
android:id =»@+id/notification_download_layout_title»
style =»@style/NotificationTitle»
android:layout_width =»wrap_content»
android:layout_height =»wrap_content»
android:layout_alignParentTop =»true»
android:layout_marginLeft =»10dip»
android:singleLine =»true»
android:text =»notification_download_layout_title»
android:layout_gravity =»center_vertical» />
LinearLayout >
ProgressBar
android:id =»@+id/notification_download_layout_progressbar»
style =»?android:attr/progressBarStyleHorizontal»
android:layout_width =»fill_parent»
android:layout_height =»wrap_content»
android:layout_marginTop =»4dp»
android:progress =»0″ />
в андроид 2.3 и выше ( API >10) был создан специальный ресурс, в котором системная тема указывает цвета текста уведомений. Из-за этого в старых версиях приходится использовать костыль:
В файл res/values/styles.xml прописываем:
А для поддержки API >10 Создаем файл res/values-v9/styles.xml и вписываем:
Теперь из кода нашего приложения обращаемся к утилите:
NotificationUtils n = NotificationUtils.getInstance(getActivity());
n.createInfoNotification( «info notification» );
Создаем уведомление с прогресс-баром:
int pbId = NotificationUtils.getInstance(getActivity()).createDownloadNotification( «downloading video» );
И во время выполнения потока постоянно обновляем прогресс вызовом:
В итоге получаем:
Как видно — нижнее уведомление, созданное нами при помощи билдера может быть удалено в любой момент. А уведомление с прогресс-баром размещается в верхнем блоке уведомлений, в котором пользователь не может очистить уведомления.
И напоследок маленькая хитрость:
Если не хотите дублирования в стеке одних и тех же Activity — поставьте в манифесте к нужной activity
android:launchMode=»singleTop»
Полный список
Отображение
Код создания простого уведомления выглядит так:
Используем билдер, в котором указываем иконку, заголовок и текст для уведомления. Методом build получаем готовое уведомление.
Далее используем NotificationManager и его метод notify, чтобы показать созданное уведомление. Кроме notification, требуется передать id. Это необходимо, чтобы в дальнейшем мы могли использовать этот id для обновления или удаления уведомления.
Конструктор new NotificationCompat.Builder(Context) будет помечен как Deprecated, если вы используете библиотеку appCompat версии 26 и выше. Так получилось потому, что в Android API 26 появился новый конструктор и рекомендуется использовать его. Пока не обращайте внимание на это. В одном из следующих уроков мы рассмотрим использование правильного конструктора.
Запустив этот код, мы увидим уведомление
Оно отображает иконку и два текста, которые мы указывали в билдере. Нажатие на него ни к чему не приведет, т.к. мы не реализовали обработчик нажатия. Мы это сделаем чуть позже.
Обновление
Мы отобразили уведомление и теперь хотим его обновить. Для этого нужно просто снова показать уведомление методом notify и использовать при этом тот же id.
Это будет выглядеть так:
Код полностью аналогичен коду, что мы использовали при отображении уведомления. Только в билдере используем другие тексты и иконку. Самое главное, что в методе notify мы снова используем NotificationManager по этому id найдет уведомление, которое мы отобразили чуть раньше и заменит его новым
Несколько уведомлений
Чтобы показать новое уведомление, а не обновить уже существующее, надо использовать другой id в методе notify.
Мы использовали разные id в методе notify и получили два разных уведомления
Удаление
Чтобы удалить уведомление, используем NotificationManager и его метод cancel с указанием id уведомления.
Либо методом cancelAll можем удалить все уведомления сразу
При удалении уведомления нет необходимости проверять, отображается оно или нет. Если уведомления по каким-то причинам уже нет, то просто ничего не произойдет.
Обработка нажатия
Мы будем создавать Intent для запуска, например, Activity, упаковывать этот Intent в PendingIntent и передавать PeningIntent в уведомление. По нажатию на уведомление, система достанет из него PedningIntent и использует вложенный в него Intent, чтобы запустить Activity.
Давайте посмотрим, как это выглядит на практике:
Создаем Intent для запуска Activity и упаковываем его в PedningIntent.
Подробно о PedningIntent и его параметрах вы можете почитать в Уроке 119. Там я подробно рассмотрел различные кейсы на примерах с уведомлением и вызовом BroadcastReceiver.
Созданный PendingIntent нам надо будет передать в билдер уведомления. Полный код создания уведомления будет выглядеть так:
Передаем PendingIntent в метод setContentIntent билдера уведомления.
По нажатию на уведомление откроется MainActivity
Обратите внимание, что уведомление не удаляется автоматически после нажатия на него. Чтобы исправить это, можно в билдере уведомления использовать включить параметр autoCancel
Уведомление, созданное с этим флагом будет закрываться после нажатия на него.
Билдер уведомления имеет еще несколько методов, которые могут быть полезны.
В старых версиях это выглядит так
В последних версиях он переехал в верхнюю часть уведомления
Иконка из setSmallIcon будет видна в статусбаре, когда панель уведомлений не раскрыта.
А само уведомление будет выглядеть так:
У метода три параметра:
Сначала отображаем бесконечный прогрессбар и текст Preparing. Т.е. делаем вид, что идет подготовка к выполнению операции.
Затем в отдельном потоке имитируем выполнение операции. Каждые 300 мсек увеличиваем значение progress и обновляем уведомление, чтобы прогрессбар показал текущий прогресс. А также в тексте показываем значение прогресса и максимума.
После выполнения операции скрываем прогрессбар и показываем текст Completed.
Повторюсь, очень рекомендую прочесть и понять Урок 119. В нем я подробно рассматриваю, почему PendingIntent последнего уведомления заменяет PendingIntent предыдущих уведомлений, и как этого можно избежать, используя, например, requestCode.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Уведомления внизу экрана на Android. Откуда они берутся и что с ними делать
Ежедневно мы получаем десятки, если не сотни уведомлений от разных приложений. Они могут требовать реакции с нашей стороны, а могут не требовать, но так или иначе их можно назвать интерактивными. Узнать их очень просто. Помимо кликабельности, они отличаются тем, что появляются в шторке уведомлений. Однако есть и такие уведомления, которые материализуются в самом низу экрана, не позволяют кликнуть на себя и даже не уточняют, какое приложение их отправило. Разбираемся, что это такое.
Уведомления на Android бывают двух типов: обычные и тост
Второй тип уведомлений, упомянутых мной в предыдущем абзаце, называется тост-уведомлениями. Несмотря на то что с английского слово toast переводится как поджаренный ломтик хлеба, на языке программистов этот термин обозначает маленькое информационное окно, присутствующее в некоторых видах программного обеспечения.
Что такое тост-уведомления
Это и есть тост-уведомление
Тост-уведомления – это системные уведомления, носящие исключительно информирующий характер и не требующие каких-либо действий от пользователя. Самый известный пример тост-уведомлений – это уведомление о получении прав разработчика. Как это сделать и, самое главное, зачем, мы рассказывали в отдельной статье – обязательно почитайте.
Поскольку тост-уведомления не позволяют кликнуть на них, зачастую пользователи, во-первых, не успевают прочесть их содержимое, а, во-вторых, не представляют, какое приложение их им направило. В результате получается полная неразбериха, что не очень удобно, учитывая, что зачастую информационная ценность тост-уведомлений оказывается довольно высокой.
Как добавить тост-уведомления в шторку
Впрочем, существует приложение, которое позволяет превратить тост-уведомления в обычные с характерными признаками: кликабельностью, упоминанием принадлежности и отображением в шторке уведомлений.
Чтобы всё заработало, нужно дать приложению разрешение на работу с универсальным доступом
Тост-уведомления на Android
На Android нельзя возвращаться к чтению тост-уведомлений, но с помощью Toast Sources — можно
Несмотря на то что скорее всего вы будете нечасто обращаться к Toast Sources, рекомендую после настройки тут же спрятать его в папку с другими ненужными приложениями. Дело в том, что оно работает в пассивном режиме, автоматически выцепляя тост-уведомления из системы и интерпретируя их в обычные, которые затем размещаются в шторке уведомлений. Поэтому просто рассматривайте его как одну из системных утилит, не претендующих на регулярное взаимодействие.
Не знаю, как часто вы получаете тост-уведомления, однако я фактически начал получать их только после установки Toast Sources. Это выглядело странным, поэтому я прикинул мозгами и понял, что раньше я просто не замечал их, а теперь у меня банально нет шанса пропустить их. Во всяком случае, когда они стали появляться в шторке уведомлений, я начал осознавать, что тост-уведомления не просто существуют, но и зачастую несут в себе очень полезную информацию.
Новости, статьи и анонсы публикаций
Свободное общение и обсуждение материалов
Я уже давно не пользуюсь социальными сетями. Их мне заменили сразу несколько сервисов: Telegram, Instagram и TikTok. Первый я использую для переписок и чтения новостей, второй – для публикации своих и просмотра чужих фоток, а третий – для деградации. Меня вполне устраивала такая расстановка сил, поэтому стремиться к чему-то ещё у меня не было никакого желания. Поэтому я даже Clubhouse не ставил – мне было попросту неинтересно. Но BeReal (скачать) я установил, пользуюсь и прекращать не планирую.
Буквально на днях Apple выпустила обновление Apple Music для Android. Его состав был ожидаемым: поддержка lossless-композиций и технологии Spatial Audio. Первое нововведение представляет собой музыку без потери качества, а второе – пространственное звучание, которое имитирует эффект присутствия. Несмотря на то что обновление пока находится в состоянии беты, уже сейчас пользователи задумались, а какие наушники нужны, чтобы прочувствовать все нововведения? Разберёмся.
Тяжело после айфона. Там все в одной непролазной куче, и иконки и уведомления. А как только появляется два места — всё, уже тяжело. Эх эпол, эпол, что же ты творишь с хомосапиенсами, эволюция вспять.
А по статье тост уведомления всегда были и есть, плохо когда выскакивают поверх клавиатуры.
Как можно не замечать тосты? Это абсурд!
И зачем тост уведомления в шторку помещать? Они нужны только для того чтобы донести информацию, полезную в данный момент. В шторке они будут только ненужным хламом
Полный список
— шлем уведомление из сервиса
В принципе, уведомления – отдельная от сервисов тема. Но чаще всего уведомления используются именно в сервисах, поэтому я решил дать эту тему сейчас.
В андроид есть строка вверху экрана. Называется она статус-бар. Туда обычно в виде иконок сваливаются различные уведомления для пользователя (новые письма, смс и прочие). Пользователь открывает статус бар – видит там чуть более подробную инфу о событии. Дальше он может либо стереть это уведомление, либо нажать на него и перейти непосредственно к событию.
В этом уроке научимся все это проделывать. Для начала разберем уведомление на логические составляющие, чтобы проще было понять как его создавать и отправлять.
Первая часть – то, что видно в статус-баре, когда уведомление только приходит – иконка и текст. Текст потом исчезает и остается только иконка.
Вторая часть – то, что мы видим, когда открываем статус бар (тянем вниз). Там уже полноценный View с иконкой и двумя текстами, т.е. более подробная информация о событии.
Третья часть – то, что произойдет, если мы нажмем на View из второй части. Тут обычно идет вызов Activity, где мы можем просмотреть полную информацию и обработать событие.
Кроме этого есть еще несколько возможностей, по которым совсем кратко пробежимся в конце урока.
Создадим приложение и сервис. Сервис, как будто загружает файл и посылает уведомление, по нажатию на которое будет открываться приложение и отображать имя файла.
Project name: P0991_ServiceNotification
Build Target: Android 2.3.3
Application name: ServiceNotification
Package name: ru.startandroid.develop.p0991servicenotification
Create Activity: MainActivity
Добавим в strings.xml строки:
Кнопки для старт/стопа сервиса и TextView для отображения результата
Создаем сервис MyService.java и прописываем его в манифесте. В манифесте же настроим сервис так, чтобы он работал в отдельном процессе. Для этого надо в его атрибуте process написать двоеточие и какое-нить слово.
Система эту строку добавит к package сервиса и, тем самым, получит название нового процесса, в котором и запустит сервис
В onCreate мы вытаскиваем из intent и кладем в TextView текст. Этот текст мы будем отправлять из сервиса через уведомление.
onClickStart и onClickStop – это обработчики кнопок. Стартуют и останавливают сервис.
В onCreate получаем менеджер уведомлений – NotificationManager. Он нам понадобится, чтобы отправить уведомление.
В onStartCommand запускаем паузу на 5 секунд (эмулируем закачку файла) и после этого отправляем уведомление. Именно из-за этой паузы мы и используем другой процесс, чтобы не тормозило основное приложение.
В sendNotif мы создаем и отправляем уведомление. Правда, немного в иной последовательности, что я описывал выше. Сначала первая часть, потом третья, потом вторая.
Первая часть – создаем Notification. В конструкторе указываем иконку и текст, которые будут видны в статус-баре. Также мы здесь указываем время. Обычно это текущее время. Но можно указать и прошлое и будущее. По этому времени уведомления будут отсортированы в статус-баре и в его раскрытой части.
Третья часть – создаем Intent, который мы бы использовали для вызова нашего Activity. Туда помещаем имя загруженного файла. Activity его достанет и поместит в TextView. Далее мы оборачиваем этот Intent в PendingIntent, с помощью метода getActivity. На вход ему передаем контекст и Intent. Второй параметр не используется (так написано в хелпе). А четвертый – это флаги, влияющие на поведение PendingIntent. Они не относятся к теме урока, мы их не используем.
Теперь этот созданный PendingIntent содержит информацию о том, что надо вызывать Activity, а также объект Intent, который для этой цели надо использовать. Это будет использовано при нажатии на уведомлении.
Вторая часть – вызываем метод setLatestEventInfo. Передаем на вход контекст, текст-заголовок, подробный текст и PendingIntent. Теперь, когда мы откроем статус-бар, мы увидим два этих текста (заголовок и подробный). А, когда нажмем на уведомление, система использует PendingIntent для запуска Activity.
Далее мы для созданного уведомления ставим флаг FLAG_AUTO_CANCEL, чтобы оно исчезло из статус-бара после нажатия. По умолчанию оно не исчезает и продолжает висеть.
Далее вызываем метод notify для менеджера уведомлений и передаем туда ID и созданное уведомление. ID используется, если мы хотим изменить или удалить уведомление.
Все сохраним, запустим.
Жмем Start и сразу закрываем приложение кнопкой Назад.
Проходит 5 сек и появляется уведомление (первая часть)
Открываем статус-бар и видим более подробную инфу (вторая часть)
Жмем на уведомление.
Открывается наше приложение (третья часть) и в TextView появляется текст, переданный из сервиса.
Теперь вкратце пробежимся по остальным интересным возможностям уведомлений.
Обновление старого или новое уведомление
Если вы создадите новое уведомление и отправите его (notify) с тем же ID, что и у уже существующего уведомления, то новое заменит старое. Таким образом, вы можете уведомления обновлять.
Если же надо показать новое уведомление, то используйте другой ID.
Удаление
Чтобы убрать уведомление из статус-бара, используется метод cancel у менеджера уведомлений. На вход подается ID. Либо используйте метод cancelAll, чтобы удалить все уведомления.
Если хотите, чтобы уведомление появилось со стандартным звуком, добавьте флаг Notification.DEFAULT_SOUND в поле уведомления defaults.
А для использования своих звуков используется поле sound.
Чтобы проиграть файл с SD:
Чтобы использовать какую-либо из стандартных мелодий, используем Content Provider:
notif.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, «6»);
Вибра
Если хотите, чтобы уведомление появилось со стандартной виброй, добавьте флаг Notification.DEFAULT_VIBRATE в поле уведомления defaults.
А для использования своей комбинации вибры используется поле vibrate. В это поле помещается массив long-чисел. Первое – длительность паузы (в миллисекундах) перед началом вибрирования, второе – длительность вибрирования, третье – длительность паузы, четвертое – длительность вибрирования … и т.д. Т.е. создаете свою комбинацию пауз и вибрирования. И мобила при получении уведомления вам ее провибрирует.
Для работы вибры необходимо прописать права VIBRATE в манифесте.
Индикатор
Если хотите, чтобы уведомление появилось с миганием индикатора, добавьте флаг Notification.DEFAULT_LIGHTS в поле уведомления defaults.
А для использования своей комбинации мигания индикатора используются поля
ledARGB – здесь задается цвет
В итоге индикатор будет мигать с заданными значениями и с заданным цветом. В хелпе написано, что не все девайсы поддерживают разные цвета. Поэтому выбранный вами цвет не гарантируется.
Число
У Notification есть поле number. Вы можете поместить туда число больше нуля и оно отобразится на уведомлении.
Например, при notif.number = 3 уведомление будет выглядеть так:
Флаги
Добавляются в поле flags
FLAG_INSISTENT – звук уведомления будет повторяться, пока не откроют статус-бар
FLAG_ONGOING_EVENT – уведомление появляется не в обычной секции, а в ongoing (постоянные). Уведомления из этой секции не удаляются при нажатии кнопки очистки уведомлений.
FLAG_NO_CLEAR – уведомление не удалится при очистке всех уведомлений
Не очень понимаю, в чем разница между ongoing и тем, что уведомление не удалится после нажатия на кнопку очистки всех уведомлений. Но флаги такие есть, и я о них упомянул.
На следующем уроке:
— изучаем IntentService
— включаем режим Foreground для сервиса
— помещаем сервис в автозагрузку
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме