Реагирование на нажатие кнопок мультимедиа

Кнопки управления медиаконтентом – это аппаратные кнопки на устройствах Android и других периферийных устройствах, например кнопка паузы/вос��роизведения на Bluetooth-гарнитуре. Когда пользователь нажимает кнопку управления медиа, Android генерирует объект KeyEvent, который содержит код клавиши, идентифицирующий кнопку. Коды клавиш для событий KeyEvent, связанных с кнопками управления медиаконтентом, – это константы, которые начинаются с KEYCODE_MEDIA (например, KEYCODE_MEDIA_PLAY).

Приложения должны обрабатывать события мультимедийных кнопок в трех случаях (в порядке приоритета):

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

Обработка кнопок мультимедиа в активном режиме

Активное действие получает событие нажатия кнопки мультимедиа в методе onKeyDown(). В зависимости от версии Android система может направлять событие контроллеру мультимедиа двумя способами:

  • Если вы используете Android 5.0 (уровень API 21) или более поздней версии, вызовите FLAG_HANDLES_MEDIA_BUTTONS MediaBrowserCompat.ConnectionCallback.onConnected. Это автоматически вызовет метод dispatchMediaButtonEvent() контроллера мультимедиа, который преобразует код клавиши в обратный вызов сеанса мультимедиа.
  • В версиях до Android 5.0 (уровень API 21) вам нужно изменить onKeyDown(), чтобы обрабатывать событие самостоятельно. Подробнее о том, как обрабатывать нажатия кнопок в активном сеансе воспроизведения… В приведенном ниже фрагменте кода показано, как перехватить код клавиши и вызвать dispatchMediaButtonEvent(). Обязательно верните true, чтобы указать, что событие было обработано:

    Kotlin

        fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                return super.onKeyDown(keyCode, event)
            }
            when (keyCode) {
                KeyEvent.KEYCODE_MEDIA_PLAY -> {
                    yourMediaController.dispatchMediaButtonEvent(event)
                    return true
                }
            }
            return super.onKeyDown(keyCode, event)
        }
        

    Java

        @Override
        boolean onKeyDown(int keyCode, KeyEvent event) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                  return super.onKeyDown(keyCode, event);
                }
                switch (keyCode) {
                  case KeyEvent.KEYCODE_MEDIA_PLAY:
                          yourMediaController.dispatchMediaButtonEvent(event);
                          return true;
                }
                return super.onKeyDown(keyCode, event);
        }
        

Поиск сеанса трансляции контента

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

  • Если вы используете Android 8.0 (уровень API 26) или более поздней версии, система пытается найти последнее приложение с MediaSession, которое воспроизводило аудио локально. Если сеанс все ещё активен, Android отправляет событие напрямую в него. В противном случае, если сеанс неактивен и у него есть получатель mediabutton, Android отправляет событие получателю, который перезапустит сеанс и сможет получить событие. Подробнее о том, как перезапустить неактивный сеанс воспроизведения с помощью кнопок управления… Если у сеанса нет получателя кнопки мультимедиа, система отбрасывает событие кнопки мультимедиа, и ничего не происходит. Логика показана на следующей диаграмме:

  • В версиях Android до 8.0 (уровень API 26) система пытается отправить событие активному сеансу воспроизведения. Если активных сеансов несколько, Android пытается выбрать тот, который готовится к воспроизведению (буферизация/подключение), воспроизводится или приостановлен, а не остановлен. Подробнее о том, как обрабатывать нажатия кнопок мультимедиа в активном сеансе… Если активного сеанса нет, Android пытается отправить событие в последний активный сеанс. Подробнее о том, как перезапустить неактивный сеанс воспроизведения с помощью кнопок управления… Логика показана на следующей диаграмме:

Обработка кнопок мультимедиа в активном сеансе

В Android 5.0 (уровень API 21) и более поздних версиях Android автоматически отправляет события кнопок мультимедиа в активный сеанс мультимедиа, вызывая onMediaButtonEvent(). По умолчанию этот обратный вызов преобразует KeyEvent в соответствующий метод обратного вызова сеанса мультимедиа, который соответствует коду клавиши.

В версиях Android до 5.0 (уровень API 21) события кнопок мультимедиа обрабатываются путем трансляции намерения с действием ACTION_MEDIA_BUTTON. Чтобы перехватывать эти интенты, в приложении должен быть зарегистрирован BroadcastReceiver. Для этого предназначен класс MediaButtonReceiver. Это вспомогательный класс в библиотеке media-compat для Android, который обрабатывает ACTION_MEDIA_BUTTON и преобразует входящие объекты Intent в соответствующие вызовы методов MediaSessionCompat.Callback.

MediaButtonReceiver – это кратковременный BroadcastReceiver. Он перенаправляет входящие намерения в сервис, который управляет вашим мультимедийным сеансом. Если вы хотите использовать кнопки управления мультимедиа в системах более ранних версий, чем Android 5.0, добавьте в манифест элемент MediaButtonReceiver с фильтром интентов MEDIA_BUTTON.

<receiver android:name="android.support.v4.media.session.MediaButtonReceiver" >
   <intent-filter>
     <action android:name="android.intent.action.MEDIA_BUTTON" />
   </intent-filter>
 </receiver>

BroadcastReceiver перенаправляет намерение в ваш сервис. Чтобы проанализировать намерение и сгенерировать обратный вызов для сеанса воспроизведения, включите метод MediaButtonReceiver.handleIntent() в onStartCommand() сервиса. Это преобразует код ключа в соответствующий метод обратного вызова сеанса.

Kotlin

private val mediaSessionCompat: MediaSessionCompat = ...

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    MediaButtonReceiver.handleIntent(mediaSessionCompat, intent)
    return super.onStartCommand(intent, flags, startId)
}

Java

private MediaSessionCompat mediaSessionCompat = ...;

 public int onStartCommand(Intent intent, int flags, int startId) {
   MediaButtonReceiver.handleIntent(mediaSessionCompat, intent);
   return super.onStartCommand(intent, flags, startId);
 }

Как перезапустить неактивный сеанс воспроизведения с помощью кнопок управления

Если Android может определить последний активный сеанс воспроизведения, он пытается перезапустить его, отправляя намерение ACTION_MEDIA_BUTTON компоненту, зарегистрированному в манифесте (например, сервису или BroadcastReceiver).

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

Это поведение автоматически включается при использовании MediaSessionCompat. Если вы используете MediaSession из фреймворка Android или Support Library версии 24.0.0–25.1.1, вам нужно вызвать setMediaButtonReceiver, чтобы кнопка мультимедиа могла перезапустить неактивный сеанс.

Вы можете отключить это поведение в Android 5.0 (уровень API 21) и более поздних версиях, установив нулевой приемник кнопки мультимедиа:

Kotlin

// Create a MediaSessionCompat
mediaSession = MediaSessionCompat(context, LOG_TAG)
mediaSession.setMediaButtonReceiver(null)

Java

// Create a MediaSessionCompat
mediaSession = new MediaSessionCompat(context, LOG_TAG);
mediaSession.setMediaButtonReceiver(null);

Настройка обработчиков кнопок мультимедиа

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

Чтобы обеспечить единообразную работу кнопок мультимедиа во всех приложениях, вы должны использовать режим работы по умолчанию и отклоняться только для определенной цели. Если для кнопки мультимедиа требуется специальная обработка, переопределите метод onMediaButtonEvent() обратного вызова, извлеките KeyEvent с помощью intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT), обработайте событие самостоятельно и верните true.

Сводка

Чтобы правильно обрабатывать события кнопок мультимедиа во всех версиях Android, необходимо указать FLAG_HANDLES_MEDIA_BUTTONS при создании сеанса мультимедиа.

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

На устройствах с Android 5.0 или более поздней версии:

  • Вызовите MediaControllerCompat.setMediaController() из обратного вызова контроллера медиа onConnected()
  • Чтобы разрешить кнопке мультимедиа перезапускать неактивный сеанс, динамически создайте MediaButtonReceiver, вызвав setMediaButtonReceiver() и передав ему PendingIntent.

На устройствах с Android более ранних версий, чем 5.0:

  • Переопределите метод onKeyDown(), чтобы обрабатывать нажатия кнопок управления воспроизведением.
  • Создайте статический объект MediaButtonReceiver, добавив его в манифест приложения.