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

Implement notification alerts using PhoneGap!

Hey everyone!

In my previous post, I spoke about the PhoneGap framework and how one can start using it for Android. In this post I will implement a basic Notification alert using the PhoneGap API.

Step 1: Create config.xml

You need to create a config.xml file under the assets/www folder and include the following content!

<?xml version="1.0" encoding="UTF-8"?>

<feature name="Notification">
  <param name="wp-package" value="Notification"/>
</feature>

Step 2: Make changes to existing HTML page

Now, let’s modify our previous HTML page to include a button and the PhoneGap notification alert.

<!DOCTYPE HTML>
<html>
<head>
<title>PhoneGap</title>
<script type="text/javascript" charset="utf-8" src="cordova-2.7.0.js"></script>

<script>

function showAlert(){

navigator.notification.alert("This is a test alert!", test , "New Alert" , "OK")

}


function test(){
   //do something
}

</script>
</head>
<body>
<h1>Test PhoneGap</h1>

<input type="button" onclick="showAlert();" id="btn" value="Click me to display alert"/>

</body>
</html>

Syntax for notification.alert method

navigator.notification.alert(message, alertCallback, [title], [buttonName]) 

where,

message: Dialog message (String)
alertCallback: Callback to invoke when alert dialog is dismissed. (Function)
title: Dialog title (String) (Optional, Default: “Alert”)
buttonName: Button name (String) (Optional, Default: “OK”)

When you click the button, you should see the following output!

phonegap_navigation_alert

Reference: PhoneGap Notification

Tutorial #3: Android 4.1(JellyBean) Notification Demo

I first read about JellyBean’s new notification features at this Android central blog post.

I even got a chance to view the demo live at the g|day India event in Mumbai.

jellybean.jpg file

I couldn’t wait to try this new feature out. Here is how I went about it.

Requirements: Android SDK, Eclipse Helios. Target SDK version Android 4.1

Step 1: Create a new Android project named NotificationBuilderExample with package name com.example. Make sure to select Android 4.1 as build SDK.

Step 2: Create activity named MainActivity

Note: If you are not familiar with Notifications in Android refer the links NotificationManager and Notification before proceeding further.

Step 3: Java code


public class MainActivity extends Activity 
  {
	private NotificationManager notificationManager;
	
     @Override
     public void onCreate(Bundle savedInstanceState) 
     {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        notificationManager = getNotificationManager();
        
         Builder build = new Notification.Builder(this)  //using the Notification.Builder class
        .setContentTitle("New JellyBean Notification")   
        .setContentText("This is just an example")
        .setSmallIcon(R.drawable.ic_action_search)
        .setPriority(Notification.PRIORITY_HIGH)
        .setSmallIcon(R.drawable.ic_action_search)
        
         .addAction(
         android.R.drawable.ic_btn_speak_now,
         "Speak",
          PendingIntent.getActivity(getApplicationContext(), 0,getIntent(), 0, null))
         
         .addAction(
         android.R.drawable.ic_dialog_email,
         "Email",
         PendingIntent.getActivity(getApplicationContext(), 0,getIntent(), 0, null))
         
         .addAction(
         android.R.drawable.ic_dialog_info,"Info", 
         PendingIntent.getActivity(getApplicationContext(), 0,getIntent(), 0, null));

         Notification notification = new Notification.BigPictureStyle(build)
         .bigPicture
         (
         BitmapFactory.decodeResource(getResources(),R.drawable.jellybean)).build();  //show image along with the notification
         Intent notificationIntent = new Intent(this, MainActivity.class);
         notificationManager.notify(0, notification);         
     }

     private NotificationManager getNotificationManager() 
      {
		return (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      }
	
    @Override
     public boolean onCreateOptionsMenu(Menu menu)
     {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
     }

}

No change is required for the XML files over here. Run the application and you should see a screen as shown in Figure 2:

Find the source code of the project over here.

Figure 2