Получать обратный вызов на все события мультимедийных кнопок 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
controllers
ты попал в OnActiveSessionsChangedListener
упорядочено по приоритету. Вам нужно только создать новый MediaSession
если вы видите, что ваш MediaSession
не первый в списке.
Обратите внимание, что вы все равно можете столкнуться с бесконечным циклом, если есть другое приложение, которое обрабатывает события медиа-ключа, используя тот же подход.