Media Session Compat not showing Lockscreen controls on Pre-Lollipop
Asked Answered
C

3

41

I'm using MediaSessionCompat from AppCompat Support Library Revision 22. And on Lollipop I'm getting notification & also the background of lockscreen is the album art. And everything works cool.

While on Pre-Lollipop devices, the music controls on lockscreen are not at all shown. It's weird & I tried everything but it doesn't show up, not even the background changes.

I hope someone has solution to this issue.

Note: RemoteControlClient used to work on Lollipop & KitKat

/**
 * Initializes the remote control client
 */
private void setupMediaSession() {
    /* Activate Audio Manager */
    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN);

    ComponentName mRemoteControlResponder = new ComponentName(getPackageName(),
            MediaButtonReceiver.class.getName());
    final Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    mediaButtonIntent.setComponent(mRemoteControlResponder);
    mMediaSessionCompat = new MediaSessionCompat(getApplication(), "JairSession", mRemoteControlResponder, null);
    mMediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    PlaybackStateCompat playbackStateCompat = new PlaybackStateCompat.Builder()
            .setActions(
                    PlaybackStateCompat.ACTION_SEEK_TO |
                    PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
                    PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
                    PlaybackStateCompat.ACTION_PLAY |
                    PlaybackStateCompat.ACTION_PAUSE |
                    PlaybackStateCompat.ACTION_STOP
            )
            .setState(
                    isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
                    getCurrentPosition(),
                    1.0f)
            .build();
    mMediaSessionCompat.setPlaybackState(playbackStateCompat);
    mMediaSessionCompat.setCallback(mMediaSessionCallback);
    mMediaSessionCompat.setSessionActivity(retrievePlaybackActions(5));
    mMediaSessionCompat.setActive(true);
    updateMediaSessionMetaData();
    mTransportController = mMediaSessionCompat.getController().getTransportControls();

Here's the updateMediaSessionMetaData() :

/**
 * Updates the lockscreen controls, if enabled.
 */
private void updateMediaSessionMetaData() {
            MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
            builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, getArtistName());
            builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, getAlbumName());
            builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, getTrackName());
            builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, getDuration());
            builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, MusicUtils.getArtwork(this, getAlbumID(), true));
            mMediaSessionCompat.setMetadata(builder.build());

}


The Media Session Callback methods

private final MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback() {

    @Override
    public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
        final String intentAction = mediaButtonEvent.getAction();
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {
            if (PrefUtils.isHeadsetPause(getBaseContext())) {
                Log.d(LOG_TAG, "Headset disconnected");
                pause();
            }
        } else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
            final KeyEvent event = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            if (event == null) return super.onMediaButtonEvent(mediaButtonEvent);
            final int keycode = event.getKeyCode();
            final int action = event.getAction();
            final long eventTime = event.getEventTime();
            if (event.getRepeatCount() == 0 && action == KeyEvent.ACTION_DOWN) {
                switch (keycode) {
                    case KeyEvent.KEYCODE_HEADSETHOOK:
                        if (eventTime - mLastClickTime < DOUBLE_CLICK) {
                            playNext(mSongNumber);
                            mLastClickTime = 0;
                        } else {
                            if (isPlaying())
                                pause();
                            else resume();
                            mLastClickTime = eventTime;
                        }
                        break;
                    case KeyEvent.KEYCODE_MEDIA_STOP:
                        mTransportController.stop();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                        if (isMediaPlayerActive()) {
                            if (isPlaying()) mTransportController.pause();
                            else mTransportController.play();
                        }
                        break;
                    case KeyEvent.KEYCODE_MEDIA_NEXT:
                        mTransportController.skipToNext();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                        mTransportController.skipToPrevious();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PAUSE:
                        mTransportController.pause();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PLAY:
                        mTransportController.play();
                        break;
                }
            }
        }
        return super.onMediaButtonEvent(mediaButtonEvent);
    }

    @Override
    public void onPlay() {
        super.onPlay();
        resume();
    }

    @Override
    public void onPause() {
        super.onPause();
        pause();
    }

    @Override
    public void onSkipToNext() {
        super.onSkipToNext();
        playNext(mSongNumber);
    }

    @Override
    public void onSkipToPrevious() {
        super.onSkipToPrevious();
        playPrevious(mSongNumber);
    }

    @Override
    public void onSeekTo(long pos) {
        super.onSeekTo(pos);
        seekTo(pos);
    }

    @Override
    public void onStop() {
        super.onStop();
        pause();
        commitMusicData();
        updatePlayingUI(STOP_ACTION);
        stopSelf();
    }
};

Media Button Receiver Manifest Entry

<!-- Media button receiver -->
    <receiver android:name=".receiver.MediaButtonReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
            <action android:name="android.media.AUDIO_BECOMING_NOISY" />
        </intent-filter>
    </receiver>

I'm trying since couple of weeks to solve this issue with no success, and in desperate need of help.

Edit: A tutorial or example of MediaSessionCompat would also be fine

Cyclostyle answered 19/6, 2015 at 15:35 Comment(18)
Revision 22.0 or 22.2? Can you include your code you use to create your MediaSessionCompat and where you setActive().Verney
@Verney You sure, I'll add the codeCyclostyle
Can you confirm you are using version 22.2.0 of the v4-support library? For debugging purposes, can you include whether mMediaSessionCompat.isActive() returns true and if mMediaSessionCompat.getRemoteControlClient() returns non-null on your Kitkat device right after all of the code you've posted runs?Verney
@Verney Thanks for the suggestion. I did that on Lollipop it gives mMediaSessionCompat.isActive() returns true and if mMediaSessionCompat.getRemoteControlClient() returns null. I'll check on KitKat soonCyclostyle
@Verney I checked on KitKat, mMediaSessionCompat.isActive() returns true and if mMediaSessionCompat.getRemoteControlClient() returns non-null. Yet no lockscreen controls :(Cyclostyle
@Verney Is this because I'm not using Notifications.MediaStyle to show Notification of Playback. Actually I'm using Custom Notification.Cyclostyle
@Verney I used Notifications.MediaStyle yet not working on Pre-Lollipop devicesCyclostyle
What version of the Support Library are you using?Verney
@Verney It's the latest v22.0Cyclostyle
The latest version is 22.2.0 - can you update to that and confirm this is still an issue?Verney
@Verney I did typo mistake in above comment, I use v22.2.0. And yet not working. But it does work on LollipopCyclostyle
@Verney I updated few things in the question. Mainly the codeCyclostyle
"I'm planning to test it on Real Device" -- I would never use an emulator for testing lockscreen behaviors like this. I would not be comfortable MediaSessionCompat or RemoteControlClient uses without testing it on at least a dozen devices, from a variety of manufacturers. That being said, I have never used MediaSessionCompat nor RemoteControlClient, so I have no specific fixes to suggest.Boult
@Verney Trying to show the lockscreen artwork for chromecast. to no avail. any ideas? much appreciated: #38752691Housemaid
@Akshay Chordia please can you provide complete code for this actually i am new to android and unable to understand the media sessions and all these things and how to use these things in notification or If you provide your notification code with the same question so it is also helpful....ThanksWoodcraft
can you please share your codeWoodcraft
@AkshayChordiya , the RemoteControlClient is only for apis of pre lollipop, right? Why not surround that with an if(<=lollipop) statement?Ptah
@AkshayChordiya , the lockscreen control works after doing this, but why does the lockscreen metadata not get updated?Ptah
C
9

Finally I figured a solution for this. Thanks to @ianhanniballake & @user1549672

  1. Add Audio Focus as suggested by @ianhanniballake
  2. Add Music Intent BroadcastReceiver can be found if searched on Google & also on Official Android Docs
  3. Write the setupMediaSession() given in my question above
  4. VERY IMPORTANT Mention the Flags properly
  5. Write MediaSessionCallbacks also available above
  6. VERY IMPORTANT Update the MediaSession on MediaPlayer#onPause(), MediaPlayer#onStart() & finally when new song is played (also includes next & previous played) mentioned by @user1549672
  7. Release the MediaSession object in onDestory()

Well that's all, most of the material (code) is available above. This question took couple of months to solve, finally it's done.

Cyclostyle answered 18/9, 2015 at 16:1 Comment(7)
bro how you are displaying the lock scree media controllerPudendas
@Pudendas I think this piece of line tells the Android OS to show music controls on the lockscreen. mMediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);Cyclostyle
bhai can you tell me where I am doing wrong I am not getting lock screen #45252234Pudendas
bro I followed what you said but dont know where I am making mistake bounty is onn but still no ans hope you may guide mePudendas
Bro i am able to get the lockscreen but it show only play pause button how i can show the next and previous button any advice?Pudendas
@Pudendas Great. To show other buttons you need to set actions using mMediaSessionCompat.setPlaybackState(new PlaybackStateCompat.Builder() .setState(playState, position(), 1.0f) .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE| PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS).build());Cyclostyle
@Akshay Chordiya as i see above and your answer it is difficult to implement this so if it is possible so can you please provide complete answer it will very helpful for me PLEASE SHARE YOUR ANSWER HERE #54596075 thaks in advanceWoodcraft
V
11

While not strictly required for MediaSession, RemoteControlClient used on API14-19 devices, does require audio focus and it is 100% strongly recommended for all media playback.

Adding lines such as:

AudioManager audioManager = (AudioManager)
    getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this,
    AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_GAIN) {
    return; //Failed to gain audio focus
}

Before playing any media should gain audio focus and show controls.

Verney answered 11/7, 2015 at 5:18 Comment(16)
I'm already using it, but I was using it below MediaSessionCompat, I shifted the AudioManager code above it. Yet it doesn't seem to work on EmulatorCyclostyle
You'll need to include a lot more code in your question then, ideally your entire service and your Manifest entry for the Media Button ReceiverVerney
Facing the same issue. Can anyone help please?Lab
@RisingUp I felt, I'm the only one facing this issue. Hopefully we solve itCyclostyle
Your code is working fine for me on an API 16 emulator - check out this super minimal example that shows lock screen controls for me.Verney
@Verney I'll check your minimal code, hope it workCyclostyle
@Verney I'll check your minimal code, hope it work. BTW is any permission in Manifest required?Cyclostyle
@Aky - the only ones I had was INTERNET and ACCESS_NETWORK_STATE for downloading the mp3 I was testing with - I stripped even that out of the example (there's actually no playback at all in the example - just simulated). I did note that things in general worked much better on a real device than the emulator, so perhaps the emulator just isn't any good at testing these type of things.Verney
@Verney I'm playing with your minimal example, I don't know but I'm damn pissed by this issueCyclostyle
@Verney @RisingUp Mainly I don't understand, how come it works on Lollipop & not on Pre-LollipopCyclostyle
Have you tried it on a real device? You may be chasing ghosts and emulator issues.Verney
@Verney I sometimes usually try on real device but when I don't get real device I try on emulator, the minimal example was tested on Emulator & I'm planning to test it on Real DeviceCyclostyle
@Aky - since the sample I provided works, the problem is with your code. Can you include any parts of your code that differ from the sample provided?Verney
@Verney You suggest me what should I attach? Because I have added most of the things required.Cyclostyle
the simple example that ianhanniballake posted worked for me!Altaf
@Altaf Superb, I'm yet stuck with this issue & have less timeCyclostyle
C
9

Finally I figured a solution for this. Thanks to @ianhanniballake & @user1549672

  1. Add Audio Focus as suggested by @ianhanniballake
  2. Add Music Intent BroadcastReceiver can be found if searched on Google & also on Official Android Docs
  3. Write the setupMediaSession() given in my question above
  4. VERY IMPORTANT Mention the Flags properly
  5. Write MediaSessionCallbacks also available above
  6. VERY IMPORTANT Update the MediaSession on MediaPlayer#onPause(), MediaPlayer#onStart() & finally when new song is played (also includes next & previous played) mentioned by @user1549672
  7. Release the MediaSession object in onDestory()

Well that's all, most of the material (code) is available above. This question took couple of months to solve, finally it's done.

Cyclostyle answered 18/9, 2015 at 16:1 Comment(7)
bro how you are displaying the lock scree media controllerPudendas
@Pudendas I think this piece of line tells the Android OS to show music controls on the lockscreen. mMediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);Cyclostyle
bhai can you tell me where I am doing wrong I am not getting lock screen #45252234Pudendas
bro I followed what you said but dont know where I am making mistake bounty is onn but still no ans hope you may guide mePudendas
Bro i am able to get the lockscreen but it show only play pause button how i can show the next and previous button any advice?Pudendas
@Pudendas Great. To show other buttons you need to set actions using mMediaSessionCompat.setPlaybackState(new PlaybackStateCompat.Builder() .setState(playState, position(), 1.0f) .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE| PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS).build());Cyclostyle
@Akshay Chordiya as i see above and your answer it is difficult to implement this so if it is possible so can you please provide complete answer it will very helpful for me PLEASE SHARE YOUR ANSWER HERE #54596075 thaks in advanceWoodcraft
L
5

Finally I got an answer to your's and my problem .Issue is you need to specify actions (mMediaSessionCompat.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE) even while updating mediasession. So your code should now look like

  private void updateMediaSessionMetaData() {
     int playState = mPlaying
            ? PlaybackStateCompat.STATE_PLAYING
            : PlaybackStateCompat.STATE_PAUSED;
           mMediaSessionCompat.setMetadata(new MediaMetadataCompat.Builder()
                .putString(MediaMetadata.METADATA_KEY_ARTIST, getArtist())
                .putString(MediaMetadata.METADATA_KEY_ALBUM, getAlbum())
                .putString(MediaMetadata.METADATA_KEY_TITLE, getSongTitle())
                .putLong(MediaMetadata.METADATA_KEY_DURATION, duration())
                .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, mSongPosn)
                .putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, songs.size())
                .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, albumArt)
                .build());
mMediaSessionCompat.setPlaybackState(new PlaybackStateCompat.Builder()
                .setState(playState, position(), 1.0f)
                .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE| PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS).build()); 

UPDATE : Added code for MediaCallback & Receiver

   private final class MediaSessionCallback extends MediaSessionCompat.Callback {

    @Override
    public void onPlay() {
        pausePlayer();
    }
    @Override
    public void onPause() {
        pausePlayer();
    }
    public void onSeekTo(long pos) {
        seek(pos);
    }
    @Override
    public void onSkipToNext() {
        playNext();
    }
    @Override
    public void onSkipToPrevious() {
        playPrev();
    }
}

Receiver :

 public class MusicIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {

        if (intent.getAction().equals(
                android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {

            Intent intent1 = new Intent(MusicService.ACTION_PAUSE);
            intent1.setClass(context,
                    com.xyz.service.MusicService.class);
            // send an intent to our MusicService to telling it to pause the
            // audio
            context.startService(intent1);

        } else if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON)) {

            KeyEvent keyEvent = (KeyEvent) intent.getExtras().get(
                    Intent.EXTRA_KEY_EVENT);
            if (keyEvent.getAction() != KeyEvent.ACTION_DOWN)
                return;

            switch (keyEvent.getKeyCode()) {
                case KeyEvent.KEYCODE_HEADSETHOOK:
                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                    Intent intentToggle = new Intent(
                            MusicService.ACTION_TOGGLE_PLAYBACK);
                    intentToggle.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentToggle);
                    break;
                case KeyEvent.KEYCODE_MEDIA_PLAY:
                    Intent intentPlay = new Intent(MusicService.ACTION_PLAY);
                    intentPlay.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentPlay);

                    break;
                case KeyEvent.KEYCODE_MEDIA_PAUSE:
                    Intent intentPause = new Intent(MusicService.ACTION_PAUSE);
                    intentPause.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentPause);

                    break;
                case KeyEvent.KEYCODE_MEDIA_NEXT:
                    Intent intentNext = new Intent(MusicService.ACTION_NEXT);
                    intentNext.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentNext);

                    break;
                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                    Intent intentPrev = new Intent(MusicService.ACTION_PREV);
                    intentPrev.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentPrev);

                    break;
                default:
                    break;
            }
        }
    }
}
Lab answered 16/7, 2015 at 18:2 Comment(12)
I tested on real device without using track number & number tracks unfortunately it didn't work again.Cyclostyle
@Aky Did you .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE both while setting up Mediasession & updating media session both?Lab
I'll check that out, but are you using MediaStyle for NotificationCyclostyle
Is that the reason why 'MediaSessionCompat' is not working on Pre LollipopCyclostyle
It still works. Just try my code and tell me if you get stuck.Lab
Can you put your whole service,receiver & related code in pastebin or something. Let me check your code on my device.Lab
Try this one .I have modified your code to remove some unnecessary stuffs. pastebin.com/Kut7JEYaLab
I'm glad, I'll try it out & let you knowCyclostyle
I tested it & didn't work. I'll perform few more tests for sure results. PS Used real deviceCyclostyle
I wonder what's going wrong. I would suggest you to break code into small snippets and test functionality one by one.Lab
Ohhkay, I'll try that tooCyclostyle
can you please provide me the full code i am stuck from last month on this and unable to handle some problems like How to get mediasession callbacks? What is the hierarchy to set all things like mediasession, callbacks, AudioFocus, PlaybackStatus? also i don't know how and why we use playbackstatusWoodcraft

© 2022 - 2024 — McMap. All rights reserved.