cordova-plugin-reply-to-not.../src/android/NotificationCommands.java

326 lines
11 KiB
Java

package com.umycode.replyToNotification;
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;
import java.util.Set;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.app.RemoteInput;
import android.app.Notification;
import android.os.Build;
import android.text.TextUtils;
import com.umycode.replyToNotification.Action;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import android.os.Parcel;
import java.util.Arrays;
import android.util.Base64;
import java.util.List;
import java.util.LinkedList;
import java.io.PrintWriter;
import java.io.StringWriter;
import android.app.PendingIntent;
public class NotificationCommands extends CordovaPlugin {
protected static LinkedList<LinkedList<Action>> intent_stacks = new LinkedList<LinkedList<Action>>();
protected static List<String> intent_rooms = new LinkedList<String>();
private static final String TAG = "NotificationCommands";
private static final String LISTEN = "listen";
private static final String[] REPLY_KEYWORDS = {"reply", "android.intent.extra.text","message_key"};
private static final CharSequence REPLY_KEYWORD = "reply";
private static final CharSequence INPUT_KEYWORD = "input";
// note that webView.isPaused() is not Xwalk compatible, so tracking it poor-man style
private boolean isPaused;
private static CallbackContext listener;
private static Context current_context;
@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (!NotificationManagerCompat.getEnabledListenerPackages (this.cordova.getActivity().getApplicationContext()).contains(this.cordova.getActivity().getApplicationContext().getPackageName())) {
Toast.makeText(this.cordova.getActivity().getApplicationContext(), "Please Enable Notification Access", Toast.LENGTH_LONG).show();
//service is not enabled try to enabled by calling...
this.cordova.getActivity().getApplicationContext().startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
callbackContext.error(TAG+". Need permissions to be granted.");
System.exit(0);
return false;
} else {
Log.i(TAG, "Received action " + action);
current_context = this.cordova.getActivity().getApplicationContext();
if (LISTEN.equals(action)) {
setListener(callbackContext);
return true;
}else if (action.equals("replytonotification")) {
if(args.length() != 0){
String room_name = args.getJSONObject(0).getString("room_name");
String message = args.getJSONObject(0).getString("message");
// Do we have an existing intent for this
int indexOfRoomName = intent_rooms.indexOf(room_name);
if (indexOfRoomName > -1) {
try{
replyToRoom(callbackContext,indexOfRoomName, message);
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
} catch (Exception e){
Log.e(TAG, "Unable to send notification "+ e);
PluginResult result = new PluginResult(PluginResult.Status.OK, "Unable to send notification "+ e);
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
}
}else{
Log.e(TAG, "Unable to send notification: Room has not been queued yet");
PluginResult result = new PluginResult(PluginResult.Status.OK, "Unable to send notification: Room has not been queued yet");
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
}
}
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);
// Store the notification for later
Action reply_result = getQuickReplyAction(n.getNotification(),json.getString("package"));
if(reply_result!=null && json.getString("package").equals("org.vom8x8.sipua") ){
// Store the intent
if (json.has("conversationTitle")) {
int indexOfRoomName = intent_rooms.indexOf(json.getString("conversationTitle"));
if (indexOfRoomName > -1) {
addIntentToList(indexOfRoomName,reply_result);
}else{
intent_rooms.add(json.getString("conversationTitle"));
addIntentToList(-1,reply_result);
}
}else{
int indexOfRoomName = intent_rooms.indexOf(json.getString("title"));
if (indexOfRoomName > -1) {
addIntentToList(indexOfRoomName,reply_result);
}else{
intent_rooms.add(json.getString("title"));
addIntentToList(-1,reply_result);
}
}
}
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("package", n.getPackageName());
Set<String> keys = extras.keySet();
/* Iterate over all keys of the bundle to give back all the information available */
for (String key : keys) {
try {
String printKey = key;
/* If key has a prefix android., this will be removed. */
if(printKey.indexOf("android.")==0 && printKey.length()>8){
printKey = printKey.substring(8,key.length());
}
// json.put(key, bundle.get(key)); see edit below
json.put(printKey, JSONObject.wrap(extras.get(key)));
} catch(JSONException e) {
Log.d(TAG,e.getMessage());
}
}
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 "";
}
}
public static Action getQuickReplyAction(Notification n, String packageName) {
NotificationCompat.Action action = null;
if(Build.VERSION.SDK_INT >= 24)
action = getQuickReplyAction(n);
if(action == null)
action = getWearReplyAction(n);
if(action == null)
return null;
return new Action(action, packageName, true);
}
private static NotificationCompat.Action getQuickReplyAction(Notification n) {
for(int i = 0; i < NotificationCompat.getActionCount(n); i++) {
NotificationCompat.Action action = NotificationCompat.getAction(n, i);
if(action.getRemoteInputs() != null) {
for (int x = 0; x < action.getRemoteInputs().length; x++) {
RemoteInput remoteInput = action.getRemoteInputs()[x];
if (isKnownReplyKey(remoteInput.getResultKey()))
return action;
}
}
}
return null;
}
private static NotificationCompat.Action getWearReplyAction(Notification n) {
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(n);
for (NotificationCompat.Action action : wearableExtender.getActions()) {
if(action.getRemoteInputs() != null) {
for (int x = 0; x < action.getRemoteInputs().length; x++) {
RemoteInput remoteInput = action.getRemoteInputs()[x];
if (isKnownReplyKey(remoteInput.getResultKey()))
return action;
else if (remoteInput.getResultKey().toLowerCase().contains(INPUT_KEYWORD))
return action;
}
}
}
return null;
}
private static boolean isKnownReplyKey(String resultKey) {
if(TextUtils.isEmpty(resultKey))
return false;
resultKey = resultKey.toLowerCase();
for(String keyword : REPLY_KEYWORDS)
if(resultKey.contains(keyword))
return true;
return false;
}
private static void addIntentToList(int indexOfRoomName, Action reply_result){
if(indexOfRoomName >= 0){
// we are adding to an existing stack
intent_stacks.get(indexOfRoomName).add(reply_result);
// is it too long now?
if(intent_stacks.get(indexOfRoomName).size() > 10){
Log.i(TAG, "Removing intent from stack ");
intent_stacks.get(indexOfRoomName).removeFirst();
}
}else{
intent_stacks.add(new LinkedList<>());
intent_stacks.get(intent_stacks.size()-1).add(reply_result);
// is it too long now?
if(intent_stacks.get(intent_stacks.size()-1).size() > 10){
Log.i(TAG, "Removing intent from stack ");
intent_stacks.get(intent_stacks.size()-1).removeFirst();
}
}
}
public static void replyToRoom(CallbackContext callbackContext,int indexOfRoomName, String message) throws PendingIntent.CanceledException{
try{
if(intent_stacks.get(indexOfRoomName).size()>0){
Action reply_result = intent_stacks.get(indexOfRoomName).get(0);
reply_result.sendReply(current_context, message);
}else{
PluginResult result = new PluginResult(PluginResult.Status.OK, "Unable to send notification. No stored intents.");
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
}
} catch (Exception e){
Log.e(TAG, "Unable to send notification "+ e);
// Can we try the next one?
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
if(exceptionAsString.contains("CanceledException")){
Log.i(TAG, "Removing intent from stack ");
intent_stacks.get(indexOfRoomName).removeFirst();
replyToRoom(callbackContext,indexOfRoomName, message);
}else{
PluginResult result = new PluginResult(PluginResult.Status.OK, "Unable to send notification "+ e);
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
}
}
}
}