Android studio media player notification

Android Music Player Controls on Lock Screen and Notifications

If you are just starting out, I recommend reading the first (basics about Media playback on Android) and second (Creating a Service to Play Music in the background) parts before proceeding.

This post will discuss how to show Media Player controls on the lock screen and the notifications.

Managing a Media Session

The first thing for this to work is to register a Media Session in our service. A MediaSession should be created to publish media playback information or handle media keys. We create a new MediaSession in the onCreate() of our Service instance.

MediaSession.Callback set with setCallback(Callback) enables us to receive commands, media keys, and other events. Note that we are not calling setActive on the media session yet. We do that only when we receive a play intent for the service and actually start playing some music.

We also need to associate the UI that should be launched when the user wants to interact with this session. Let’s say we have an activity called MusicPlayerActivity that manages a music player interface. This is how we can set it to launch when the user wants to interact with the session.

In order to populate the lock screen with the current track information, Media Session uses MediaMetadata . Remember that we created some meta data in the previous post, but there we were using it as just a way to pass on our media ids to the playback implementation. If you have done that, and created a media session, you are already good to go. It will start picking up information from the meta data to show on the lock screen.

If you have the album artwork ready, you can also set it immediately on the meta data using .putBitmap for keys MediaMetadata.METADATA_KEY_ALBUM_ART and MediaMetadata.METADATA_KEY_DISPLAY_ICON .

Loading the album art asynchronously

But, what if you have a url to the album art? Although, MediaMetadata doesn’t directly allow putting url’s for the album arts, it’s pretty easy to load the images asynchronously. Use your favorite image loading library (like Glide, Picasso, Fresco, …) to load the image in the background and then update the meta data when you have the bitmap ready like so.

Showing playback controls in notification

Once we have the lock screen controls sorted out, let’s take a look at how we can add these controls to the notification. We always need a notification to make our service act as a foreground service so that it isn’t killed by the system when it needs resources. This is how we can add a notification and set the Service to foreground:

mController is an instance of MediaController . It is a view containing controls for a MediaPlayer that typically contains the buttons like “Play/Pause”, “Rewind”, “Fast Forward” and a progress slider. It takes care of synchronizing the controls with the state of the MediaPlayer. We use it to simplify the notification creation process to let the system create a “standard” media controller and register a callback ( mCb ) that listens to the media controller events.

Here’s how we create the notification:

Читайте также:  Аналог майкрософт офис для андроид

Again, we have some code to fetch the bitmap for the notification on the background thread. I leave it to you to use your favorite image loading library to handle that.

There is some special handling required for the play/pause buttons since we want to show only one of them based on the current state of the media player.

And, to update the notification with the current state of the media player

We need to update the notification whenever we receive some events from the media controller. Here’s how we can create the callback to handle these events:

Handling user interaction with the controls

So far, we have the controls on the lock screen and the notification. But, they don’t work magically. We will need to listen to user actions on these controls and perform corresponding actions in our Service. Remember that we created the MediaSession and set a callback in the first section. This is where that comes into play. The user events are sent to the media session callback.

Here’s a sample implementation of the MediaSession.Callback that can be used in your Service. You might need to modify it to fit your needs. If you are following this series of tutorials, it should pretty much fit right through in your application.

I have had far too many requests for the source code and the reason I am not putting it up is that as all source code, it requires constant maintenance and I don’t have enough time to manage this. Secondly, I have seen far too many people picking up the source code and putting it as is into the app which floods the already sub-par app market with low-quality apps aimed only for ad revenue. There is already a good, well-maintained music player source code maintained by Google if someone really needs it. Otherwise, if you need professional help, please feel free to shoot me an email.

This is the third tutorial in the series Building a Music Player App for Android. Stay tuned for more tutorials in this series! Take me to next tutorial

Источник

Android и звук: как делать правильно

В статье рассматривается архитектура и API для создания приложений, воспроизводящих музыку. Мы напишем простое приложение, которое будет проигрывать небольшой заранее заданный плейлист, но «по-взрослому» — с использованием официально рекомендуемых практик. Мы применим MediaSession и MediaController для организации единой точки доступа к медиаплееру, и MediaBrowserService для поддержки Android Auto. А также оговорим ряд шагов, которые обязательны, если мы не хотим вызвать ненависти пользователя.

В первом приближении задача выглядит просто: в activity создаем MediaPlayer, при нажатии кнопки Play начинаем воспроизведение, а Stop — останавливаем. Все прекрасно работает ровно до тех пор, пока пользователь не выйдет из activity. Очевидным решением будет перенос MediaPlayer в сервис. Однако теперь у нас встают вопросы организации доступа к плееру из UI. Нам придется реализовать binded-сервис, придумать для него API, который позволил бы управлять плеером и получать от него события. Но это только половина дела: никто, кроме нас, не знает API сервиса, соответственно, наша activity будет единственным средством управления. Пользователю придется зайти в приложение и нажать Pause, если он хочет позвонить. В идеале нам нужен унифицированный способ сообщить Android, что наше приложение является плеером, им можно управлять и что в настоящий момент мы играем такой-то трек из такого-то альбома. Чтобы система со своей стороны подсобила нам с UI. В Lollipop (API 21) был представлен такой механизм в виде классов MediaSession и MediaController. Немногим позже в support library появились их близнецы MediaSessionCompat и MediaControllerCompat.

Читайте также:  Андроид 10 go edition недостатки

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

MediaSession

Итак, мы создаем экземпляр MediaSession в сервисе, заполняем его сведениями о нашем плеере, его состоянии и отдаем MediaSession.Callback, в котором определены методы onPlay, onPause, onStop, onSkipToNext и прочие. В эти методы мы помещаем код управления MediaPlayer (в примере воспользуемся ExoPlayer). Наша цель, чтобы события и от аппаратных кнопок, и из окна блокировки, и с часов под Android Wear вызывали эти методы.

Полностью рабочий код доступен на GitHub (ветка master). В статьи приводятся только переработанные выдержки из него.

Для доступа извне к MediaSession требуется токен. Для этого научим сервис его отдавать

и пропишем в манифест

MediaController

Теперь реализуем activity с кнопками управления. Создаем экземпляр MediaController и передаем в конструктор полученный из сервиса токен.

MediaController предоставляет как методы управления плеером play, pause, stop, так и коллбэки onPlaybackStateChanged(PlaybackState state) и onMetadataChanged(MediaMetadata metadata). К одному MediaSession могут подключиться несколько MediaController, таким образом можно легко обеспечить консистентность состояний кнопок во всех окнах.

Наша activity работает, но ведь идея исходно была, чтобы из окна блокировки тоже можно было управлять. И тут мы приходим к важному моменту: в API 21 полностью переделали окно блокировки, теперь там отображаются уведомления и кнопки управления плеером надо делать через уведомления. К этому мы вернемся позже, давайте пока рассмотрим старое окно блокировки.

Как только мы вызываем mediaSession.setActive(true), система магическим образом присоединяется без всяких токенов к MediaSession и показывает кнопки управления на фоне картинки из метаданных.

Однако в силу исторических причин события о нажатии кнопок приходят не напрямую в MediaSession, а в виде бродкастов. Соответственно, нам надо еще подписаться на эти бродкасты и перебросить их в MediaSession.

MediaButtonReceiver

Для этого разработчики Android любезно предлагают нам воспользоваться готовым ресивером MediaButtonReceiver.

Добавим его в манифест

MediaButtonReceiver при получении события ищет в приложении сервис, который также принимает «android.intent.action.MEDIA_BUTTON» и перенаправляет его туда. Поэтому добавим аналогичный интент-фильтр в сервис

Если подходящий сервис не найден или их несколько, будет выброшен IllegalStateException.

Теперь в сервис добавим

Метод handleIntent анализирует коды кнопок из intent и вызывает соответствующие коллбэки в mediaSession. Получилось немного плясок с бубном, но зато почти без написания кода.

На системах с API >= 21 система не использует бродкасты для отправки событий нажатия на кнопки и вместо этого напрямую обращается в MediaSession. Однако, если наш MediaSession неактивен (setActive(false)), его пробудят бродкастом. И для того, чтобы этот механизм работал, надо сообщить MediaSession, в какой ресивер отправлять бродкасты.
Добавим в onCreate сервиса

На системах с API Так это выглядит

Android 4.4

MIUI 8 (базируется на Android 6, то есть теоретически окно блокировки не должно отображать наш трек, но здесь уже сказывается кастомизация MIUI).

Уведомления

Однако, как ранее упоминалось, начиная с API 21 окно блокировки научилось отображать уведомления. И по этому радостному поводу, вышеописанный механизм был выпилен. Так что теперь давайте еще формировать уведомления. Это не только требование современных систем, но и просто удобно, поскольку пользователю не придется выключать и включать экран, чтобы просто нажать паузу. Заодно применим это уведомление для перевода сервиса в foreground-режим.

Читайте также:  Гугл для андроид заметки

Нам не придется рисовать кастомное уведомление, поскольку Android предоставляет специальный стиль для плееров — Notification.MediaStyle.

Добавим в сервис два метода

И добавим вызов refreshNotificationAndForegroundStatus(int playbackState) во все коллбэки MediaSession.

Android 4.4

Android 7.1.1

Android Wear

Started service

В принципе у нас уже все работает, но есть засада: наша activity запускает сервис через binding. Соответственно, после того, как activity отцепится от сервиса, он будет уничтожен и музыка остановится. Поэтому нам надо в onPlay добавить

Никакой обработки в onStartCommand не надо, наша цель не дать системе убить сервис после onUnbind.

А в onStop добавить

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

ACTION_AUDIO_BECOMING_NOISY

Продолжаем полировать сервис. Допустим пользователь слушает музыку в наушниках и выдергивает их. Если эту ситуацию специально не обработать, звук переключится на динамик телефона и его услышат все окружающие. Было бы хорошо в этом случае встать на паузу.
Для этого в Android есть специальный бродкаст AudioManager.ACTION_AUDIO_BECOMING_NOISY.
Добавим в onPlay

В onPause и onStop

И по факту события встаем на паузу

Android Auto

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

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

Исходный код выложен на GitHub (ветка MediaBrowserService).

Прежде всего надо указать в манифесте, что наше приложение совместимо с Android Auto.
Добавим в манифест

Здесь automotive_app_desc — это ссылка на файл automotive_app_desc.xml, который надо создать в папке xml

Преобразуем наш сервис в MediaBrowserService. Его задача, помимо всего ранее сделанного, отдавать токен в Android Auto и предоставлять плейлисты.

Поправим декларацию сервиса в манифесте

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

И, во-вторых, добавлен интент-фильтр android.media.browse.MediaBrowserService.

Меняем родительский класс на MediaBrowserServiceCompat.

Поскольку теперь сервис должен отдавать разные IBinder в зависимости от интента, поправим onBind

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

И, наконец, имплементируем новый коллбэк MediaSession

Здесь mediaId — это тот, который мы отдали в setMediaId в onLoadChildren.

Плейлист

Трек

UPDATE от 27.10.2017: Пример на GitHub переведен на targetSdkVersion=26. Из релевантных теме статьи изменений необходимо отметить следующее:

  • android.support.v7.app.NotificationCompat.MediaStyle теперь deprecated. Вместо него следует использовать android.support.v4.media.app.NotificationCompat.MediaStyle. Соответственно, больше нет необходимости использовать android.support.v7.app.NotificationCompat, теперь можно использовать android.support.v4.app.NotificationCompat
  • Метод AudioManager.requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) теперь тоже deprecated. Вместо него надо использовать AudioManager.requestAudioFocus(AudioFocusRequest focusRequest). AudioFocusRequest — новый класс, добавленный с API 26, поэтому не забывайте проверять на API level.
    Создание AudioFocusRequest выглядит следующим образом

Теперь запрашиваем фокус

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

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

Еще очень рекомендую к просмотру доклад Ian Lake. Доклад от 2015 года, но вполне актуален.

Источник

Оцените статью