Tutorial #90: Implement Media Style Notifications in Android

UPDATE: According to the latest release, the createSession() method has now been removed from the MediaSessionManager class. The below tutorial will hence not work as expected. Sorry for the inconvenience. I will try and post a working tutorial soon! ๐Ÿ™‚

SOLUTION: I have fixed the above problem and the working code can be found over here. Sorry for the delay.

Hello everyone!

Even before Google officially launched the Android 5.0 (Lollipop) SDK, a preview to the same was made available to developers to experience the new features. Apart from material design, another interesting feature was Notifications. The Android L (Preview SDK) added a new lock screen notification feature called Notification.MediaStyle. Up until this point lock screen media controls had to be implemented through the use of a RemoteView.

Starting from Android 5.0 (Lollipop), lock screen notifications are now displayed using Notification.MediaStyle template with the addAction() method, which converts actions into clickable icons. Through this tutorial, we will learn how to implement media style notifications in Android.

Pre-requisites: Eclipse IDE, Android SDK (L Preview)

Step 1: Create Android project

Create a new Android application project called AndroidLollipopExamples with both the build target and minimum SDK set to Android L (API level L). Let the package name be com.app.android.lollipop.

Step 2: Create Service class

In order to implement the media style lock screen notification, we need to create a Service class that will contain the various media controls & media session callbacks. Create a new class called MediaPlayerService and write the following code!

MediaPlayerService.java

package com.app.android.lollipop;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.Rating;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.os.IBinder;
import android.util.Log;

public class MediaPlayerService extends Service {

    private MediaPlayer m_objMediaPlayer;
    private MediaSessionManager m_objMediaSessionManager;
    private MediaSession m_objMediaSession;
    private MediaController m_objMediaController;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void handleIntent( Intent intent ) {
        if( intent == null || intent.getAction() == null )
            return;

        String action = intent.getAction();

        if( action.equalsIgnoreCase( Constants.ACTION_PLAY ) ) {
            m_objMediaController.getTransportControls().play();
        } else if( action.equalsIgnoreCase( Constants.ACTION_PAUSE ) ) {
            m_objMediaController.getTransportControls().pause();
        } else if( action.equalsIgnoreCase( Constants.ACTION_FAST_FORWARD ) ) {
            m_objMediaController.getTransportControls().fastForward();
        } else if( action.equalsIgnoreCase( Constants.ACTION_REWIND ) ) {
            m_objMediaController.getTransportControls().rewind();
        } else if( action.equalsIgnoreCase( Constants.ACTION_PREVIOUS ) ) {
            m_objMediaController.getTransportControls().skipToPrevious();
        } else if( action.equalsIgnoreCase(Constants.ACTION_NEXT ) ) {
            m_objMediaController.getTransportControls().skipToNext();
        } else if( action.equalsIgnoreCase( Constants.ACTION_STOP ) ) {
            m_objMediaController.getTransportControls().stop();
        }
    }

    private Notification.Action createAction( int icon, String title, String intentAction ) {
        Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class);
        intent.setAction( intentAction );
        PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
        return new Notification.Action.Builder( icon, title, pendingIntent ).build();

    }

    private void buildNotification( Notification.Action action ) {
            Notification.MediaStyle style = new Notification.MediaStyle();
            style.setMediaSession( m_objMediaSession.getSessionToken() );

            Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
            intent.setAction( Constants.ACTION_STOP );
            PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
            Notification.Builder builder = new Notification.Builder(this)
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setContentTitle( "Sample Title" )
                    .setContentText( "Sample Artist" )
                    .setDeleteIntent( pendingIntent )
                    .setStyle(style);
            

            builder.addAction( createAction( android.R.drawable.ic_media_previous, "Previous", Constants.ACTION_PREVIOUS ) );
            builder.addAction( createAction( android.R.drawable.ic_media_rew, "Rewind", Constants.ACTION_REWIND ) );
            builder.addAction( action );
            builder.addAction( createAction( android.R.drawable.ic_media_ff, "Fast Foward", Constants.ACTION_FAST_FORWARD ) );
            builder.addAction( createAction( android.R.drawable.ic_media_next, "Next", Constants.ACTION_NEXT ) );

            NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
            notificationManager.notify( 1, builder.build() );
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if( m_objMediaSessionManager == null ) {
            initMediaSessions();
        }

        handleIntent( intent );
        return super.onStartCommand(intent, flags, startId);
    }

    private void initMediaSessions() {
        m_objMediaPlayer = new MediaPlayer();
        m_objMediaSessionManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
        m_objMediaSession = m_objMediaSessionManager.createSession("sample session");
        m_objMediaController = MediaController.fromToken( m_objMediaSession.getSessionToken() );

        m_objMediaSession.addTransportControlsCallback( new MediaSession.TransportControlsCallback() {
            @Override
            public void onPlay() {
                super.onPlay();
                Log.e( Constants.LOG_TAG, "onPlay");
                buildNotification( createAction( android.R.drawable.ic_media_pause, "Pause", Constants.ACTION_PAUSE ) );
            }

            @Override
            public void onPause() {
                super.onPause();
                Log.e(Constants.LOG_TAG, "onPause");
                buildNotification(createAction(android.R.drawable.ic_media_play, "Play", Constants.ACTION_PLAY));
            }

            @Override
            public void onSkipToNext() {
                super.onSkipToNext();
                Log.e(Constants.LOG_TAG, "onSkipToNext");
                buildNotification( createAction( android.R.drawable.ic_media_pause, "Pause", Constants.ACTION_PAUSE ) );
            }

            @Override
            public void onSkipToPrevious() {
                super.onSkipToPrevious();
                Log.e(Constants.LOG_TAG, "onSkipToPrevious");
                buildNotification( createAction( android.R.drawable.ic_media_pause, "Pause", Constants.ACTION_PAUSE ) );
            }

            @Override
            public void onFastForward() {
                super.onFastForward();
                Log.e(Constants.LOG_TAG, "onFastForward");
            }

            @Override
            public void onRewind() {
                super.onRewind();
                Log.e(Constants.LOG_TAG, "onRewind");
            }

            @Override
            public void onStop() {
                super.onStop();
                Log.e(Constants.LOG_TAG, "onStop");
                NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
                notificationManager.cancel( 1 );
                Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
                stopService( intent );
            }

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

            @Override
            public void onSetRating(Rating rating) {
                super.onSetRating(rating);
            }
        });
    }

    @Override
    public boolean onUnbind(Intent intent) {
        m_objMediaSession.release();
        return super.onUnbind(intent);
    }
}

Constants.java

package com.app.android.lollipop;

public class Constants {

	public static final String ACTION_PLAY = "action_play";
	public static final String ACTION_PAUSE = "action_pause";
	public static final String ACTION_REWIND = "action_rewind";
	public static final String ACTION_FAST_FORWARD = "action_fast_foward";
	public static final String ACTION_NEXT = "action_next";
	public static final String ACTION_PREVIOUS = "action_previous";
	public static final String ACTION_STOP = "action_stop";
	public static final String LOG_TAG = "MediaService";
	
}

Step 2: Create Activity class

Create a new Activity class called MediaPlayerActivity that will start the above service.

MediaPlayerActivity.java

package com.app.android.lollipop;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class MediaPlayerActivity extends Activity{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
		intent.setAction( Constants.ACTION_PLAY );
		startService( intent );
	}
}

Step 3: Add permission in Manifest file

It is important for developers to add the new MEDIA_CONTENT_CONTROL permission in the AndroidManifest.xml file in order to implement the media style notification.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.app.android.lollipop"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-sdk
        android:minSdkVersion="L"
        android:targetSdkVersion="21" />

    <permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:name=".MediaPlayerActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.app.android.lollipop.SecondActivity"
            android:label="@string/app_name" />
        
        <service android:name=".MediaPlayerService"/>
   
     </application>
</manifest>

Save all changes. Make sure no errors are present. Run the application on an Android L emulator and you should see the following output!

media style notification

media style notification

Reference: Android Notifications

Karan Balkar About Karan Balkar
self proclaimed extraterrestrial fandroid, computer engineer, amateur gamer and die hard punk rock fan!

5 thoughts on “Tutorial #90: Implement Media Style Notifications in Android

  1. Great example! Thank you! One request… Could you show how to actually implement the MediaPlayer to handle play, pause, previous/next with actual audio files? I’m running into problems with initializing and handling the playback controls…

  2. Great post! Thank you! I do have one request, however. Would you be able to fully implement the MediaPlayer functionality? I’ve been running into problems with initializing the MediaPlayer object and handling the playback. Would definitely appreciate it! Controlling a seek bar from an app activity would be great too ๐Ÿ™‚

Leave a Reply

Your email address will not be published. Required fields are marked *