diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationReceiver.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationReceiver.java --- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationReceiver.java +++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationReceiver.java @@ -27,46 +27,68 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.support.annotation.RequiresApi; +import android.util.Log; import java.util.ArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public class NotificationReceiver extends NotificationListenerService { + private boolean connected; + public interface NotificationListener { void onNotificationPosted(StatusBarNotification statusBarNotification); + void onNotificationRemoved(StatusBarNotification statusBarNotification); + + void onListenerConnected(NotificationReceiver service); } private final ArrayList listeners = new ArrayList<>(); public void addListener(NotificationListener listener) { listeners.add(listener); } + public void removeListener(NotificationListener listener) { listeners.remove(listener); } @Override public void onNotificationPosted(StatusBarNotification statusBarNotification) { //Log.e("NotificationReceiver.onNotificationPosted","listeners: " + listeners.size()); - for(NotificationListener listener : listeners) { + for (NotificationListener listener : listeners) { listener.onNotificationPosted(statusBarNotification); } } @Override public void onNotificationRemoved(StatusBarNotification statusBarNotification) { - for(NotificationListener listener : listeners) { + for (NotificationListener listener : listeners) { listener.onNotificationRemoved(statusBarNotification); } } + @Override + public void onListenerConnected() { + super.onListenerConnected(); + connected = true; + } + @Override + public void onListenerDisconnected() { + super.onListenerDisconnected(); + for (NotificationListener listener : listeners) { + listener.onListenerConnected(this); + } + connected = false; + } - + public boolean isConnected() { + return connected; + } //To use the service from the outer (name)space diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java --- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java @@ -36,7 +36,6 @@ import android.support.annotation.RequiresApi; import android.util.Log; - import org.kde.kdeconnect.Helpers.AppsHelper; import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.Plugins.Plugin; @@ -47,10 +46,8 @@ 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) @@ -60,13 +57,12 @@ 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 pendingIntents; + private boolean serviceReady; //For compat with API<21, because lollipop changed the way to cancel notifications - public static void cancelNotificationCompat(NotificationReceiver service, String compatKey) { + private static void cancelNotificationCompat(NotificationReceiver service, String compatKey) { + if (Build.VERSION.SDK_INT >= 21) { service.cancelNotification(compatKey); } else { @@ -90,7 +86,7 @@ } } - public static String getNotificationKeyCompat(StatusBarNotification statusBarNotification) { + private static String getNotificationKeyCompat(StatusBarNotification statusBarNotification) { String result; // first check if it's one of our remoteIds String tag = statusBarNotification.getTag(); @@ -108,7 +104,7 @@ return result; } - public static String bytesToHex(byte[] bytes) { + private static String bytesToHex(byte[] bytes) { char[] hexArray = "0123456789ABCDEF".toCharArray(); char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { @@ -151,27 +147,23 @@ @Override public boolean onCreate() { - pendingIntents = new HashMap(); - if (hasPermission()) { - NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { - @Override - public void onServiceStart(NotificationReceiver service) { - try { - service.addListener(NotificationsPlugin.this); - StatusBarNotification[] notifications = service.getActiveNotifications(); - for (StatusBarNotification notification : notifications) { - sendNotification(notification, true); - } - } catch (Exception e) { - Log.e("NotificationsPlugin", "Exception"); - e.printStackTrace(); - } + if (!hasPermission()) return false; + + pendingIntents = new HashMap<>(); + + NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { + @Override + public void onServiceStart(NotificationReceiver service) { + + service.addListener(NotificationsPlugin.this); + serviceReady = service.isConnected(); + if (serviceReady) { + sendCurrentNotifications(service); } - }); - } else { - return false; - } + } + }); + return true; } @@ -199,12 +191,18 @@ device.sendPackage(np); } + @Override + public void onListenerConnected(NotificationReceiver service) { + serviceReady = true; + sendCurrentNotifications(service); + } + @Override public void onNotificationPosted(StatusBarNotification statusBarNotification) { - sendNotification(statusBarNotification, false); + sendNotification(statusBarNotification); } - public void sendNotification(StatusBarNotification statusBarNotification, boolean requestAnswer) { + private void sendNotification(StatusBarNotification statusBarNotification) { Notification notification = statusBarNotification.getNotification(); AppDatabase appDatabase = new AppDatabase(context); @@ -250,26 +248,24 @@ np.set("requestAnswer", true); //For compatibility with old desktop versions of KDE Connect that don't support "silent" } - if (sendIcons) { - try { - Bitmap appIcon = notification.largeIcon; + try { + 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(); + 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.setPayload(bitmapData); - np.set("payloadHash", getChecksum(bitmapData)); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e("NotificationsPlugin", "Error retrieving icon"); + np.set("payloadHash", getChecksum(bitmapData)); } + } catch (Exception e) { + e.printStackTrace(); + Log.e("NotificationsPlugin", "Error retrieving icon"); } RepliableNotification rn = extractRepliableNotification(statusBarNotification); @@ -285,16 +281,12 @@ np.set("title", getNotificationTitle(notification)); np.set("text", getNotificationText(notification)); np.set("time", Long.toString(statusBarNotification.getPostTime())); - if (requestAnswer) { - np.set("requestAnswer", true); - np.set("silent", true); - } device.sendPackage(np); } @RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH) - void replyToNotification(String id, String message) { + private void replyToNotification(String id, String message) { if (pendingIntents.isEmpty() || !pendingIntents.containsKey(id)) { Log.e("NotificationsPlugin", "No such notification"); return; @@ -350,7 +342,7 @@ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { Bundle extras = notification.extras; - title = extras.getCharSequence(TITLE_KEY).toString(); + title = extras.getString(TITLE_KEY); } catch (Exception e) { Log.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText); e.printStackTrace(); @@ -432,7 +424,7 @@ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { Bundle extras = notification.extras; - String extraTitle = extras.getCharSequence(TITLE_KEY).toString(); + String extraTitle = extras.getString(TITLE_KEY); String extraText = null; Object extraTextExtra = extras.get(TEXT_KEY); if (extraTextExtra != null) extraText = extraTextExtra.toString(); @@ -458,50 +450,29 @@ return ticker; } - @Override - public boolean onPackageReceived(final NetworkPackage np) { -/* - if (np.getBoolean("sendIcons")) { - sendIcons = true; - } -*/ - if (np.getBoolean("request")) { + private void sendCurrentNotifications(NotificationReceiver service) { - NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { - private void sendCurrentNotifications(NotificationReceiver service) { - StatusBarNotification[] notifications = service.getActiveNotifications(); - for (StatusBarNotification notification : notifications) { - sendNotification(notification, true); - } - } + StatusBarNotification[] notifications = service.getActiveNotifications(); + for (StatusBarNotification notification : notifications) { + sendNotification(notification); + } + } + @Override + public boolean onPackageReceived(final NetworkPackage np) { - @Override - public void onServiceStart(final NotificationReceiver service) { - try { - //If service just started, this call will throw an exception because the answer is not ready yet + if (np.getBoolean("request")) { + if (serviceReady) { + NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { + @Override + public void onServiceStart(NotificationReceiver service) { sendCurrentNotifications(service); - } catch (Exception e) { - Log.e("onPackageReceived", "Error when answering 'request': Service failed to start. Retrying in 100ms..."); - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(100); - Log.e("onPackageReceived", "Error when answering 'request': Service failed to start. Retrying..."); - sendCurrentNotifications(service); - } catch (Exception e) { - Log.e("onPackageReceived", "Error when answering 'request': Service failed to start twice!"); - e.printStackTrace(); - } - } - }).start(); } - } - }); + }); - } else if (np.has("cancel")) { + } + } else if (np.has("cancel")) { NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { @Override public void onServiceStart(NotificationReceiver service) { @@ -512,7 +483,9 @@ } else if (np.has("requestReplyId") && np.has("message")) { - replyToNotification(np.getString("requestReplyId"), np.getString("message")); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + replyToNotification(np.getString("requestReplyId"), np.getString("message")); + } } @@ -551,7 +524,7 @@ return new String[]{PACKAGE_TYPE_NOTIFICATION}; } - public String getChecksum(byte[] data) { + private String getChecksum(byte[] data) { try { MessageDigest md = MessageDigest.getInstance("MD5");