Media Session Compat not showing Lockscreen controls on Pre-Lollipop

11,107

Solution 1

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.

Solution 2

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.

Solution 3

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;
            }
        }
    }
}
Share:
11,107
Akshay Chordiya
Author by

Akshay Chordiya

Tech Enthusiast who likes learning new tools, platforms, frameworks and a bunch of similar things. I love working on Android and it's my niche, one of the reason being it helps to build something which helps me. My Short Story I got involved in Computers by playing with them, tweaking and breaking the hardware and software. I started development and coding with Symbian Operation System (Now Deprecated). Later completing my Engineering during which I learned lots of new languages, frameworks, and basically a lot about Computers. Let's look at stats Platform Android Programming Languages Kotlin (Favorite) Java (Also Java 8) Java EE HTML 5 Python Go JavaScript C and C++ (I don't like them much though :-P) Framework Django Spring Boot Databases MySQL Postgres Oracle 11g Hadoop 1 MongoDB (Basic) Complete Story Part 1: Back to Basics I worked on Symbian which got deprecated. Then I started learning Java, C++ and C to create some cool simple software(s) to make my life easier using the Desktop Machine. Slowly I started feeling that there would be lots of people who might make use of my softwares designed but I was immature at that time and didn't have internet at home. My Work on Symbian Part 2: Joining College and Falling in Love with Android I started with Computer Engineering where I got to learn Databases like MySQL, Mongo and Hadoop, Java-EE and few small things and make more better software to help people out. But I wanted to do something more hence I started learning Android individually. I started with Android app development without having Internet at my home. Currently it's been more than 3.5 years since I started. My goal is to make my life easier and then my first useful app Automaton Locker Play Store link, it is on Play Store with more than 1 Lakh downloads. I got lots of help from XDA-Developers too and I fell in love with Android :) Part 3: Continued Making great Android apps I didn't stop with my first app. I made lots of quality apps and I'm always backed by my friends even I've never met some of them in real. Jair Music Player is one of them and now it has more than 300K+ downloads. All my apps on Play Store Part 4: Continue my Passion the entire Life This part is coming soon so stay connected ;)

Updated on June 03, 2022

Comments

  • Akshay Chordiya
    Akshay Chordiya almost 2 years

    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
    Akshay Chordiya almost 9 years
    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 Emulator
  • ianhanniballake
    ianhanniballake almost 9 years
    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 Receiver
  • Siju
    Siju almost 9 years
    Facing the same issue. Can anyone help please?
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @RisingUp I felt, I'm the only one facing this issue. Hopefully we solve it
  • ianhanniballake
    ianhanniballake almost 9 years
    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.
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @ianhanniballake I'll check your minimal code, hope it work
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @ianhanniballake I'll check your minimal code, hope it work. BTW is any permission in Manifest required?
  • ianhanniballake
    ianhanniballake almost 9 years
    @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.
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @ianhanniballake I'm playing with your minimal example, I don't know but I'm damn pissed by this issue
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @ianhanniballake @RisingUp Mainly I don't understand, how come it works on Lollipop & not on Pre-Lollipop
  • ianhanniballake
    ianhanniballake almost 9 years
    Have you tried it on a real device? You may be chasing ghosts and emulator issues.
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @ianhanniballake 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 Device
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @ianhanniballake Any solution?
  • ianhanniballake
    ianhanniballake almost 9 years
    @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?
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @ianhanniballake You suggest me what should I attach? Because I have added most of the things required.
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    I tested on real device without using track number & number tracks unfortunately it didn't work again.
  • Siju
    Siju almost 9 years
    @Aky Did you .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE both while setting up Mediasession & updating media session both?
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    I'll check that out, but are you using MediaStyle for Notification
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    Is that the reason why 'MediaSessionCompat' is not working on Pre Lollipop
  • Siju
    Siju almost 9 years
    It still works. Just try my code and tell me if you get stuck.
  • Siju
    Siju almost 9 years
    Can you put your whole service,receiver & related code in pastebin or something. Let me check your code on my device.
  • Siju
    Siju almost 9 years
    Try this one .I have modified your code to remove some unnecessary stuffs. pastebin.com/Kut7JEYa
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    I'm glad, I'll try it out & let you know
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    I tested it & didn't work. I'll perform few more tests for sure results. PS Used real device
  • Siju
    Siju almost 9 years
    I wonder what's going wrong. I would suggest you to break code into small snippets and test functionality one by one.
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    Ohhkay, I'll try that too
  • user1549672
    user1549672 almost 9 years
    the simple example that ianhanniballake posted worked for me!
  • Akshay Chordiya
    Akshay Chordiya almost 9 years
    @user1549672 Superb, I'm yet stuck with this issue & have less time
  • SAVVY
    SAVVY almost 7 years
    bro how you are displaying the lock scree media controller
  • Akshay Chordiya
    Akshay Chordiya almost 7 years
    @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);
  • SAVVY
    SAVVY almost 7 years
    bhai can you tell me where I am doing wrong I am not getting lock screen stackoverflow.com/questions/45251734/…
  • SAVVY
    SAVVY over 6 years
    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
    SAVVY over 6 years
    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?
  • Akshay Chordiya
    Akshay Chordiya over 6 years
    @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_PREVI‌​OUS).build());
  • Vipul Chauhan
    Vipul Chauhan over 5 years
    @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 stackoverflow.com/questions/54595575/… thaks in advance
  • Vipul Chauhan
    Vipul Chauhan about 5 years
    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 playbackstatus