diff --git a/src/org/kde/kdeconnect/Device.java b/src/org/kde/kdeconnect/Device.java --- a/src/org/kde/kdeconnect/Device.java +++ b/src/org/kde/kdeconnect/Device.java @@ -28,6 +28,7 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.drawable.Drawable; +import android.os.Build; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.content.ContextCompat; @@ -693,6 +694,12 @@ private synchronized boolean addPlugin(final String pluginKey) { Plugin existing = plugins.get(pluginKey); if (existing != null) { + + if (existing.getMinSdk() > Build.VERSION.SDK_INT) { + Log.i("KDE/addPlugin", "Min API level not fulfilled " + pluginKey); + return false; + } + //Log.w("KDE/addPlugin","plugin already present:" + pluginKey); if (existing.checkOptionalPermissions()) { Log.i("KDE/addPlugin", "Optional Permissions OK " + pluginKey); @@ -712,6 +719,11 @@ return false; } + if (plugin.getMinSdk() > Build.VERSION.SDK_INT) { + Log.i("KDE/addPlugin", "Min API level not fulfilled" + pluginKey); + return false; + } + boolean success; try { success = plugin.onCreate(); 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 @@ -23,47 +23,72 @@ import android.app.Service; import android.content.Context; import android.content.Intent; +import android.os.Build; 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(); + for (NotificationListener listener : listeners) { + listener.onListenerConnected(this); + } + connected = true; + } + @Override + public void onListenerDisconnected() { + super.onListenerDisconnected(); + 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 @@ -33,9 +33,9 @@ import android.os.Bundle; import android.provider.Settings; import android.service.notification.StatusBarNotification; +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; @@ -46,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) @@ -59,11 +57,63 @@ 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 + private static void cancelNotificationCompat(NotificationReceiver service, String compatKey) { + + if (Build.VERSION.SDK_INT >= 21) { + service.cancelNotification(compatKey); + } else { + int first = compatKey.indexOf(':'); + if (first == -1) { + Log.e("cancelNotificationCompa", "Not formated like a notification key: " + compatKey); + return; + } + int last = compatKey.lastIndexOf(':'); + String packageName = compatKey.substring(0, first); + String tag = compatKey.substring(first + 1, last); + if (tag.length() == 0) tag = null; + String idString = compatKey.substring(last + 1); + int id; + try { + id = Integer.parseInt(idString); + } catch (Exception e) { + id = 0; + } + service.cancelNotification(packageName, tag, id); + } + } + private static String getNotificationKeyCompat(StatusBarNotification statusBarNotification) { + String result; + // first check if it's one of our remoteIds + String tag = statusBarNotification.getTag(); + if (tag != null && tag.startsWith("kdeconnectId:")) + result = Integer.toString(statusBarNotification.getId()); + else if (Build.VERSION.SDK_INT >= 21) { + result = statusBarNotification.getKey(); + } else { + String packageName = statusBarNotification.getPackageName(); + int id = statusBarNotification.getId(); + String safePackageName = (packageName == null) ? "" : packageName; + String safeTag = (tag == null) ? "" : tag; + result = safePackageName + ":" + safeTag + ":" + id; + } + return result; + } + + 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++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars).toLowerCase(); + } @Override public String getDisplayName() { @@ -97,45 +147,37 @@ @Override public boolean onCreate() { - pendingIntents = new HashMap(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - 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(); - } - } - }); - } else { - return false; + + 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); + } } - } + }); + return true; } @Override public void onDestroy() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) - NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { - @Override - public void onServiceStart(NotificationReceiver service) { - service.removeListener(NotificationsPlugin.this); - } - }); + NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { + @Override + public void onServiceStart(NotificationReceiver service) { + service.removeListener(NotificationsPlugin.this); + } + }); } - @Override public void onNotificationRemoved(StatusBarNotification statusBarNotification) { if (statusBarNotification == null) { @@ -150,11 +192,17 @@ } @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); @@ -167,7 +215,7 @@ } appDatabase.open(); - if (!appDatabase.isEnabled(statusBarNotification.getPackageName())){ + if (!appDatabase.isEnabled(statusBarNotification.getPackageName())) { return; // we dont want notification from this app } @@ -178,7 +226,6 @@ String appName = AppsHelper.appNameLookup(context, packageName); - if ("com.facebook.orca".equals(packageName) && (statusBarNotification.getId() == 10012) && "Messenger".equals(appName) && @@ -188,72 +235,65 @@ } if ("com.android.systemui".equals(packageName) && - "low_battery".equals(statusBarNotification.getTag())) - { + "low_battery".equals(statusBarNotification.getTag())) { //HACK: Android low battery notification are posted again every few seconds. Ignore them, as we already have a battery indicator. return; } NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION); - if (packageName.equals("org.kde.kdeconnect_tp")) - { + if (packageName.equals("org.kde.kdeconnect_tp")) { //Make our own notifications silent :) 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 { - 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); - if(rn.pendingIntent != null) { + 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("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); - np.set("silent", true); - } device.sendPackage(np); } - void replyToNotification(String id, String message){ - if(pendingIntents.isEmpty() || !pendingIntents.containsKey(id)){ + @RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH) + private 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) { + if (repliableNotification == null) { Log.e("NotificationsPlugin", "No such notification"); return; } @@ -263,47 +303,48 @@ localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); Bundle localBundle = new Bundle(); int i = 0; - for(RemoteInput remoteIn : repliableNotification.remoteInputs){ + 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); } + @RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH) 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) { + if (remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) { String[] possibleChoices = new String[remoteInput.getChoices().length]; - for(int i = 0; i < remoteInput.getChoices().length; i++){ + 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) { + 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); + title = extras.getString(TITLE_KEY); + } catch (Exception e) { + Log.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText); e.printStackTrace(); } } @@ -313,17 +354,17 @@ return title; } - + private RepliableNotification extractRepliableNotification(StatusBarNotification statusBarNotification) { RepliableNotification repliableNotification = new RepliableNotification(); - - if(statusBarNotification != null) { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + + if (statusBarNotification != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { try { Boolean reply = false; - + //works for WhatsApp, but not for Telegram - if(statusBarNotification.getNotification().actions!=null) { + if (statusBarNotification.getNotification().actions != null) { for (Notification.Action act : statusBarNotification.getNotification().actions) { if (act != null && act.getRemoteInputs() != null) { repliableNotification.remoteInputs.addAll(Arrays.asList(act.getRemoteInputs())); @@ -337,28 +378,28 @@ 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); + } 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) { + 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); + } catch (Exception e) { + Log.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText); e.printStackTrace(); } } @@ -369,7 +410,6 @@ return text; } - /** * Returns the ticker text of the notification. * If device android version is KitKat or newer, the title and text of the notification is used @@ -380,11 +420,11 @@ final String TEXT_KEY = "android.text"; String ticker = ""; - if(notification != null) { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (notification != null) { + 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(); @@ -396,195 +436,108 @@ } else if (extraText != null) { ticker = extraText; } - } catch(Exception e) { - Log.w("NotificationPlugin","problem parsing notification extras for " + notification.tickerText); + } catch (Exception e) { + Log.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText); e.printStackTrace(); } } if (ticker.isEmpty()) { - ticker = (notification.tickerText != null)? notification.tickerText.toString() : ""; + ticker = (notification.tickerText != null) ? notification.tickerText.toString() : ""; } } return ticker; } + private void sendCurrentNotifications(NotificationReceiver service) { + + StatusBarNotification[] notifications = service.getActiveNotifications(); + for (StatusBarNotification notification : notifications) { + sendNotification(notification); + } + } @Override public boolean onPackageReceived(final NetworkPackage np) { -/* - if (np.getBoolean("sendIcons")) { - sendIcons = true; - } -*/ - if (np.getBoolean("request")) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) + if (np.getBoolean("request")) { + if (serviceReady) { NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { - private void sendCurrentNotifications(NotificationReceiver service) { - StatusBarNotification[] notifications = service.getActiveNotifications(); - for (StatusBarNotification notification : notifications) { - sendNotification(notification, true); - } - } - - @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 - 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(); - } + public void onServiceStart(NotificationReceiver service) { + sendCurrentNotifications(service); } }); - } else if (np.has("cancel")) { + } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) - NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { - @Override - public void onServiceStart(NotificationReceiver service) { - String dismissedId = np.getString("cancel"); - cancelNotificationCompat(service, dismissedId); - } - }); + } else if (np.has("cancel")) { + NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { + @Override + public void onServiceStart(NotificationReceiver service) { + String dismissedId = np.getString("cancel"); + cancelNotificationCompat(service, dismissedId); + } + }); } 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")); + } + } return true; } - @Override public AlertDialog getErrorDialog(final Activity deviceActivity) { - if (Build.VERSION.SDK_INT < 18) { - return new AlertDialog.Builder(deviceActivity) - .setTitle(R.string.pref_plugin_notifications) - .setMessage(R.string.plugin_not_available) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - - } - }) - .create(); - } else { - return new AlertDialog.Builder(deviceActivity) - .setTitle(R.string.pref_plugin_notifications) - .setMessage(R.string.no_permissions) - .setPositiveButton(R.string.open_settings, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); - deviceActivity.startActivityForResult(intent, MaterialActivity.RESULT_NEEDS_RELOAD); - } - }) - .setNegativeButton(R.string.cancel,new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - //Do nothing - } - }) - .create(); - } + return new AlertDialog.Builder(deviceActivity) + .setTitle(R.string.pref_plugin_notifications) + .setMessage(R.string.no_permissions) + .setPositiveButton(R.string.open_settings, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); + deviceActivity.startActivityForResult(intent, MaterialActivity.RESULT_NEEDS_RELOAD); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + //Do nothing + } + }) + .create(); } @Override public String[] getSupportedPackageTypes() { - return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST,PACKAGE_TYPE_NOTIFICATION_REPLY}; + return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST, PACKAGE_TYPE_NOTIFICATION_REPLY}; } @Override public String[] getOutgoingPackageTypes() { return new String[]{PACKAGE_TYPE_NOTIFICATION}; } - //For compat with API<21, because lollipop changed the way to cancel notifications - public static void cancelNotificationCompat(NotificationReceiver service, String compatKey) { - if (Build.VERSION.SDK_INT >= 21) { - service.cancelNotification(compatKey); - } else { - int first = compatKey.indexOf(':'); - if (first == -1) { - Log.e("cancelNotificationCompa","Not formated like a notification key: "+ compatKey); - return; - } - int last = compatKey.lastIndexOf(':'); - String packageName = compatKey.substring(0, first); - String tag = compatKey.substring(first + 1, last); - if (tag.length() == 0) tag = null; - String idString = compatKey.substring(last + 1); - int id; - try { - id = Integer.parseInt(idString); - } catch (Exception e) { - id = 0; - } - service.cancelNotification(packageName, tag, id); - } - } - - public static String getNotificationKeyCompat(StatusBarNotification statusBarNotification) { - String result; - // first check if it's one of our remoteIds - String tag = statusBarNotification.getTag(); - if (tag != null && tag.startsWith("kdeconnectId:")) - result = Integer.toString(statusBarNotification.getId()); - else if (Build.VERSION.SDK_INT >= 21) { - result = statusBarNotification.getKey(); - } else { - String packageName = statusBarNotification.getPackageName(); - int id = statusBarNotification.getId(); - String safePackageName = (packageName == null) ? "" : packageName; - String safeTag = (tag == null) ? "" : tag; - result = safePackageName + ":" + safeTag + ":" + id; - } - return result; - } - - public String getChecksum(byte[] data){ + private 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); + 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(); + @Override + public int getMinSdk() { + return Build.VERSION_CODES.JELLY_BEAN_MR2; } - } diff --git a/src/org/kde/kdeconnect/Plugins/Plugin.java b/src/org/kde/kdeconnect/Plugins/Plugin.java --- a/src/org/kde/kdeconnect/Plugins/Plugin.java +++ b/src/org/kde/kdeconnect/Plugins/Plugin.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.os.Build; import android.support.annotation.StringRes; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; @@ -279,4 +280,8 @@ return arePermissionsGranted(getOptionalPermissions()); } + public int getMinSdk() { + return Build.VERSION_CODES.BASE; + } + }