android – NotificationListenerService Implementation-ThrowExceptions

Exception or error:

I am trying to implement NotificationListnerService which is added in android 4.3 but I am not able to get the notification details.

My code are as below

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setSmallIcon(R.drawable.ic_launcher);
        mBuilder.setContentTitle("notification test");
        mBuilder.setContentText("Notification text");
        mBuilder.setAutoCancel(true);
        Intent resultIntent = new Intent(this, ResultActivity.class);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        // Adds the back stack for the Intent (but not the Intent itself)
        stackBuilder.addParentStack(ResultActivity.class);
        // Adds the Intent that starts the Activity to the top of the stack
        stackBuilder.addNextIntent(resultIntent);
        PendingIntent resultPendingIntent =
                stackBuilder.getPendingIntent(
                    0,
                    PendingIntent.FLAG_UPDATE_CURRENT
                );
        mBuilder.setContentIntent(resultPendingIntent);
        NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(1, mBuilder.build());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

public class NotificationListenerTesting extends NotificationListenerService{

    public static String TAG = "NotificationListenerTesting";
    //private StatusBarNotification[] mStatusBarNotification;

    @Override
    public void onCreate(){
        super.onCreate();
        Log.d(TAG, "Inside on create");
    }
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        TAG = "onNotificationPosted";
        Log.d(TAG, "id = " + sbn.getId() + "Package Name" + sbn.getPackageName() + 
                "Post time = " + sbn.getPostTime() + "Tag = " + sbn.getTag());
    }
    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
        TAG = "onNotificationRemoved";
        Log.d(TAG, "id = " + sbn.getId() + "Package Name" + sbn.getPackageName() + 
                "Post time = " + sbn.getPostTime() + "Tag = " + sbn.getTag());

    }

}

Android manifest file is

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.notificationtest"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.notificationtest.MainActivity"
            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.example.notificationtest.ResultActivity"></activity>
        <service android:name="com.example.notificationtest.NotificationListenerTesting"
            android:label="notification"
            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService"/>
            </intent-filter>
        </service>
    </application>
</manifest>

but after notification click or on notification post NotificationListenerService is not getting called , Whats wrong in this or did I miss somthing ? How to implement it ?

How to solve:

Inside the NotificationListenerService you need a looper to communicate with GUI thread so you can create a broadcast to handle the GUI interaction.

Hope this example will help you.

###

You need to grant access to your app to read notifications:
“Settings > Security > Notification access” and check your app.

###

At least one problem with your code is that your implementation of onBind()

There is no necessity to override this method. But if you must, then at least return the IBinder returned by the superclass.

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

###

It might be a bit late. But a few months ago I also struggled with making NotificationListenerService work.

Since then I`ve learned how to implement it and felt like building a implementation tutorial to help others who went through the same as me

If anyone is interested, check the project here:
https://github.com/Chagall/notification-listener-service-example

Hope it helps someone who is struggling with it.

###

I know its too late to answer the question , but as I was not able to find Settings > Sound and notifications -> Notification access, so I directly allow access of notifications to my app by firing this intent:

startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));

###

I ran into the same problem and have found some clues for that.

NotificationListenerService may not be running unless you call bindService() to start it.

Sometimes it started automatically when you enabled “Notification access” and sometimes it’s not.

###

The notification that you build does not have “tickerText.” I have found that if the notification does not have tickerText, onNotificationPosted does not get called.

In your code add mBuilder.setTicker( “your text here” ).

onNotificationPosted should now be called assuming that the rest of the NotificationListenerService set up is copacetic.

###

I am doing the same thing as in GitHub, but still I am not going into the notification class.

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;

/**
 * @author dinesh
 *
 */
public class UserNotificationService extends NotificationListenerService {

    private String TAG = "UserNotificationService";
    UserNotificationServiceReceiver notfRcvr;

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {

        Log.i(TAG,"********** onNotificationRemoved");
    Log.i(TAG,"ID :" + sbn.getId() + "\t" + sbn.getNotification().tickerText +"\t" + sbn.getPackageName());
    Intent i = new Intent("de.tu.darmstadt.moodsense.services.Notification");
    i.putExtra("notification event", "On notification removed");
    sendBroadcast(i);

    }

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

    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {

        Log.i(TAG,"**********  onNotificationPosted");
    Log.i(TAG,"ID :" + sbn.getId() + "\t" + sbn.getNotification().tickerText + "\t" + sbn.getPackageName());
    Intent i = new Intent("de.tu.darmstadt.moodsense.services.Notification");
    i.putExtra("notification event", "On notification posted");
    sendBroadcast(i);

    }

    @Override
    public void onCreate() {
        super.onCreate();
        notfRcvr = new UserNotificationServiceReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("de.tu.darmstadt.moodsense.services.Notification");
        registerReceiver(notfRcvr, filter);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(notfRcvr);
    }

    class UserNotificationServiceReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
            if(intent.getStringExtra("command").equals("clearall")) {
                UserNotificationService.this.cancelAllNotifications();
            }
        }

    }
}



import java.lang.Thread.State;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import twitter4j.Status;
import twitter4j.TwitterException;
import de.tu.darmstadt.moodsense.R;
import de.tu.darmstadt.moodsense.app.UserMood;
import de.tu.darmstadt.moodsense.constants.Constants;
import de.tu.darmstadt.moodsense.util.MqttMoodClient;
import de.tu.darmstadt.moodsense.util.TwitterMoodUtils;
import de.tu.darmstadt.moodsense.util.TwitterUtils;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;


/**
 * @author dinesh
 * Added for V1.1
 * Code style based on : https://newcircle.com/s/post/1049/
 *                       tutorial_services_part_1_android_bootcamp_series_2012
 *
 */
public class UserMoodService extends Service{

    static final String TAG = "UserMoodService";
    public static boolean userMoodSet = false;
    //declarations for twitter
    private SharedPreferences prefs;
    SharedPreferences userPref;
    String userTwitterMood = "";
    String worldTwitterMood = "";
    String screenName, userName;
    int m_counter;
    long shortMinutes;
    boolean m_enterMood;
    int m_myMood;
    int m_moodIntensity;
    MqttMoodClient mqc;
    TwitterMoodUtils tmu;
    Calendar cal = Calendar.getInstance();  

    private static final int MY_NOTIFICATION_ID=1;
    NotificationManager notificationManager;
    Notification myNotification;
    UserMoodNotificationReceiver usrMoodNotfnnRcvr;

    public UserMoodService() {
        // TODO Auto-generated constructor stub
        mqc = new MqttMoodClient();
        tmu = new TwitterMoodUtils();
    }
    public void reset() {

        m_myMood = Constants.NUM_MOOD_TYPES;
        m_moodIntensity = Constants.MILD;
        m_enterMood = false;

        m_counter = 0;

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

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        // TODO Auto-generated method stub
        Intent restartService = new Intent(getApplicationContext(),this.getClass());
        restartService.setPackage(getPackageName());
        PendingIntent restartServicePI = PendingIntent.getService(getApplicationContext(),
                1, restartService, PendingIntent.FLAG_ONE_SHOT);

         AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +100, restartServicePI);
    }

    /** (non-Javadoc)
     * @see android.app.Service#onCreate()
     */
    @Override
    public void onCreate() {
        Log.d(TAG, "OnCreation");
        //super.onCreate();
        usrMoodNotfnnRcvr = new UserMoodNotificationReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("Notofication Obj");
        registerReceiver(usrMoodNotfnnRcvr, filter);

    }


    /** (non-Javadoc)
     * @see android.app.Service#onStartCommand(android.content.Intent, int, int)
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "OnStartCommand");
        try {
            ConnectivityManager cm =
                (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo netInfo = cm.getActiveNetworkInfo();
            if (netInfo != null && netInfo.isConnectedOrConnecting()) { 
                Log.d(TAG,"Twitter loop enter");
                //Check the user's mood on twitter
                computeMoodOnTwitter();
                if(userMoodSet) {
                    Log.d(TAG, "user's twitter mood" + userTwitterMood);
                } /*else {
                    Log.d(TAG, "user mood not set, world mood computation started");
                    //If user's mood is not set then check for world's mood
                }*/

            }
        } catch(Exception e) {
            e.printStackTrace();
        }

        return START_STICKY;
    }

    private void computeMoodOnTwitter() {
        // TODO Auto-generated method stub
        reset();
        this.prefs = PreferenceManager.getDefaultSharedPreferences(this);
        Thread twitterThread;
        twitterThread = new Thread() {
            public void run() {
                //userMoodSet = false;
                Log.d(TAG, "User mood is :: "+ userMoodSet);
            /*try {
                  String usrNme =  TwitterUtils.getUserName(prefs).toString();

                  List<Status> statuses = TwitterUtils.getHomeTimeline(prefs);

                     for(int i=0; i < Constants.NUM_MOOD_TYPES; i++) {                  
                        for (int j =0 ; j < Constants.NUM_MOOD_TYPES; j++)
                        {           
                            for (twitter4j.Status status : statuses) {

                                //Check if the status is from the user and it matches our mood strings
                            if(status.getText().contains(Constants.searchStrings[i][j])
                                    && (status.getUser().getScreenName().equals(usrNme))) {
                                Date date = status.getCreatedAt();
                                long Minutes = tmu.getMinuteDifference(cal.getTime(), date);

                                if((Constants.sdf.format(date).equals(Constants.sdf.format(cal.getTime())))) {
                                  //Increment counter for each tweet
                                    Log.d(TAG, "User has a status");
                                    userMoodSet = true;
                                    m_counter++;
                                   //track time for the first tweet
                                   if(m_counter == 1) {
                                    shortMinutes = Minutes;
                                    m_moodIntensity = computeMoodIntensity(i,j);
                                    m_myMood = i;
                                    Log.d(TAG, "intensity + mood" + m_moodIntensity +","+ m_myMood);
                                    Log.d(TAG,"SocialMood:: mymood- " + Constants.moodIntensityNames[m_moodIntensity]+
                                               " "+ Constants.moodNames[m_myMood]);                                    
                                    Log.d(TAG, "SocialMood:: status-"+status.getText());                                        

                                   } else //counter more than 1   //track time for the later tweets 
                                   {  //take latest tweet only if logged minutes is shorter than earlier minutes
                                       if(Minutes < shortMinutes) {
                                          shortMinutes = Minutes;
                                          Log.d(TAG, "Called compute mood_intensity :: "+ m_counter);
                                          m_moodIntensity = computeMoodIntensity(i,j);
                                          m_myMood = i;
                                        }

                                   }                                    
                                  }
                                }
                              }
                            }
                         }                                                                               
                    } catch(TwitterException te) {
                      userMoodSet = false;                                  
                        Log.d(TAG, "Unable to process twitter get requests "+te.getErrorCode()+ " "+ te.getErrorMessage());

                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        Log.d(TAG,"Error msg");
                        e.printStackTrace();
                    }*/

                try {
                    stopThread(this);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };

        twitterThread.start();  

    }

    public int computeMoodIntensity(int m_detect, int m_type) {
        // TODO Auto-generated method stub
        for(int j=0; j < Constants.m_extreme.length; j++) {
            if(m_type == Constants.m_extreme[m_detect][j])
                return Constants.EXTREME;
        }
        for(int j=0; j < Constants.m_considerable.length; j++) {
            if(m_type == Constants.m_considerable[m_detect][j])
                return Constants.CONSIDERABLE;
        }

        return Constants.MILD;

    }

    private String userStatusToMood(int myMood) {
        // TODO Auto-generated method stub
        String userMood = Constants.userNoTwitter;
         if(m_myMood >= Constants.NUM_MOOD_TYPES) {
             m_enterMood = true;                
         Log.d(TAG, userMood);
         //Unreachable code - maybe we need to delete this ?? QNS
         /*Intent i = new Intent(UserMoodService.this,UserMood.class);
             i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             startActivity(i);*/
         }
         else {
             userMood = "User mood is "+ Constants.moodNames[m_myMood];
             userTwitterMood = Constants.moodIntensityNames[m_moodIntensity]
                     +" "+Constants.moodNames[m_myMood];

             Log.d(TAG, "Updated user mood is "+userTwitterMood);   
             //call MQTT
             MqttMoodClient mqc = new MqttMoodClient();

             mqc.setupMqttClient();
             mqc.sendMessage(userTwitterMood);

         }
         return userMood;
    }

    private void stopThread(Thread theThread) throws Exception {
        // method to stop the worker thread once the process needed to do has been completed
        Log.d(TAG,"userMoodSet :: "+ userMoodSet);
        if (theThread != null)
        {
            theThread = null;
            Log.d(TAG, "Execution complete inside stop thread");
            if(userMoodSet)
                userStatusToMood(m_myMood);         
        }

        if(!userMoodSet) {
            Log.d(TAG, "In world thread");
            //Call world Service
            //WorldMoodService worldService = new WorldMoodService();
            //worldService.computeWorldMood(this);
            //show notification!!
            /**
             * V1.1 
             * @author dinesh
             * Code adapted from : http://android-er.blogspot.de/2013/06/
             *                     start-activity-once-notification-clicked.html
             */
            Intent myIntent = new Intent(UserMoodService.this, UserMood.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(
                    UserMoodService.this, 
                0, 
                myIntent, 
                Intent.FLAG_ACTIVITY_NEW_TASK);

            myNotification = new NotificationCompat.Builder(UserMoodService.this)
            .setContentTitle("MoodSense notification")
            .setContentText("Please enter mood to play music as per your mood")
            .setTicker("Please enter mood to play music as per your mood")
            .setWhen(System.currentTimeMillis())
            .setContentIntent(pendingIntent)
            .setDefaults(Notification.DEFAULT_SOUND)
            .setAutoCancel(true)
            .setSmallIcon(R.drawable.app_icon)
            .build();

            notificationManager = 
                       (NotificationManager)UserMoodService.this.
                       getSystemService(Context.NOTIFICATION_SERVICE);
                     notificationManager.notify(MY_NOTIFICATION_ID, myNotification);                                

        } else if (userMoodSet) {
            Intent i = new Intent("de.tu.darmstadt.moodsense.services.Notification");
            i.putExtra("command", "clear all");
            sendBroadcast(i);
        }
    }

    public class UserMoodNotificationReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String temp = intent.getStringExtra("notification event");

        }

    }
    /** (non-Javadoc)
     * @see android.app.Service#onDestroy()
     */
    @Override
    public void onDestroy() {   
        Log.d(TAG, "OnDeletion");
        super.onDestroy();
    }   
}

###

Update 2020 Kotlin And AndroidX

enter image description here

package com.hiteshsahu.notificationlistener.notification

import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager

class CustomNotificationListenerService : NotificationListenerService() {

    private var commandFromUIReceiver: CommandFromUIReceiver? = null


    override fun onCreate() {
        super.onCreate()

        // Register broadcast from UI
        commandFromUIReceiver = CommandFromUIReceiver()
        val filter = IntentFilter()
        filter.addAction(READ_COMMAND_ACTION)
        registerReceiver(commandFromUIReceiver, filter)
    }


    /**
     * New Notn Added Callback
     */
    override fun onNotificationPosted(newNotification: StatusBarNotification) {
        Log.i(
            TAG,
            "-------- onNotificationPosted(): " + "ID :" + newNotification.id + "\t" + newNotification.notification.tickerText + "\t" + newNotification.packageName
        )
        sendResultOnUI("onNotificationPosted :" + newNotification.packageName + "\n")
    }

    /**
     * Notn Removed callback
     */
    override fun onNotificationRemoved(removedNotification: StatusBarNotification) {
        Log.i(
            TAG,
            "-------- onNotificationRemoved() :" + "ID :" + removedNotification.id + "\t" + removedNotification.notification.tickerText + "\t" + removedNotification.packageName
        )
        sendResultOnUI("onNotificationRemoved: " + removedNotification.packageName + "\n")
    }


    internal inner class CommandFromUIReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {
            if (intent.getStringExtra(COMMAND_KEY) == CLEAR_NOTIFICATIONS)
                 // remove Notns
                cancelAllNotifications()
            else if (intent.getStringExtra(COMMAND_KEY) == GET_ACTIVE_NOTIFICATIONS)
                // Read Notns
                fetchCurrentNotifications()
        }
    }


    /**
     * Fetch list of Active Notns
     */
    private fun fetchCurrentNotifications() {
        sendResultOnUI("===== Notification List START ====")

        val activeNotnCount = this@CustomNotificationListenerService.activeNotifications.size

        if (activeNotnCount > 0) {
            for (count in 0..activeNotnCount) {
                val sbn = this@CustomNotificationListenerService.activeNotifications[count]
                sendResultOnUI("#" + count.toString() + " Package: " + sbn.packageName + "\n")
            }
        } else {
            sendResultOnUI("No active Notn found")
        }

        sendResultOnUI("===== Notification List END====")
    }


    // sendMessage success result on UI
    private fun sendResultOnUI(result: String?) {
        val resultIntent = Intent(UPDATE_UI_ACTION)
        resultIntent.putExtra(RESULT_KEY, Activity.RESULT_OK)
        resultIntent.putExtra(RESULT_VALUE, result)
        LocalBroadcastManager.getInstance(this).sendBroadcast(resultIntent)
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(commandFromUIReceiver)
    }

    companion object {


        const val TAG = "NotificationListener"

        //Update UI action
        const val UPDATE_UI_ACTION =   "ACTION_UPDATE_UI"
        const val READ_COMMAND_ACTION = "ACTION_READ_COMMAND"


        // Bundle Key Value Pair
        const val RESULT_KEY = "readResultKey"
        const val RESULT_VALUE = "readResultValue"


        //Actions sent from UI
        const val COMMAND_KEY = "READ_COMMAND"
        const val CLEAR_NOTIFICATIONS = "clearall"
        const val GET_ACTIVE_NOTIFICATIONS = "list"


    }
}

Complete Project

https://github.com/hiteshsahu/Android-Notification-Demo

Leave a Reply

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