diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java index deab36825..010ff0af3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java @@ -8,6 +8,7 @@ public class GBDeviceEventNotificationControl extends GBDeviceEvent { public enum Event { UNKNOWN, DISMISS, + DISMISS_ALL, OPEN } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 0b325a05b..ed15ebfbe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -1,5 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; @@ -27,10 +28,13 @@ public class NotificationListener extends NotificationListenerService { public static final String ACTION_DISMISS = "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.dismiss"; + public static final String ACTION_DISMISS_ALL + = "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.dismiss_all"; public static final String ACTION_OPEN = "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.open"; private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @SuppressLint("NewApi") @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -50,8 +54,25 @@ public class NotificationListener extends NotificationListenerService { } } } else if (action.equals(ACTION_DISMISS)) { + StatusBarNotification[] sbns = NotificationListener.this.getActiveNotifications(); + int handle = intent.getIntExtra("handle", -1); + for (StatusBarNotification sbn : sbns) { + if ((int) sbn.getPostTime() == handle) { + if (GBApplication.isRunningLollipopOrLater()) { + String key = sbn.getKey(); + NotificationListener.this.cancelNotification(key); + } else { + int id = sbn.getId(); + String pkg = sbn.getPackageName(); + String tag = sbn.getTag(); + NotificationListener.this.cancelNotification(pkg, tag, id); + } + } + } + } else if (action.equals(ACTION_DISMISS_ALL)) { NotificationListener.this.cancelAllNotifications(); } + } }; @@ -59,8 +80,9 @@ public class NotificationListener extends NotificationListenerService { public void onCreate() { super.onCreate(); IntentFilter filterLocal = new IntentFilter(); - filterLocal.addAction(ACTION_DISMISS); filterLocal.addAction(ACTION_OPEN); + filterLocal.addAction(ACTION_DISMISS); + filterLocal.addAction(ACTION_DISMISS_ALL); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index 9aaa4d9dd..3bc4e64de 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -214,6 +214,9 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { case DISMISS: action = NotificationListener.ACTION_DISMISS; break; + case DISMISS_ALL: + action = NotificationListener.ACTION_DISMISS_ALL; + break; case OPEN: action = NotificationListener.ACTION_OPEN; break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index a26dda7fd..bc55e0943 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -362,7 +362,7 @@ public class PebbleProtocol extends GBDeviceProtocol { return buf.array(); } - private byte[] encodeNotification(int id, String title, String subtitle, String body, byte type) { + private byte[] encodeNotification(int id, String title, String subtitle, String body, byte type, boolean hasHandle) { Long ts = System.currentTimeMillis(); if (!isFw3x) { ts += (SimpleTimeZone.getDefault().getOffset(ts)); @@ -371,10 +371,10 @@ public class PebbleProtocol extends GBDeviceProtocol { if (isFw3x) { // 3.x notification - return encodeBlobdbNotification((int) (ts & 0xffffffff), title, subtitle, body, type); + return encodeBlobdbNotification(id, (int) (ts & 0xffffffff), title, subtitle, body, type, hasHandle); } else if (mForceProtocol || type != NOTIFICATION_EMAIL) { // 2.x notification - return encodeExtensibleNotification(id, (int) (ts & 0xffffffff), title, subtitle, body, type); + return encodeExtensibleNotification(id, (int) (ts & 0xffffffff), title, subtitle, body, type, hasHandle); } else { // 1.x notification on FW 2.X String[] parts = {title, body, ts.toString(), subtitle}; @@ -384,17 +384,17 @@ public class PebbleProtocol extends GBDeviceProtocol { @Override public byte[] encodeSMS(String from, String body) { - return encodeNotification(mRandom.nextInt(), from, null, body, NOTIFICATION_SMS); + return encodeNotification(mRandom.nextInt(), from, null, body, NOTIFICATION_SMS, false); } @Override public byte[] encodeEmail(String from, String subject, String body) { - return encodeNotification(mRandom.nextInt(), from, subject, body, NOTIFICATION_EMAIL); + return encodeNotification(mRandom.nextInt(), from, subject, body, NOTIFICATION_EMAIL, false); } @Override public byte[] encodeGenericNotification(String title, String details, int handle) { - return encodeNotification(handle, title, null, details, NOTIFICATION_SMS); + return encodeNotification(handle, title, null, details, NOTIFICATION_SMS, true); } @Override @@ -431,13 +431,23 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeSetCallState("Where are you?", "Gadgetbridge", start ? ServiceCommand.CALL_INCOMING : ServiceCommand.CALL_END); } - private static byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, byte type) { + private static byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, byte type, boolean hasHandle) { String[] parts = {title, subtitle, body}; // Calculate length first + String actionstring; + byte action_id; + if (hasHandle) { + actionstring = "dismiss"; + action_id = 0x02; + } else { + actionstring = "dismiss all"; + action_id = 0x03; + } + byte attributes_count = 0; - int length = 21 + 17; //+ 19 + int length = 21 + 10 + actionstring.length(); //+ 19 if (parts != null) { for (String s : parts) { if (s == null || s.equals("")) { @@ -485,11 +495,10 @@ public class PebbleProtocol extends GBDeviceProtocol { } // ACTION - buf.put((byte) 0x02); // id + buf.put(action_id); buf.put((byte) 0x04); // dismiss action buf.put((byte) 0x01); // number attributes buf.put((byte) 0x01); // attribute id (title) - String actionstring = "dismiss all"; buf.putShort((short) actionstring.length()); buf.put(actionstring.getBytes()); @@ -537,7 +546,7 @@ public class PebbleProtocol extends GBDeviceProtocol { return buf.array(); } - private byte[] encodeBlobdbNotification(int timestamp, String title, String subtitle, String body, byte type) { + private byte[] encodeBlobdbNotification(int id, int timestamp, String title, String subtitle, String body, byte type, boolean hasHandle) { String[] parts = {title, subtitle, body}; int icon_id = 0x80000000 | 1; @@ -549,13 +558,24 @@ public class PebbleProtocol extends GBDeviceProtocol { icon_id = 0x80000000 | 45; } // Calculate length first + + String actionstring; + byte action_id; + if (hasHandle) { + actionstring = "dismiss"; + action_id = 0x02; + } else { + actionstring = "dismiss all"; + action_id = 0x03; + } + final short NOTIFICATION_PIN_LENGTH = 46; - final short ACTIONS_LENGTH = 17; + short actions_length = (short) (10 + actionstring.length()); byte attributes_count = 1; // icon byte actions_count = 1; // dismiss - short attributes_length = 7 + ACTIONS_LENGTH; // icon + short attributes_length = (short) (7 + actions_length); // icon if (parts != null) { for (String s : parts) { if (s == null || s.equals("")) { @@ -574,9 +594,12 @@ public class PebbleProtocol extends GBDeviceProtocol { // pin - 46 bytes buf.order(ByteOrder.BIG_ENDIAN); buf.putLong(uuid.getMostSignificantBits()); - buf.putLong(uuid.getLeastSignificantBits()); + buf.putInt((int) (uuid.getLeastSignificantBits() >>> 32)); + buf.putInt(id); buf.putLong(uuid.getMostSignificantBits()); - buf.putLong(uuid.getLeastSignificantBits()); + buf.putInt((int) (uuid.getLeastSignificantBits() >>> 32)); + buf.putInt(id); + LOG.info("id " + id); buf.order(ByteOrder.LITTLE_ENDIAN); buf.putInt(timestamp); // 32-bit timestamp buf.putShort((short) 0); // duration @@ -609,19 +632,19 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putInt(icon_id); // ACTION - buf.put((byte) 0x02); // id + buf.put(action_id); buf.put((byte) 0x02); // generic action, dismiss did not do anything buf.put((byte) 0x01); // number attributes buf.put((byte) 0x01); // attribute id (title) - String actionstring = "dismiss all"; + buf.putShort((short) actionstring.length()); buf.put(actionstring.getBytes()); return encodeBlobdb(UUID.randomUUID(), BLOBDB_INSERT, BLOBDB_NOTIFICATION, buf.array()); } - public byte[] encodeActionResponse(UUID uuid) { - short length = 38; + public byte[] encodeActionResponse(UUID uuid, int iconId, String caption) { + short length = (short) (29 + caption.length()); ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + length); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(length); @@ -634,10 +657,10 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.put((byte) 2); //nr of attributes buf.put((byte) 6); // icon buf.putShort((short) 4); // length - buf.putInt(0x80000033); // icon id + buf.putInt(0x80000000 | iconId); buf.put((byte) 2); // title - buf.putShort((short) 9); // length - buf.put("Dismissed".getBytes()); + buf.putShort((short) caption.length()); + buf.put(caption.getBytes()); return buf.array(); } @@ -1095,7 +1118,7 @@ public class PebbleProtocol extends GBDeviceProtocol { if (command == 0x02) { int id = buf.getInt(); byte action = buf.get(); - if (action == 0x01 || action == 0x02) { + if (action >= 0x01 && action <= 0x03) { GBDeviceEventNotificationControl devEvtNotificationControl = new GBDeviceEventNotificationControl(); devEvtNotificationControl.handle = id; switch (action) { @@ -1105,6 +1128,9 @@ public class PebbleProtocol extends GBDeviceProtocol { case 0x02: devEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.DISMISS; break; + case 0x03: + devEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.DISMISS_ALL; + break; default: return null; } @@ -1124,14 +1150,33 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.order(ByteOrder.BIG_ENDIAN); long uuid_high = buf.getLong(); long uuid_low = buf.getLong(); - int id = (int) (uuid_low & 0xffff); + int id = (int) (uuid_low & 0xffffffff); + LOG.info("id " + id); byte action = buf.get(); - if (action == 0x02) { + if (action >= 0x01 && action <= 0x03) { GBDeviceEventNotificationControl dismissNotification = new GBDeviceEventNotificationControl(); dismissNotification.handle = id; - dismissNotification.event = GBDeviceEventNotificationControl.Event.DISMISS; + String caption = "undefined"; + int icon_id = 1; + switch (action) { + case 0x01: + dismissNotification.event = GBDeviceEventNotificationControl.Event.OPEN; + caption = "Opened"; + icon_id = 47; // FIXME: find a better one + break; + case 0x02: + dismissNotification.event = GBDeviceEventNotificationControl.Event.DISMISS; + caption = "Dismissed"; + icon_id = 51; + break; + case 0x03: + dismissNotification.event = GBDeviceEventNotificationControl.Event.DISMISS_ALL; + caption = "All dismissed"; + icon_id = 51; + break; + } GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = encodeActionResponse(new UUID(uuid_high, uuid_low)); + sendBytesAck.encodedBytes = encodeActionResponse(new UUID(uuid_high, uuid_low), icon_id, caption); return new GBDeviceEvent[]{sendBytesAck, dismissNotification}; } LOG.info("unexpected action: " + action);