2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-05 16:45:08 +00:00

Merge branch 'master' into 1.x

# Conflicts:
#	AndroidManifest.xml
#	res/values-es/strings.xml
#	res/values-fr/strings.xml
#	res/values-ru/strings.xml
#	res/values-sr/strings.xml
#	src/org/kde/kdeconnect/Helpers/NetworkHelper.java
#	src/org/kde/kdeconnect/Plugins/SftpPlugin/SftpPlugin.java
#	src/org/kde/kdeconnect/Plugins/TelepathyPlugin/TelepathyPlugin.java
#	src/org/kde/kdeconnect/Plugins/TelephonyPlugin/TelephonyPlugin.java
This commit is contained in:
Albert Vaca
2017-07-14 01:12:13 +02:00
54 changed files with 1830 additions and 435 deletions

View File

@@ -24,14 +24,18 @@ 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;
import android.os.Build;
import android.os.Bundle;
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;
@@ -39,15 +43,27 @@ import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.UserInterface.SettingsActivity;
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;
/*
private boolean sendIcons = false;
*/
@Override
public String getDisplayName() {
@@ -81,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() {
@@ -159,6 +177,8 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
String packageName = statusBarNotification.getPackageName();
String appName = AppsHelper.appNameLookup(context, packageName);
if ("com.facebook.orca".equals(packageName) &&
(statusBarNotification.getId() == 10012) &&
"Messenger".equals(appName) &&
@@ -182,28 +202,41 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
np.set("silent", true);
np.set("requestAnswer", true); //For compatibility with old desktop versions of KDE Connect that don't support "silent"
}
/*
if (sendIcons) {
try {
Drawable drawableAppIcon = AppsHelper.appIconLookup(context, packageName);
Bitmap appIcon = ImagesHelper.drawableToBitmap(drawableAppIcon);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
if (appIcon.getWidth() > 128) {
appIcon = Bitmap.createScaledBitmap(appIcon, 96, 96, true);
Bitmap appIcon = notification.largeIcon;
if (appIcon != null) {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
if (appIcon.getWidth() > 128) {
appIcon = Bitmap.createScaledBitmap(appIcon, 96, 96, true);
}
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
byte[] bitmapData = outStream.toByteArray();
np.setPayload(bitmapData);
np.set("payloadHash", getChecksum(bitmapData));
}
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
byte[] bitmapData = outStream.toByteArray();
np.setPayload(bitmapData);
} catch (Exception e) {
} catch(Exception e){
e.printStackTrace();
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);
np.set("isClearable", statusBarNotification.isClearable());
np.set("ticker", getTickerText(notification));
np.set("title", getNotificationTitle(notification));
np.set("text", getNotificationText(notification));
np.set("time", Long.toString(statusBarNotification.getPostTime()));
if (requestAnswer) {
np.set("requestAnswer", true);
@@ -213,6 +246,127 @@ 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";
String title = "";
if(notification != null) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
Bundle extras = notification.extras;
title = extras.getCharSequence(TITLE_KEY).toString();
} catch(Exception e) {
Log.w("NotificationPlugin","problem parsing notification extras for " + notification.tickerText);
e.printStackTrace();
}
}
}
//TODO Add compat for under Kitkat devices
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";
String text = "";
if(notification != null) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
Bundle extras = notification.extras;
Object extraTextExtra = extras.get(TEXT_KEY);
if (extraTextExtra != null) text = extraTextExtra.toString();
} catch(Exception e) {
Log.w("NotificationPlugin","problem parsing notification extras for " + notification.tickerText);
e.printStackTrace();
}
}
}
//TODO Add compat for under Kitkat devices
return text;
}
/**
* Returns the ticker text of the notification.
@@ -234,7 +388,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
if (extraTextExtra != null) extraText = extraTextExtra.toString();
if (extraTitle != null && extraText != null && !extraText.isEmpty()) {
ticker = extraTitle + " " + extraText;
ticker = extraTitle + ": " + extraText;
} else if (extraTitle != null) {
ticker = extraTitle;
} else if (extraText != null) {
@@ -309,6 +463,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;
@@ -352,7 +510,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
@@ -402,4 +560,29 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
}
return result;
}
public String getChecksum(byte[] data){
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(data);
return bytesToHex(md.digest());
} catch (NoSuchAlgorithmException e) {
Log.e("KDEConenct", "Error while generating checksum", e);
}
return null;
}
public static String bytesToHex(byte[] bytes) {
char[] hexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars).toLowerCase();
}
}