2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-30 21:55:10 +00:00

reply to notifications

Summary: These changes allow replying to any notifications that offer quick reply text fields (like WhatsApp). Does work for WhatsApp. Does not work for Telegram.

Reviewers: #kde_connect, albertvaka

Reviewed By: #kde_connect, albertvaka

Subscribers: albertvaka

Maniphest Tasks: T4674

Differential Revision: https://phabricator.kde.org/D5991
This commit is contained in:
Julian Wolff
2017-05-31 00:08:38 +02:00
committed by Albert Vaca
parent 05d728b846
commit 6d216b2766
2 changed files with 117 additions and 1 deletions

View File

@@ -24,6 +24,8 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -33,6 +35,7 @@ import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
@@ -43,15 +46,23 @@ import org.kde.kdeconnect_tp.R;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
public final static String PACKAGE_TYPE_NOTIFICATION_REQUEST = "kdeconnect.notification.request";
public final static String PACKAGE_TYPE_NOTIFICATION_REPLY = "kdeconnect.notification.reply";
private boolean sendIcons = true;
private Map<String, RepliableNotification> pendingIntents;
@Override
@@ -86,6 +97,8 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public boolean onCreate() {
pendingIntents = new HashMap<String, RepliableNotification>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if (hasPermission()) {
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@@ -207,6 +220,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
Log.e("NotificationsPlugin", "Error retrieving icon");
}
}
RepliableNotification rn = extractRepliableNotification(statusBarNotification);
if(rn.pendingIntent != null) {
np.set("requestReplyId", rn.id);
pendingIntents.put(rn.id, rn);
}
np.set("id", key);
np.set("appName", appName == null? packageName : appName);
@@ -223,6 +242,52 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
device.sendPackage(np);
}
void replyToNotification(String id, String message){
if(pendingIntents.isEmpty() || !pendingIntents.containsKey(id)){
Log.e("NotificationsPlugin", "No such notification");
return;
}
RepliableNotification repliableNotification = pendingIntents.get(id);
if(repliableNotification == null) {
Log.e("NotificationsPlugin", "No such notification");
return;
}
RemoteInput[] remoteInputs = new RemoteInput[repliableNotification.remoteInputs.size()];
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bundle localBundle = new Bundle();
int i = 0;
for(RemoteInput remoteIn : repliableNotification.remoteInputs){
getDetailsOfNotification(remoteIn);
remoteInputs[i] = remoteIn;
localBundle.putCharSequence(remoteInputs[i].getResultKey(), message);
i++;
}
RemoteInput.addResultsToIntent(remoteInputs, localIntent, localBundle);
try {
repliableNotification.pendingIntent.send(context, 0, localIntent);
} catch (PendingIntent.CanceledException e) {
Log.e("NotificationPlugin", "replyToNotification error: " + e.getMessage());
}
pendingIntents.remove(id);
}
private void getDetailsOfNotification(RemoteInput remoteInput) {
//Some more details of RemoteInput... no idea what for but maybe it will be useful at some point
String resultKey = remoteInput.getResultKey();
String label = remoteInput.getLabel().toString();
Boolean canFreeForm = remoteInput.getAllowFreeFormInput();
if(remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
String[] possibleChoices = new String[remoteInput.getChoices().length];
for(int i = 0; i < remoteInput.getChoices().length; i++){
possibleChoices[i] = remoteInput.getChoices()[i].toString();
}
}
}
private String getNotificationTitle(Notification notification) {
final String TITLE_KEY = "android.title";
final String TEXT_KEY = "android.text";
@@ -244,6 +309,37 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
return title;
}
private RepliableNotification extractRepliableNotification(StatusBarNotification statusBarNotification) {
RepliableNotification repliableNotification = new RepliableNotification();
if(statusBarNotification != null) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
try {
Boolean reply = false;
//works for WhatsApp, but not for Telegram
for(Notification.Action act : statusBarNotification.getNotification().actions) {
if(act != null && act.getRemoteInputs() != null) {
repliableNotification.remoteInputs.addAll(Arrays.asList(act.getRemoteInputs()));
repliableNotification.pendingIntent = act.actionIntent;
reply = true;
break;
}
}
repliableNotification.packageName = statusBarNotification.getPackageName();
repliableNotification.tag = statusBarNotification.getTag();//TODO find how to pass Tag with sending PendingIntent, might fix Hangout problem
} catch(Exception e) {
Log.w("NotificationPlugin","problem extracting notification wear for " + statusBarNotification.getNotification().tickerText);
e.printStackTrace();
}
}
}
return repliableNotification;
}
private String getNotificationText(Notification notification) {
final String TEXT_KEY = "android.text";
@@ -363,6 +459,10 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
}
});
} else if (np.has("requestReplyId") && np.has("message")) {
replyToNotification(np.getString("requestReplyId"), np.getString("message"));
}
return true;
@@ -406,7 +506,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST};
return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST,PACKAGE_TYPE_NOTIFICATION_REPLY};
}
@Override

View File

@@ -0,0 +1,16 @@
package org.kde.kdeconnect.Plugins.NotificationsPlugin;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.UUID;
public class RepliableNotification {
public String id = UUID.randomUUID().toString();
public PendingIntent pendingIntent;
public ArrayList<android.app.RemoteInput> remoteInputs = new ArrayList<>();
public String packageName;
public String tag;
}