NotificationListenerService and a Whatsapp extension for Dashclock
Android 4.3 (API 18) introduced NotificationListenerService.
In this post I'll talk how to use this API, and I'll show how an app can observe the stream of notifications with the user's permission.
It is very easy to use a NotificationListenerService.
Now we can use this service to observe the stream of notifications, and I'll show how to use it in a Dashclock Extension.
About 6 months ago I wrote how to use the Accessibility Service to listen the notifications and to use it with a Whatsapp extension for Dashclock.
Now only for Android 4.3 (!) we can improve this extension and switch from Accessibility Service to the new service as Android Team suggests.
First of all, we have to complete our NotificationListenerService.
In both cases we have to test the package name of the StatusBarNotification
We can use
The method onNotificationRemoved is very useful.With Accessibility Service we don't know when the notification is read by user. Now we can know when it is removed (either because the user dismissed it or the originating app withdrew it).
We can find the notification's text with sbn.getNotification().tickerText;
Here you can find the logcat of a new whatsapp notification:
I used a MessageManager to connect accessibility service with DashClockExtension.
Below our dashclock extension:
You can get code from GitHub:
In this post I'll talk how to use this API, and I'll show how an app can observe the stream of notifications with the user's permission.
It is very easy to use a NotificationListenerService.
- First of all you have to extend NotificationListenerService and implement onNotificationPosted() and onNotificationRemoved() methods
public class WhtsNotificationListener extends NotificationListenerService { @Override public void onNotificationPosted(StatusBarNotification sbn) { //.............. } @Override public void onNotificationRemoved(StatusBarNotification sbn) { //.............. } }
- Then you must declare the service in your manifest file with the BIND_NOTIFICATION_LISTENER_SERVICE permission and include an intent filter with the SERVICE_INTERFACE action
- Finally user must enable your service. Without this authorization it doesn't work!
You can find it in "Settings" -> "Security" -> "Notification access".
It could be a good idea to use a Intent in PreferenceScreen to help user to find this menu to enable/disable this permission.
You can use android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS
A little note about it.In Settings there is a String ACTION_NOTIFICATION_LISTENER_SETTINGS that references this action but it is @hide (little bug in 4.3).
Now we can use this service to observe the stream of notifications, and I'll show how to use it in a Dashclock Extension.
About 6 months ago I wrote how to use the Accessibility Service to listen the notifications and to use it with a Whatsapp extension for Dashclock.
Now only for Android 4.3 (!) we can improve this extension and switch from Accessibility Service to the new service as Android Team suggests.
First of all, we have to complete our NotificationListenerService.
public class WhtsNotificationListener extends NotificationListenerService { private static final String TAG = LogUtils.makeLogTag(WhtsNotificationListener.class); @Override public void onCreate() { super.onCreate(); LOGD(TAG,"Notification Listener created!"); MessageManager mManager=MessageManager.getInstance(this); } public void addNotification(StatusBarNotification sbn,boolean updateDash) { if (sbn==null) return; if (sbn!=null && sbn.getPackageName().equalsIgnoreCase("com.whatsapp")){ MessageManager mManager=MessageManager.getInstance(this); if (mManager!=null){ mManager.addNotification(sbn.getNotification(), sbn.getPostTime(),updateDash); } } } @Override public void onNotificationPosted(StatusBarNotification sbn) { LOGD(TAG,"Notification Posted:\n"); LOGD(TAG,"StatusBarNotification="+ sbn.toString()); if (sbn.getNotification()!=null) LOGD(TAG,"Notification="+sbn.getNotification().toString()); addNotification(sbn,true); } @Override public void onNotificationRemoved(StatusBarNotification sbn) { LOGD(TAG,"Notification Removed:\n"); if (sbn!=null && sbn.getPackageName().equalsIgnoreCase("com.whatsapp")){ MessageManager mManager=MessageManager.getInstance(this); if (mManager!=null){ mManager.clearAll(); } } } }We use the method onNotificationPosted(StatusBarNotification sbn) to add a message in dashclock and the method onNotificationRemoved(StatusBarNotification sbn) to remove the message in dashclock.
In both cases we have to test the package name of the StatusBarNotification
We can use
sbn.getPackageName().equalsIgnoreCase("com.whatsapp"))
.The method onNotificationRemoved is very useful.With Accessibility Service we don't know when the notification is read by user. Now we can know when it is removed (either because the user dismissed it or the originating app withdrew it).
We can find the notification's text with sbn.getNotification().tickerText;
Here you can find the logcat of a new whatsapp notification:
08-08 23:12:27.242: D/dashclock_WhtsNotifica(1041): Notification Posted: 08-08 23:12:27.242: D/dashclock_WhtsNotifica(1041): StatusBarNotification= StatusBarNotification(pkg=com.whatsapp user=UserHandle{0} id=1 tag=null score=0: Notification(pri=0 contentView=com.whatsapp/0x1090082 vibrate=null sound=null defaults=0x0 flags=0x0 kind=[null])) 08-08 23:12:27.242: D/MessageManager(1041): tickerText=Message from Mr.Rossi
I used a MessageManager to connect accessibility service with DashClockExtension.
public class MessageManager { private final static String TAG = "MessageManager"; private WhtsExtension mDashExtension; private WhtsNotificationListener mNotificationListener; private static MessageManager sInstance; //---------------------------------------------------------------------------- public static MessageManager getInstance(WhtsExtension context) { if (sInstance == null) sInstance = new MessageManager(); if (sInstance.mDashExtension==null) sInstance.mDashExtension=context; return sInstance; } public static MessageManager getInstance(WhtsNotificationListener listener) { if (sInstance == null) sInstance = new MessageManager(); if (sInstance.mNotificationListener==null) sInstance.mNotificationListener=listener; return sInstance; } private MessageManager() { mCount = 0; mMsgs = new ArrayListThis class stores information about messages (count and text).(); } public void setListener(WhtsNotificationListener notificationListener) { this.mNotificationListener=notificationListener; } //---------------------------------------------------------------------------- private ArrayList mMsgs; private int mCount; public void addNotification(Notification notification, long postTime,boolean updateDash) { if (notification==null) return; String rawText=""+notification.tickerText; LOGD(TAG,"rawrext="+rawText); MessageWht msg=new MessageWht(); msg.setText(rawText); mCount++; mMsgs.add(msg); if (updateDash && mDashExtension!=null) mDashExtension.onUpdateData(WhtsExtension.UPDATE_REASON_CONTENT_CHANGED); } public void clearAll() { mCount=0; mMsgs=new ArrayList (); if (mDashExtension!=null) mDashExtension.onUpdateData(WhtsExtension.UPDATE_REASON_CONTENT_CHANGED); } }
Below our dashclock extension:
public class WhtsExtension extends DashClockExtension { private static final String TAG = LogUtils.makeLogTag(WhtsExtension.class); private MessageManager mManager; private String dashTitle; private String dashSubtitle; private String dashStatus; private int dashIcon; private boolean dashVisible = true; // ---------------------------------------------------- @Override protected void onInitialize(boolean isReconnect) { super.onInitialize(isReconnect); LOGD(TAG, "onInitialize " + isReconnect); setUpdateWhenScreenOn(true); if (!isReconnect) { mManager = MessageManager.getInstance(this); } } @Override protected void onUpdateData(int reason) { LOGD(TAG, "onUpdate " + reason); readData(); // publish publishUpdateExtensionData(); } // ------------------------------------------------------------ private void publishUpdateExtensionData() { // Publish the extension data update. ExtensionData extData = new ExtensionData(); if (dashVisible) { extData.visible(true).icon(dashIcon).status(dashStatus) .expandedTitle(dashTitle).expandedBody(dashSubtitle); extData.clickIntent(null); } else { extData.visible(false); } publishUpdate(extData); } //-------------------------------------------------------------- private void readData() { if (mManager == null) return; dashIcon=R.drawable.ic_extension_whts; int mCount = mManager.getmCount(); LOGD(TAG,"count="+mCount); if (mCount > 0) { dashVisible = true; dashStatus = "" + mCount; Resources res = getResources(); String book = res.getQuantityString(R.plurals.notifications, mCount, mCount); dashTitle = book; ArrayListThat's all.... it works.msgs = mManager.getmMsgs(); if (msgs != null) { StringBuilder sb = new StringBuilder(); String and = ""; for (MessageWht msg:msgs) { sb.append(and); sb.append(msg.getText()); and="\n"; } dashSubtitle=sb.toString(); } } else { dashVisible = false; } } }
You can get code from GitHub:
This comment has been removed by the author.
ReplyDelete