始终接收所有Android媒体按钮事件的回调(即使其他应用正在播放音频)

时间:2016-07-10 05:59:43

标签: android android-mediasession

背景信息:我需要检测用户何时按下大多数耳机上的播放/暂停按钮(KEYCODE_MEDIA_PLAY_PAUSE)。

我完全使用MediaSessions工作,但当另一个应用程序开始播放音频时,我会停止获取回调。

这似乎是因为播放音频的应用创建了自己的MediaSession,而Android仅将KeyEvents发送到最新的MediaSession。为了防止这种情况,我创建一个OnActiveSessionsChangedListener并在每次触发时创建一个新的MediaSession。

这确实有效,但每次我创建一个新的MediaSession时,监听器再次触发,所以我发现自己陷入了一个inf循环。

我的问题:有谁知道我怎么能做以下任何事情?:

  • 防止其他应用窃取我的媒体按钮
  • 当我将媒体按钮焦点丢失到另一个应用程序时检测到,所以我只能创建一个新的MediaSession,而不是每当有效时 会议改变
  • 检查我当前是否已经拥有媒体按钮焦点所以我不必要地创建一个新的MediaSession

什么没有用?

  • BroadcastReceiver on AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION没有工作 因为应用必须手动触发广播和许多应用, 像NPR一样不
  • AudioManager.OnAudioFocusChangeListener没有用,因为它要求我有 音频焦点
  • 在android.intent.action.MEDIA_BUTTON&上具有最高优先级的BroadcastReceiver调用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);
        }
    }
};

最后,这是Handler触发的方法:

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 个答案:

答案 0 :(得分:2)

如果您只想捕获 vendorNpmFiles: [ 'systemjs/dist/system-polyfills.js', 'systemjs/dist/system.src.js', 'zone.js/dist/**/*.+(js|js.map)', 'es6-shim/es6-shim.js', 'reflect-metadata/**/*.+(ts|js|js.map)', 'rxjs/**/*.+(js|js.map)', '@angular/**/*.+(js|js.map)', 'jquery/**/*.js' ], polyfills:[ 'vendor/jquery/dist/jquery.min.js', 'vendor/es6-shim/es6-shim.js', 'vendor/reflect-metadata/Reflect.js', 'vendor/systemjs/dist/system.src.js', 'vendor/zone.js/dist/zone.js', ] ,可以注册MediaButton以始终获取媒体按钮操作。

MediaButtonIntentReceiver类:

BroadcastReceiver

将其添加到manifest.xml:

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

并像这样注册<receiver android:name=".MediaButtonIntentReceiver"> <intent-filter> <action android:name="android.intent.action.MEDIA_BUTTON" /> </intent-filter> </receiver> (在主要活动中)

BroadcastReceiver

另见:

How to capture key events from bluetooth headset with android

How do I intercept button presses on the headset in Android?

答案 1 :(得分:1)

controllers OnActiveSessionsChangedListener按优先顺序排列MediaSession。如果您发现MediaSession不是列表中的第一个,则只需创建新的Array.prototype.every = function(cb){ return this.reduce((p,c,i,a) => i === 1 ? cb(p,i-1,a) && cb(c,i,a) : p && cb(c,i,a)); }; var arr = [9,2,3,9,12,5], brr = [1,2,3,4,5,6,9]; console.log(arr.every(e => e < 10)); console.log(brr.every(e => e < 10));

请注意,如果有另一个应用使用相同的方法竞争媒体键事件,您可能仍会遇到无限循环。