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

Akshay Chordiya
  • 4,761
  • 3
  • 40
  • 52
  • Revision 22.0 or 22.2? Can you include your code you use to create your `MediaSessionCompat` and where you `setActive()`. – ianhanniballake Jun 19 '15 at 16:25
  • @ianhanniballake You sure, I'll add the code – Akshay Chordiya Jun 19 '15 at 18:03
  • @ianhanniballake Added the code – Akshay Chordiya Jun 19 '15 at 18:30
  • 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? – ianhanniballake Jun 19 '15 at 18:41
  • @ianhanniballake 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 soon – Akshay Chordiya Jun 22 '15 at 15:47
  • @ianhanniballake I checked on KitKat, `mMediaSessionCompat.isActive()` returns `true` and if `mMediaSessionCompat.getRemoteControlClient()` returns non-null. Yet no lockscreen controls :( – Akshay Chordiya Jun 23 '15 at 12:02
  • @ianhanniballake Is this because I'm not using `Notifications.MediaStyle` to show Notification of Playback. Actually I'm using Custom Notification. – Akshay Chordiya Jul 08 '15 at 13:52
  • @ianhanniballake I used Notifications.MediaStyle yet not working on Pre-Lollipop devices – Akshay Chordiya Jul 08 '15 at 15:53
  • What version of the Support Library are you using? – ianhanniballake Jul 08 '15 at 21:24
  • @ianhanniballake It's the latest v22.0 – Akshay Chordiya Jul 09 '15 at 13:49
  • The latest version is 22.2.0 - can you update to that and confirm this is still an issue? – ianhanniballake Jul 09 '15 at 16:36
  • @ianhanniballake I did typo mistake in above comment, I use v22.2.0. And yet not working. But it does work on `Lollipop` – Akshay Chordiya Jul 09 '15 at 16:39
  • @ianhanniballake I updated few things in the question. Mainly the code – Akshay Chordiya Jul 10 '15 at 03:59
  • "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. – CommonsWare Jul 16 '15 at 14:26
  • @ianhanniballake Trying to show the lockscreen artwork for chromecast. to no avail. any ideas? much appreciated: http://stackoverflow.com/questions/38752191/chromecast-sender-v2-7-0-lockscreen-image – user2511882 Aug 03 '16 at 23:10
  • @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....Thanks – Vipul Chauhan Feb 19 '19 at 17:03
  • can you please share your code – Vipul Chauhan Feb 22 '19 at 14:13
  • @AkshayChordiya , the RemoteControlClient is only for apis of pre lollipop, right? Why not surround that with an if(<=lollipop) statement? – juztcode Sep 15 '20 at 05:11
  • @AkshayChordiya , the lockscreen control works after doing this, but why does the lockscreen metadata not get updated? – juztcode Sep 20 '20 at 03:23

3 Answers3

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.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
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.

Community
  • 1
  • 1
Akshay Chordiya
  • 4,761
  • 3
  • 40
  • 52
  • bro how you are displaying the lock scree media controller – SAVVY Jul 22 '17 at 05:59
  • @SAVVY 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);` – Akshay Chordiya Jul 22 '17 at 09:10
  • bhai can you tell me where I am doing wrong I am not getting lock screen https://stackoverflow.com/questions/45251734/lockscreen-for-media-control-is-not-showing-in-android – SAVVY Jul 23 '17 at 07:42
  • 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 me – SAVVY Sep 16 '17 at 19:06
  • 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? – SAVVY Oct 17 '17 at 07:54
  • 1
    @SAVVY 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());` – Akshay Chordiya Oct 18 '17 at 07:09
  • @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 https://stackoverflow.com/questions/54595575/how-to-set-this-type-of-view-for-a-notification thaks in advance – Vipul Chauhan Feb 11 '19 at 08:00
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;
            }
        }
    }
}
Siju
  • 2,585
  • 4
  • 29
  • 53