mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-09-02 15:15:09 +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:
committed by
Albert Vaca
parent
05d728b846
commit
6d216b2766
@@ -24,6 +24,8 @@ import android.annotation.TargetApi;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.RemoteInput;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@@ -33,6 +35,7 @@ import android.provider.Settings;
|
|||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
||||||
import org.kde.kdeconnect.Helpers.AppsHelper;
|
import org.kde.kdeconnect.Helpers.AppsHelper;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
@@ -43,16 +46,24 @@ import org.kde.kdeconnect_tp.R;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
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)
|
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||||
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
|
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
|
||||||
|
|
||||||
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
|
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_REQUEST = "kdeconnect.notification.request";
|
||||||
|
public final static String PACKAGE_TYPE_NOTIFICATION_REPLY = "kdeconnect.notification.reply";
|
||||||
|
|
||||||
|
|
||||||
private boolean sendIcons = true;
|
private boolean sendIcons = true;
|
||||||
|
|
||||||
|
private Map<String, RepliableNotification> pendingIntents;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
@@ -86,6 +97,8 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
|
pendingIntents = new HashMap<String, RepliableNotification>();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
if (hasPermission()) {
|
if (hasPermission()) {
|
||||||
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
|
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
|
||||||
@@ -208,6 +221,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RepliableNotification rn = extractRepliableNotification(statusBarNotification);
|
||||||
|
if(rn.pendingIntent != null) {
|
||||||
|
np.set("requestReplyId", rn.id);
|
||||||
|
pendingIntents.put(rn.id, rn);
|
||||||
|
}
|
||||||
|
|
||||||
np.set("id", key);
|
np.set("id", key);
|
||||||
np.set("appName", appName == null? packageName : appName);
|
np.set("appName", appName == null? packageName : appName);
|
||||||
np.set("isClearable", statusBarNotification.isClearable());
|
np.set("isClearable", statusBarNotification.isClearable());
|
||||||
@@ -223,6 +242,52 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
device.sendPackage(np);
|
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) {
|
private String getNotificationTitle(Notification notification) {
|
||||||
final String TITLE_KEY = "android.title";
|
final String TITLE_KEY = "android.title";
|
||||||
final String TEXT_KEY = "android.text";
|
final String TEXT_KEY = "android.text";
|
||||||
@@ -245,6 +310,37 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
return title;
|
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) {
|
private String getNotificationText(Notification notification) {
|
||||||
final String TEXT_KEY = "android.text";
|
final String TEXT_KEY = "android.text";
|
||||||
String text = "";
|
String 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;
|
return true;
|
||||||
@@ -406,7 +506,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getSupportedPackageTypes() {
|
public String[] getSupportedPackageTypes() {
|
||||||
return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST};
|
return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST,PACKAGE_TYPE_NOTIFICATION_REPLY};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -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;
|
||||||
|
}
|
Reference in New Issue
Block a user