First commit

This commit is contained in:
Javier Rengel 2015-09-13 12:34:59 +01:00
commit 29f9e8ddab
9 changed files with 381 additions and 0 deletions

3
CHANGES.txt Normal file
View File

@ -0,0 +1,3 @@
= 0.0.1 =
In progress

13
LICENSE.txt Normal file
View File

@ -0,0 +1,13 @@
Copyright 2015 Javier Rengel (Coconauts)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

72
README.md Normal file
View File

@ -0,0 +1,72 @@
# NotificationListenerService plugin for Cordova
This is an implementation of the
[NotificationListenerService in Android](https://developer.android.com/reference/android/service/notification/NotificationListenerService.html)
for Cordova.
A service that receives calls from the system when new notifications are posted or removed, or their ranking changed.
Note: This plugin doesn't work for IOS or Windows Phone, feel free to create a pull request if you want to add that functionality to this project.
## How to install
cordova plugin add https://github.com/coconauts/NotificationListener-cordova
## Enable notification listener service
This service requires an special permission that must be enabled from settings on Android (Settings > Notifications > Notification access)
![](/settings.jpg)
Note: The app requires the following permission in your Manifest file on Android, which will be added automatically:
android.permission.BIND_NOTIFICATION_LISTENER_SERVICE
## How to use
On Cordova initialization, add the callback for your notification-listener.
Then everytime you get a notification in your phone that callback in JS will be triggered with the notification data.
```
var app = {
initialize: function() {
console.log("Initializing app");
this.bindEvents();
},
bindEvents: function() {
console.log("Binding events");
document.addEventListener('deviceready', this.onDeviceReady, false);
},
onDeviceReady: function() {
console.log("Device ready");
notificationListener.listen(function(n){
console.log("Received notification " + JSON.stringify(n) );
}, function(e){
console.log("Notification Error " + e);
});
}
};
app.initialize();
```
For a full example, please see our [WatchDuino2 repository](https://github.com/coconauts/watchduino2-companion-app)
## Sample output
```
Received notification
{
"title":"Chuck Norris",
"package":"com.google.android.talk",
"text":"Hello world",
"textLines":""
}
```
## Notification response format
The notification response received by Javascript is a simplified object from the
[StatusBarNotification class](https://developer.android.com/reference/android/service/notification/StatusBarNotification.html)
in Android.
Feel free to update the [notification parser](#TODO) inside this plugin if needed.

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"version": "0.0.1",
"name": "net.coconauts.notification-listener",
"cordova_name": "Notification-listener",
"description": "Notification listener Plugin for Android",
"license": "Apache 2.0",
"repo": "https://github.com/coconauts/NotificationListener-cordova",
"issue": "https://github.com/coconauts/NotificationListener-cordova/issues",
"keywords": [
"notification",
"listener",
"android"
],
"platforms": [
"android"
],
"engines": [
{
"name": "cordova",
"version": ">=3.1.0"
}
]
}

48
plugin.xml Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<plugin
xmlns="http://www.phonegap.com/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="net.coconauts.notification-listener"
version="0.0.1">
<name>Notification-listener</name>
<description>Notification listener Plugin for Android</description>
<license>Apache 2.0</license>
<keywords>notification, listener, android</keywords>
<repo>https://github.com/coconauts/NotificationListener-cordova</repo>
<issue>https://github.com/coconauts/NotificationListener-cordova/issues</issue>
<engines>
<engine name="cordova" version=">=3.1.0" />
</engines>
<js-module src="www/notification-listener.js" name="NotificationListener">
<clobbers target="notificationListener" />
</js-module>
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="NotificationListener">
<param name="android-package" value="net.coconauts.notificationListener.NotificationCommands"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
<service android:name="net.coconauts.notificationListener.NotificationService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" ></action>
</intent-filter>
</service>
</config-file>
<source-file src="src/android/NotificationService.java" target-dir="src/net/coconauts/notificationListener"/>
<source-file src="src/android/NotificationCommands.java" target-dir="src/net/coconauts/notificationListener"/>
</platform>
</plugin>

BIN
settings.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,110 @@
package net.coconauts.notificationListener;
import android.view.Gravity;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import org.apache.cordova.PluginResult;
import android.service.notification.StatusBarNotification;
import android.os.Bundle;
public class NotificationCommands extends CordovaPlugin {
private static final String TAG = "NotificationCommands";
private static final String LISTEN = "listen";
// note that webView.isPaused() is not Xwalk compatible, so tracking it poor-man style
private boolean isPaused;
private static CallbackContext listener;
@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
Log.i(TAG, "Received action " + action);
if (LISTEN.equals(action)) {
setListener(callbackContext);
return true;
} else {
callbackContext.error(TAG+". " + action + " is not a supported function.");
return false;
}
}
@Override
public void onPause(boolean multitasking) {
this.isPaused = true;
}
@Override
public void onResume(boolean multitasking) {
this.isPaused = false;
}
public void setListener(CallbackContext callbackContext) {
Log.i("Notification", "Attaching callback context listener " + callbackContext);
listener = callbackContext;
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
}
public static void notifyListener(StatusBarNotification n){
if (listener == null) {
Log.e(TAG, "Must define listener first. Call notificationListener.listen(success,error) first");
return;
}
try {
JSONObject json = parse(n);
PluginResult result = new PluginResult(PluginResult.Status.OK, json);
Log.i(TAG, "Sending notification to listener " + json.toString());
result.setKeepCallback(true);
listener.sendPluginResult(result);
} catch (Exception e){
Log.e(TAG, "Unable to send notification "+ e);
listener.error(TAG+". Unable to send message: "+e.getMessage());
}
}
private static JSONObject parse(StatusBarNotification n) throws JSONException{
JSONObject json = new JSONObject();
Bundle extras = n.getNotification().extras;
json.put("title", getExtra(extras, "android.title"));
json.put("package", n.getPackageName());
json.put("text", getExtra(extras,"android.text"));
json.put("textLines", getExtraLines(extras, "android.textLines"));
return json;
}
private static String getExtraLines(Bundle extras, String extra){
try {
CharSequence[] lines = extras.getCharSequenceArray(extra);
return lines[lines.length-1].toString();
} catch( Exception e){
Log.d(TAG, "Unable to get extra lines " + extra);
return "";
}
}
private static String getExtra(Bundle extras, String extra){
try {
return extras.get(extra).toString();
} catch( Exception e){
return "";
}
}
}

View File

@ -0,0 +1,88 @@
package net.coconauts.notificationListener;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class NotificationService extends NotificationListenerService {
//http://developer.android.com/reference/android/service/notification/NotificationListenerService.html
private static final String TAG = NotificationService.class.getSimpleName();
//TODO store this in config
private static final String IGNORE_PKG = "snapdragon,com.google.android.googlequicksearchbox";
private static int notificationId = 1;
private static List<StatusBarNotification> notifications ;
public static boolean enabled = false;
private static Context context ;
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
enabled = true;
notifications = new ArrayList<StatusBarNotification>();
context = this;
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
enabled = false;
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
//Do not send notifications from this app (can cause an infinite loop)
Log.d(TAG, "notification package name " + sbn.getPackageName());
String pk = sbn.getPackageName();
if (pk.equals("android") || ignorePkg(pk) || sbn.isOngoing()) Log.d(TAG, "Ignore notification from pkg " + pk);
else {
NotificationCommands.notifyListener(sbn);
addNotification(sbn);
}
}
private boolean ignorePkg(String pk){
for(String s: IGNORE_PKG.split(",")) if (pk.contains(s)) return true;
return false;
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
//debugNotification(sbn);
}
private void addNotification(StatusBarNotification msg){
notifications.add(msg);
}
public static void removeAll(){
try {
for (StatusBarNotification n : notifications) remove(n);
notifications.clear();
} catch (Exception e){
Log.e(TAG, "Unable to remove notifications",e);
}
}
private static void remove(StatusBarNotification n){
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager nMgr = (NotificationManager) context.getApplicationContext().getSystemService(ns);
int id = n.getId();
String tag = n.getTag();
Log.i("Cancelling notification ", tag + ", " + id);
nMgr.cancel(tag, id);
}
}

View File

@ -0,0 +1,24 @@
// (c) 2015 Javier Rengel (Coconauts)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
"use strict";
module.exports = {
// value must be an ArrayBuffer
listen: function (success, failure) {
console.log("Calling cordova listen method");
cordova.exec(success, failure, 'NotificationListener', 'listen', []);
}
};