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

Справочная информация: мне нужно обнаруживать, когда пользователь нажимает кнопку воспроизведения / паузы, найденную на большинстве гарнитур (KEYCODE_MEDIA_PLAY_PAUSE).

У меня все это в основном работает с использованием MediaSessions, но когда другое приложение начинает воспроизводить звук, я прекращаю получать обратные вызовы.

Похоже, что это потому, что приложение, которое воспроизводит аудио, создало свою собственную MediaSession, а Android отправляет KeyEvents только в новейшую MediaSession. Чтобы предотвратить это, я создаю OnActiveSessionsChangedListener и создаю новую MediaSession при каждом запуске.

Это работает, но каждый раз, когда я создаю новую MediaSession, слушатель снова запускается, поэтому я застреваю в цикле inf.

Мой вопрос: кто-нибудь знает, как я могу сделать любое из следующих действий?

  • Предотвратить другие приложения от кражи моего медиа-кнопки
  • Определите, когда я потерял фокус мультимедийной кнопки для другого приложения, поэтому я могу создать новую MediaSession только тогда, а не всякий раз, когда активные сеансы меняются
  • Убедитесь, что у меня уже есть фокус мультимедийной кнопки, поэтому я без необходимости создаю новую MediaSession

Что не сработало:

  • BroadcastReceiver для AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION не работает, потому что приложения должны вручную запускать эту трансляцию, а многие приложения, такие как NPR One, не работают
  • AudioManager.OnAudioFocusChangeListener не работает, потому что он требует, чтобы у меня был аудио фокус
  • BroadcastReceiver с максимальным приоритетом для android.intent.action.MEDIA_BUTTON и вызова abortBroadcast(), но когда другие приложения воспроизводили звук, мой приемник не срабатывал. Также другие приложения могут установить максимальный приоритет.

Мой код:

mMediaSessionManager.addOnActiveSessionsChangedListener(controllers -> {
    boolean updateButtonReceiver = false;

    // recreate MediaSession if another app handles media buttons
    for (MediaController mediaController : controllers) {
        if (!TextUtils.equals(getPackageName(), mediaController.getPackageName())) {
            if ((mediaController.getFlags() & (MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)) != 0L) {
                updateButtonReceiver = true;
            }
        }

    }

    if (updateButtonReceiver) {
        // using a handler with a delay of about 2 seconds because this listener fires very often.
        mAudioFocusHandler.removeCallbacksAndMessages(null);
        mAudioFocusHandler.sendEmptyMessageDelayed(0, AUDIO_FOCUS_DELAY_MS);
    }
}, ClickAppNotificationListener.getComponentName(this));

Вот обработчик, который запускается:

private final Handler mAudioFocusHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (mShouldBeEnabled) {
            updateButtonReceiverEnabled(true);
        }
    }
};

И, наконец, вот метод, который запускает обработчик:

private void updateButtonReceiverEnabled(boolean shouldBeEnabled) {
    // clear old session (not sure if this is necessary)
    if (mMediaSession != null) {
        mMediaSession.setActive(false);
        mMediaSession.setFlags(0);
        mMediaSession.setCallback(null);
        mMediaSession.release();
        mMediaSession = null;
    }

    mMediaSession = new MediaSessionCompat(this, MEDIA_SESSION_TAG);
    mMediaSession.setCallback(mMediaButtonCallback);
    mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    mMediaSession.setPlaybackToLocal(AudioManager.STREAM_MUSIC);
    mMediaSession.setActive(true);
    mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
            .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
            .setState(PlaybackStateCompat.STATE_CONNECTING, 0, 0f)
            .build());

    if (shouldBeEnabled != mShouldBeEnabled) {            
        getPackageManager().setComponentEnabledSetting(mMediaButtonComponent,
                shouldBeEnabled
                        ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }

    mShouldBeEnabled = shouldBeEnabled;
}

Спасибо!

2 ответа

Если вы просто хотите захватить MediaButton Вы можете зарегистрировать BroadcastReceiver чтобы получить действие Media Button все время.

Класс MediaButtonIntentReceiver:

public class MediaButtonIntentReceiver extends BroadcastReceiver {

  public MediaButtonIntentReceiver() {
    super();
    }

 @Override
 public void onReceive(Context context, Intent intent) {
     String intentAction = intent.getAction();
     if (!Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
        return;
       }
     KeyEvent event =   (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
     if (event == null) {
        return;
       }
     int action = event.getAction();
     if (action == KeyEvent.ACTION_DOWN) {
          // do something
       Toast.makeText(context, "BUTTON PRESSED!", Toast.LENGTH_SHORT).show(); 
        }
    abortBroadcast();
  }
}

добавьте это в manifest.xml:

<receiver android:name=".MediaButtonIntentReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

и зарегистрируйте свой BroadcastReceiver как это (в основной деятельности)

IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
MediaButtonIntentReceiver r = new MediaButtonIntentReceiver();
filter.setPriority(1000); 
registerReceiver(r, filter); 

также посмотрите на:

Как захватить ключевые события от Bluetooth-гарнитуры с Android

Как перехватить нажатие кнопок на гарнитуре в Android?

controllers ты попал в OnActiveSessionsChangedListener упорядочено по приоритету. Вам нужно только создать новый MediaSession если вы видите, что ваш MediaSessionне первый в списке.

Обратите внимание, что вы все равно можете столкнуться с бесконечным циклом, если есть другое приложение, которое обрабатывает события медиа-ключа, используя тот же подход.

Другие вопросы по тегам