简体   繁体   中英

Android: Sending Metadata from media player app to car stereo via bluetooth blocks broadcast receiver to play next song or pause music

I am working on a little media player app. When I go into my car, I want the app to play music and show the current song's metadata on my car stereo's screen. But I also want the media player to play the NEXT song, when I press NEXT on my car stereo.

I got both function to work already, but not together. Sounds a bit wierd but let me explain:

First, I set up a broadcast receiver in my app to catch the next, prev, paus, and stop clicks from a remote bluetooth device, such as a car stereo ( or a headset, etc). This looks like this:

 [BroadcastReceiver]
    [IntentFilter(new[] { Intent.ActionMediaButton })]
    public class MyMediaButtonBroadcastReceiver : BroadcastReceiver
    {
        public string ComponentName { get { return Class.Name; } }

        static long lastClick = 0;

        public override void OnReceive(Context context, Intent intent)
        {
            if (intent.Action != Intent.ActionMediaButton)
                return;

            var keyEvent = (KeyEvent)intent.GetParcelableExtra(Intent.ExtraKeyEvent);

            switch (keyEvent.KeyCode)
            {
                case Keycode.Headsethook:

                    if(canClick())
                        Activity_Player.Instance.PlayOrPauseLogic();
                    break;
                case Keycode.MediaPlay:
                    if (canClick())
                        Activity_Player.Instance.PlayOrPauseLogic();
                    break;
                case Keycode.MediaPlayPause:
                    if (canClick())
                        Activity_Player.Instance.PlayOrPauseLogic();
                    break;
                case Keycode.MediaNext:
                    if(Activity_Player.CurrentSongObject != null && canClick())
                        Activity_Player.Instance.ChooseRandomNewSongAndPlay(false);
                    break;
                case Keycode.MediaPrevious:
                    if (Activity_Player.CurrentSongObject != null && canClick())
                        Activity_Player.mediaPlayer.SeekTo(0);
                    break;
            }

            if (intent.GetStringExtra(BluetoothAdapter.ExtraState) == BluetoothAdapter.ActionConnectionStateChanged)
            {
                Toast.MakeText(Activity_Player.ctx, "connection off1", ToastLength.Short).Show();
                if (canClick())
                    Activity_Player.Instance.PlayOrPauseLogic();
            }



        }

        private bool canClick()
        {
            if(lastClick < Java.Lang.JavaSystem.CurrentTimeMillis() - 500) // needs to be atleast one second bigger 
            {
                lastClick = Java.Lang.JavaSystem.CurrentTimeMillis();
                return true;
            }
            else
                return false;
        }

    }

I Init this broadcast receiver in my app like so:

private void RegisterBroadCastReceiver()
{
    var am = (AudioManager)this.GetSystemService(AudioService);
    var componentName = new ComponentName(PackageName, new MyMediaButtonBroadcastReceiver().ComponentName);
    am.RegisterMediaButtonEventReceiver(componentName);            
}

Again: this totally worked perfectly.

But then I noticed that my car stereo does not display the current song information (meta data from the mp3) on its screen. So with a bit of googeling I coded this:

private void InitBluetoohSending()
{
    if (mAudioManager == null)
    {
        mAudioManager = (AudioManager)GetSystemService(Context.AudioService);
    }

    if (Build.VERSION.SdkInt < Build.VERSION_CODES.Lollipop)
    {
        if (mRemoteControlClient == null)
        {

            mRemoteControlClient = new RemoteControlClient(PendingIntent.GetBroadcast(this, 0, new Intent(Intent.ActionMediaButton), 0));
            mAudioManager.RegisterRemoteControlClient(mRemoteControlClient);
        }
    }
    else
    {
        if (mMediaSession == null)
        {
            mMediaSession = new MediaSession(this, "PlayerServiceMediaSession");
            mMediaSession.SetFlags(MediaSession.FlagHandlesTransportControls);
            mMediaSession.Active = true;

        }
    }
}

AND:

public static void SendInfoToBluetoothDevice()
{

    if (Build.VERSION.SdkInt < Build.VERSION_CODES.Lollipop)
    {

        RemoteControlClient.MetadataEditor ed = mRemoteControlClient.EditMetadata(true);
        ed.PutString(MediaMetadataRetriever.MetadataKeyTitle, CurrentSongObject.SongName);
        ed.PutString(MediaMetadataRetriever.MetadataKeyArtist, CurrentSongObject.ArtistName);
        ed.PutString(MediaMetadataRetriever.MetadataKeyAlbum, CurrentSongObject.AlbumName);
        ed.PutLong(MediaMetadataRetriever.MetadataKeyDuration, CurrentSongObject.DurationInSec);
        ed.Apply();

        mRemoteControlClient.SetPlaybackState(RemoteControlClient.PlaystatePlaying, mediaPlayer.CurrentPosition, 1.0f);
    }
    else
    {

        MediaMetadata metadata = new MediaMetadata.Builder()
                .PutString(MediaMetadata.MetadataKeyTitle, CurrentSongObject.SongName)
                .PutString(MediaMetadata.MetadataKeyArtist, CurrentSongObject.ArtistName)
                .PutString(MediaMetadata.MetadataKeyAlbum, CurrentSongObject.AlbumName)
                .PutLong(MediaMetadata.MetadataKeyDuration, CurrentSongObject.DurationInSec)
                .Build();

        mMediaSession.SetMetadata(metadata);

        PlaybackState state = new PlaybackState.Builder()
                .SetActions(PlaybackState.ActionPlay)
                .SetState(PlaybackState.StatePlaying, mediaPlayer.CurrentPosition, 1.0f, SystemClock.ElapsedRealtime())
                .Build();

        mMediaSession.SetPlaybackState(state);
    }
}

Again, this works fine. I am calling the last function everytime I change the track on my phone and the car stereo displays everything as it should BUT now the broadcast receiver to catch the next, pause, prev, and play clicks from the car stopped working immediately.

They do however still work, when the phone is NOT sending out the info. Before sending info out, I am checking wether a device is connected or not like so:

    if (mAudioManager.BluetoothA2dpOn)
    {
        SendInfoToBluetoothDevice();
    }

With my headset on but no device connected, I can stop my music with the headset just fine. As soon as I am in my car and the car stereo now displays the songs (a device is now connected) I cannot use my headset hook anymore, neither my next or prev buttons on my car ...

This is especially frustrating since I have to try some code and then go back to my car with a release version on my phone to see if anything worked, but with no debugger attached. Also, I dont know what to change since both things work just fine individually but stop working once used together. Or at least the receiver stops working entirely when the sender is active.

Please, can anyone help me out here?

Thanks a lot!

Thank god, I have found it!

The problem was the mediaSession. This itself registered a broadcast receiver which interefered with my custom one. The issue was very simple:

First, remove the "old" broadcast receiver entirely, then go:

                mMediaSession = new MediaSession(this, "PlayerServiceMediaSession");
                mMediaSession.SetFlags(MediaSession.FlagHandlesTransportControls);
                mMediaSession.SetFlags(MediaSession.FlagHandlesMediaButtons);
                mMediaSession.Active = true;
                mMediaSession.SetCallback(new MediaButtonReceiver(this));

And for the custom class:

public class MediaButtonReceiver : MediaSession.Callback
{
    static long lastClick = 0;

    Context ctx;

    public MediaButtonReceiver(Context ctx)
    {
        this.ctx = ctx;
    }

    public override bool OnMediaButtonEvent(Intent mediaButtonIntent)
    {

        if (mediaButtonIntent.Action != Intent.ActionMediaButton)
            return false;

        var keyEvent = (KeyEvent)mediaButtonIntent.GetParcelableExtra(Intent.ExtraKeyEvent);

        switch (keyEvent.KeyCode)
        {
            case Keycode.Headsethook:

                if (canClick())
                    Activity_Player.Instance.PlayOrPauseLogic();
                break;
            case Keycode.MediaPlay:
                if (canClick())
                    Activity_Player.Instance.PlayOrPauseLogic();
                break;
            case Keycode.MediaPlayPause:
                if (canClick())
                    Activity_Player.Instance.PlayOrPauseLogic();
                break;
            case Keycode.MediaNext:
                if (Activity_Player.CurrentSongObject != null && canClick())
                    Activity_Player.Instance.ChooseRandomNewSongAndPlay(false);
                break;
            case Keycode.MediaPrevious:
                if (Activity_Player.CurrentSongObject != null && canClick())
                    Activity_Player.mediaPlayer.SeekTo(0);
                break;
        }

        return base.OnMediaButtonEvent(mediaButtonIntent);


    }


    private bool canClick()
    {
        if (lastClick < Java.Lang.JavaSystem.CurrentTimeMillis() - 500) // needs to be atleast one second bigger 
        {
            lastClick = Java.Lang.JavaSystem.CurrentTimeMillis();
            return true;
        }
        else
            return false;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM