Реагирование на медиа-кнопки

Мультимедийные кнопки — это аппаратные кнопки, имеющиеся на устройствах Android и других периферийных устройствах, например кнопка паузы/воспроизведения на гарнитуре Bluetooth. Когда пользователь нажимает кнопку мультимедиа, Android генерирует KeyEvent , который содержит код ключа , идентифицирующий кнопку. Коды клавиш для кнопки мультимедиа KeyEvents — это константы, начинающиеся с 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() чтобы самостоятельно обрабатывать событие. (Подробнее см. в разделе «Обработка медиа-кнопок в активном медиа-сеансе» .) В следующем фрагменте кода показано, как перехватить код ключа и вызвать sendMediaButtonEvent(). Обязательно верните true , чтобы указать, что событие было обработано:

    Котлин

        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)
        }
        

    Ява

        @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 отправляет событие непосредственно ему. В противном случае, если сеанс не активен и у него есть приемник медиа-кнопки, Android отправляет событие получателю, который перезапускает сеанс и может получить событие. (Подробнее см. в разделе «Использование мультимедийных кнопок для перезапуска неактивного мультимедийного сеанса» .) Если в сеансе нет приемника мультимедийных кнопок, система отбрасывает событие мультимедийной кнопки, и ничего не происходит. Логика показана на следующей схеме:

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

Обработка медиа-кнопок в активном медиа-сеансе

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

До Android 5.0 (уровень API 21) Android обрабатывал события мультимедийных кнопок, передавая намерение с помощью действия ACTION_MEDIA_BUTTON . Ваше приложение должно зарегистрировать BroadcastReceiver, чтобы перехватывать эти намерения. Класс MediaButtonReceiver был разработан специально для этой цели. Это удобный класс в библиотеке совместимости мультимедиа Android , который обрабатывает ACTION_MEDIA_BUTTON и преобразует входящие намерения в соответствующие вызовы метода 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() вашего сервиса. Это преобразует код ключа в соответствующий метод обратного вызова сеанса.

Котлин

private val mediaSessionCompat: MediaSessionCompat = ...

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

Ява

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 версий 24.0.0–25.1.1, вам необходимо вызвать setMediaButtonReceiver , чтобы позволить кнопке мультимедиа перезапустить неактивный сеанс мультимедиа.

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

Котлин

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

Ява

// 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 , добавив его в манифест приложения.