From 497372f3771af0f6c97d9b0ec38c3b6d1c9371ed Mon Sep 17 00:00:00 2001 From: Sebastian Kranz Date: Tue, 3 Jul 2018 13:09:46 +0200 Subject: [PATCH] Add support for sending some generic notifications for incoming calls, missed calls, sms, email, social and messenger. --- .../devices/zetime/ZeTimeConstants.java | 25 ++ .../devices/zetime/ZeTimeDeviceSupport.java | 366 +++++++++++++++++- 2 files changed, 386 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeConstants.java index 4a0cd8e26..bc881106a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeConstants.java @@ -55,10 +55,35 @@ public class ZeTimeConstants { public static final byte CMD_INACTIVITY_ALERT = (byte) 0x5E; public static final byte CMD_CALORIES_TYPE = (byte) 0x60; public static final byte CMD_GET_HEARTRATE_EXDATA = (byte) 0x61; + public static final byte CMD_PUSH_EX_MSG = (byte) 0x76; // here are the action commands public static final byte CMD_REQUEST = (byte) 0x70; + public static final byte CMD_SEND = (byte) 0x71; public static final byte CMD_REQUEST_RESPOND = (byte) 0x80; // further commands public static final byte CMD_END = (byte) 0x8f; public static final byte CMD_ACK_WRITE = (byte) 0x03; + // notification types and icons + public static final byte NOTIFICATION_MISSED_CALL = (byte) 0x00; + public static final byte NOTIFICATION_SMS = (byte) 0x01; + public static final byte NOTIFICATION_SOCIAL = (byte) 0x02; + public static final byte NOTIFICATION_EMAIL = (byte) 0x03; + public static final byte NOTIFICATION_CALENDAR = (byte) 0x04; + public static final byte NOTIFICATION_INCOME_CALL = (byte) 0x05; + public static final byte NOTIFICATION_CALL_OFF = (byte) 0x06; + public static final byte NOTIFICATION_WECHAT = (byte) 0x07; + public static final byte NOTIFICATION_VIBER = (byte) 0x08; + public static final byte NOTIFICATION_SNAPCHAT = (byte) 0x09; + public static final byte NOTIFICATION_WHATSAPP = (byte) 0x0A; + public static final byte NOTIFICATION_QQ = (byte) 0x0B; + public static final byte NOTIFICATION_FACEBOOK = (byte) 0x0C; + public static final byte NOTIFICATION_HANGOUTS = (byte) 0x0D; + public static final byte NOTIFICATION_GMAIL = (byte) 0x0E; + public static final byte NOTIFICATION_MESSENGER = (byte) 0x0F; + public static final byte NOTIFICATION_INSTAGRAM = (byte) 0x10; + public static final byte NOTIFICATION_TWITTER = (byte) 0x11; + public static final byte NOTIFICATION_LINKEDIN = (byte) 0x12; + public static final byte NOTIFICATION_UBER = (byte) 0x13; + public static final byte NOTIFICATION_LINE = (byte) 0x14; + public static final byte NOTIFICATION_SKYPE = (byte) 0x15; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java index c2b093536..c74382ccc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java @@ -9,8 +9,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -55,6 +58,8 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { private int progressSteps; private int progressSleep; private int progressHeartRate; + private final int maxMsgLength = 20; + private boolean callIncoming = false; public BluetoothGattCharacteristic notifyCharacteristic = null; public BluetoothGattCharacteristic writeCharacteristic = null; @@ -151,6 +156,84 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onSetCallState(CallSpec callSpec) { + int subject_length = 0; + int notification_length = 0; + byte[] subject = null; + byte[] notification = null; + Calendar time = GregorianCalendar.getInstance(); + byte[] datetimeBytes = new byte[]{ + (byte) ((time.get(Calendar.YEAR) / 1000) + '0'), + (byte) (((time.get(Calendar.YEAR) / 100)%10) + '0'), + (byte) (((time.get(Calendar.YEAR) / 10)%10) + '0'), + (byte) ((time.get(Calendar.YEAR)%10) + '0'), + (byte) (((time.get(Calendar.MONTH)+1)/10) + '0'), + (byte) (((time.get(Calendar.MONTH)+1)%10) + '0'), + (byte) ((time.get(Calendar.DAY_OF_MONTH)/10) + '0'), + (byte) ((time.get(Calendar.DAY_OF_MONTH)%10) + '0'), + (byte) 'T', + (byte) ((time.get(Calendar.HOUR_OF_DAY)/10) + '0'), + (byte) ((time.get(Calendar.HOUR_OF_DAY)%10) + '0'), + (byte) ((time.get(Calendar.MINUTE)/10) + '0'), + (byte) ((time.get(Calendar.MINUTE)%10) + '0'), + (byte) ((time.get(Calendar.SECOND)/10) + '0'), + (byte) ((time.get(Calendar.SECOND)%10) + '0'), + }; + + if(callIncoming || (callSpec.command == CallSpec.CALL_INCOMING)) { + if (callSpec.command == CallSpec.CALL_INCOMING) { + if (callSpec.name != null) { + notification_length += callSpec.name.length(); + subject_length = callSpec.name.length(); + subject = new byte[subject_length]; + System.arraycopy(callSpec.name.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } else if (callSpec.number != null) { + notification_length += callSpec.number.length(); + subject_length = callSpec.number.length(); + subject = new byte[subject_length]; + System.arraycopy(callSpec.number.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } + notification_length += datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte) ((notification_length - 6) & 0xff); + notification[4] = (byte) ((notification_length - 6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_INCOME_CALL; + notification[6] = 1; + notification[7] = (byte) subject_length; + notification[8] = (byte) 0; + System.arraycopy(subject, 0, notification, 9, subject_length); + System.arraycopy(datetimeBytes, 0, notification, 9 + subject_length, datetimeBytes.length); + notification[notification_length - 1] = ZeTimeConstants.CMD_END; + callIncoming = true; + } else { + notification_length = datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte) ((notification_length - 6) & 0xff); + notification[4] = (byte) ((notification_length - 6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_CALL_OFF; + notification[6] = 1; + notification[7] = (byte) 0; + notification[8] = (byte) 0; + System.arraycopy(datetimeBytes, 0, notification, 9, datetimeBytes.length); + notification[notification_length - 1] = ZeTimeConstants.CMD_END; + callIncoming = false; + } + if(notification != null) + { + try { + TransactionBuilder builder = performInitialized("setCallState"); + sendMsgToWatch(builder, notification); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error set call state: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + } } @@ -248,6 +331,254 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { + int subject_length = 0; + int body_length = notificationSpec.body.length(); + int notification_length = notificationSpec.body.length(); + byte[] subject = null; + byte[] notification = null; + Calendar time = GregorianCalendar.getInstance(); + byte[] datetimeBytes = new byte[]{ + (byte) ((time.get(Calendar.YEAR) / 1000) + '0'), + (byte) (((time.get(Calendar.YEAR) / 100)%10) + '0'), + (byte) (((time.get(Calendar.YEAR) / 10)%10) + '0'), + (byte) ((time.get(Calendar.YEAR)%10) + '0'), + (byte) (((time.get(Calendar.MONTH)+1)/10) + '0'), + (byte) (((time.get(Calendar.MONTH)+1)%10) + '0'), + (byte) ((time.get(Calendar.DAY_OF_MONTH)/10) + '0'), + (byte) ((time.get(Calendar.DAY_OF_MONTH)%10) + '0'), + (byte) 'T', + (byte) ((time.get(Calendar.HOUR_OF_DAY)/10) + '0'), + (byte) ((time.get(Calendar.HOUR_OF_DAY)%10) + '0'), + (byte) ((time.get(Calendar.MINUTE)/10) + '0'), + (byte) ((time.get(Calendar.MINUTE)%10) + '0'), + (byte) ((time.get(Calendar.SECOND)/10) + '0'), + (byte) ((time.get(Calendar.SECOND)%10) + '0'), + }; + + switch(notificationSpec.type) + { + case GENERIC_SMS: + if (notificationSpec.sender != null) + { + notification_length += notificationSpec.sender.length(); + subject_length = notificationSpec.sender.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.sender.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } else if(notificationSpec.phoneNumber != null) + { + notification_length += notificationSpec.phoneNumber.length(); + subject_length = notificationSpec.phoneNumber.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.phoneNumber.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } + notification_length += datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte)((notification_length-6) & 0xff); + notification[4] = (byte)((notification_length-6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_SMS; + notification[6] = 1; + notification[7] = (byte)subject_length; + notification[8] = (byte)body_length; + System.arraycopy(subject, 0, notification, 9, subject_length); + System.arraycopy(notificationSpec.body.getBytes(StandardCharsets.UTF_8), 0, notification, 9+subject_length, body_length); + System.arraycopy(datetimeBytes, 0, notification, 9+subject_length+body_length, datetimeBytes.length); + notification[notification_length-1] = ZeTimeConstants.CMD_END; + break; + case GENERIC_PHONE: + if (notificationSpec.sender != null) + { + notification_length += notificationSpec.sender.length(); + subject_length = notificationSpec.sender.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.sender.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } else if(notificationSpec.phoneNumber != null) + { + notification_length += notificationSpec.phoneNumber.length(); + subject_length = notificationSpec.phoneNumber.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.phoneNumber.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } + notification_length += datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte)((notification_length-6) & 0xff); + notification[4] = (byte)((notification_length-6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_MISSED_CALL; + notification[6] = 1; + notification[7] = (byte)subject_length; + notification[8] = (byte)body_length; + System.arraycopy(subject, 0, notification, 9, subject_length); + System.arraycopy(notificationSpec.body.getBytes(StandardCharsets.UTF_8), 0, notification, 9+subject_length, body_length); + System.arraycopy(datetimeBytes, 0, notification, 9+subject_length+body_length, datetimeBytes.length); + notification[notification_length-1] = ZeTimeConstants.CMD_END; + break; + case GMAIL: + case GOOGLE_INBOX: + case MAILBOX: + case OUTLOOK: + case YAHOO_MAIL: + case GENERIC_EMAIL: + if (notificationSpec.sender != null) + { + notification_length += notificationSpec.sender.length(); + subject_length = notificationSpec.sender.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.sender.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } else if(notificationSpec.subject != null) + { + notification_length += notificationSpec.subject.length(); + subject_length = notificationSpec.subject.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.subject.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } + notification_length += datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte)((notification_length-6) & 0xff); + notification[4] = (byte)((notification_length-6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_EMAIL; + notification[6] = 1; + notification[7] = (byte)subject_length; + notification[8] = (byte)body_length; + System.arraycopy(subject, 0, notification, 9, subject_length); + System.arraycopy(notificationSpec.body.getBytes(StandardCharsets.UTF_8), 0, notification, 9+subject_length, body_length); + System.arraycopy(datetimeBytes, 0, notification, 9+subject_length+body_length, datetimeBytes.length); + notification[notification_length-1] = ZeTimeConstants.CMD_END; + break; + case CONVERSATIONS: + case FACEBOOK_MESSENGER: + case RIOT: + case SIGNAL: + case TELEGRAM: + case THREEMA: + case KONTALK: + case ANTOX: + case WHATSAPP: + case GOOGLE_MESSENGER: + case GOOGLE_HANGOUTS: + case HIPCHAT: + case SKYPE: + case WECHAT: + case KIK: + case KAKAO_TALK: + case SLACK: + case LINE: + case VIBER: + if (notificationSpec.sender != null) + { + notification_length += notificationSpec.sender.length(); + subject_length = notificationSpec.sender.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.sender.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } else if(notificationSpec.subject != null) + { + notification_length += notificationSpec.subject.length(); + subject_length = notificationSpec.subject.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.subject.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } + notification_length += datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte)((notification_length-6) & 0xff); + notification[4] = (byte)((notification_length-6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_MESSENGER; + notification[6] = 1; + notification[7] = (byte)subject_length; + notification[8] = (byte)body_length; + System.arraycopy(subject, 0, notification, 9, subject_length); + System.arraycopy(notificationSpec.body.getBytes(StandardCharsets.UTF_8), 0, notification, 9+subject_length, body_length); + System.arraycopy(datetimeBytes, 0, notification, 9+subject_length+body_length, datetimeBytes.length); + notification[notification_length-1] = ZeTimeConstants.CMD_END; + break; + case FACEBOOK: + case TWITTER: + case SNAPCHAT: + case INSTAGRAM: + case LINKEDIN: + if (notificationSpec.sender != null) + { + notification_length += notificationSpec.sender.length(); + subject_length = notificationSpec.sender.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.sender.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } else if(notificationSpec.subject != null) + { + notification_length += notificationSpec.subject.length(); + subject_length = notificationSpec.subject.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.subject.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } + notification_length += datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte)((notification_length-6) & 0xff); + notification[4] = (byte)((notification_length-6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_SOCIAL; + notification[6] = 1; + notification[7] = (byte)subject_length; + notification[8] = (byte)body_length; + System.arraycopy(subject, 0, notification, 9, subject_length); + System.arraycopy(notificationSpec.body.getBytes(StandardCharsets.UTF_8), 0, notification, 9+subject_length, body_length); + System.arraycopy(datetimeBytes, 0, notification, 9+subject_length+body_length, datetimeBytes.length); + notification[notification_length-1] = ZeTimeConstants.CMD_END; + break; + case GENERIC_CALENDAR: + if (notificationSpec.sender != null) + { + notification_length += notificationSpec.sender.length(); + subject_length = notificationSpec.sender.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.sender.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } else if(notificationSpec.subject != null) + { + notification_length += notificationSpec.subject.length(); + subject_length = notificationSpec.subject.length(); + subject = new byte[subject_length]; + System.arraycopy(notificationSpec.subject.getBytes(StandardCharsets.UTF_8), 0, subject, 0, subject_length); + } + notification_length += datetimeBytes.length + 10; // add message overhead + notification = new byte[notification_length]; + notification[0] = ZeTimeConstants.CMD_PREAMBLE; + notification[1] = ZeTimeConstants.CMD_PUSH_EX_MSG; + notification[2] = ZeTimeConstants.CMD_SEND; + notification[3] = (byte)((notification_length-6) & 0xff); + notification[4] = (byte)((notification_length-6) >> 8); + notification[5] = ZeTimeConstants.NOTIFICATION_CALENDAR; + notification[6] = 1; + notification[7] = (byte)subject_length; + notification[8] = (byte)body_length; + System.arraycopy(subject, 0, notification, 9, subject_length); + System.arraycopy(notificationSpec.body.getBytes(StandardCharsets.UTF_8), 0, notification, 9+subject_length, body_length); + System.arraycopy(datetimeBytes, 0, notification, 9+subject_length+body_length, datetimeBytes.length); + notification[notification_length-1] = ZeTimeConstants.CMD_END; + break; + default: + break; + } + if(notification != null) + { + try { + TransactionBuilder builder = performInitialized("sendNotification"); + //builder.write(writeCharacteristic, notification); + //builder.write(ackCharacteristic, new byte[]{ZeTimeConstants.CMD_ACK_WRITE}); + sendMsgToWatch(builder, notification); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error sending notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } } @Override @@ -370,7 +701,7 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { ZeTimeConstants.CMD_REQUEST, 0x01, 0x00, - 0x00, + 0x02, ZeTimeConstants.CMD_END}); builder.write(ackCharacteristic, new byte[]{ZeTimeConstants.CMD_ACK_WRITE}); return this; @@ -414,8 +745,8 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { private void handleDeviceInfo(byte[] value) { value[value.length-1] = 0; // convert the end to a String end - byte[] string = Arrays.copyOfRange(value,5, value.length-1); - if(string.length > 6) + byte[] string = Arrays.copyOfRange(value,6, value.length-1); + if(value[5] == 5) { versionCmd.fwVersion = new String(string); } else{ @@ -452,7 +783,6 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { 0x00, 0x00, ZeTimeConstants.CMD_END}); - performConnected(builder.getTransaction()); builder.write(ackCharacteristic, new byte[]{ZeTimeConstants.CMD_ACK_WRITE}); performConnected(builder.getTransaction()); } catch (IOException e) { @@ -471,7 +801,6 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { 0x00, 0x00, ZeTimeConstants.CMD_END}); - performConnected(builder.getTransaction()); builder.write(ackCharacteristic, new byte[]{ZeTimeConstants.CMD_ACK_WRITE}); performConnected(builder.getTransaction()); } catch (IOException e) { @@ -606,4 +935,31 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { } } } + + private void sendMsgToWatch(TransactionBuilder builder, byte[] msg) + { + if(msg.length > maxMsgLength) + { + int msgpartlength = 0; + byte[] msgpart = null; + + do { + if((msg.length - msgpartlength) < maxMsgLength) + { + msgpart = new byte[msg.length - msgpartlength]; + System.arraycopy(msg, msgpartlength, msgpart, 0, msg.length - msgpartlength); + msgpartlength += (msg.length - msgpartlength); + } else { + msgpart = new byte[maxMsgLength]; + System.arraycopy(msg, msgpartlength, msgpart, 0, maxMsgLength); + msgpartlength += maxMsgLength; + } + builder.write(writeCharacteristic, msgpart); + }while(msgpartlength < msg.length); + } else + { + builder.write(writeCharacteristic, msg); + } + builder.write(ackCharacteristic, new byte[]{ZeTimeConstants.CMD_ACK_WRITE}); + } }