1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-24 15:43:46 +02:00

ID115: notification support

This commit is contained in:
Vadim Kaushan 2018-07-13 14:08:11 +03:00
parent 4bea2a3407
commit c9e7a2ff1d
2 changed files with 239 additions and 3 deletions

View File

@ -2,6 +2,8 @@ package nodomain.freeyourgadget.gadgetbridge.devices.id115;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
public class ID115Constants {
@ -28,6 +30,53 @@ public class ID115Constants {
public static final byte CMD_KEY_SET_TIME = 0x01;
public static final byte CMD_KEY_SET_DISPLAY_MODE = 0x2B;
// CMD_ID_NOTIFY
public static final byte CMD_KEY_NOTIFY_CALL = 0x01;
public static final byte CMD_KEY_NOTIFY_STOP = 0x02;
public static final byte CMD_KEY_NOTIFY_MSG = 0x03;
// CMD_ID_DEVICE_RESTART
public static final byte CMD_KEY_REBOOT = 0x01;
public static byte getNotificationType(NotificationType type) {
switch (type) {
// case GENERIC_EMAIL:
// return 2; // Icon is not supported
case WECHAT:
return 3;
// case QQ:
// return 4;
case FACEBOOK:
return 6;
case TWITTER:
return 7;
case WHATSAPP:
return 8;
case FACEBOOK_MESSENGER:
return 9;
case INSTAGRAM:
return 10;
case LINKEDIN:
return 11;
// case GENERIC_CALENDAR:
// return 12; // Icon is not supported
// case SKYPE:
// return 13; // Icon is not supported
// case LINE:
// return 17; // Icon is not supported
// case VIBER:
// return 18; // Icon is not supported
// case KAKAO_TALK:
// return 19; // Icon is not supported
// case VK:
// return 16; // Icon is not supported
// case GMAIL:
// return 20; // Icon is not supported
// case OUTLOOK:
// return 21; // Icon is not supported
// case SNAPCHAT:
// return 22; // Icon is not supported
}
return 1;
}
}

View File

@ -1,15 +1,17 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.id115;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.UUID;
@ -22,6 +24,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
@ -32,8 +35,14 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(ID115Support.class);
public BluetoothGattCharacteristic normalWriteCharacteristic = null;
public BluetoothGattCharacteristic normalNotifyCharacteristic = null;
public BluetoothGattCharacteristic healthWriteCharacteristic = null;
byte[] currentNotificationBuffer;
int currentNotificationSize;
int currentNotificationIndex;
byte currentNotificationType;
public ID115Support() {
super(LOG);
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
@ -44,10 +53,14 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
normalWriteCharacteristic = getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_WRITE_NORMAL);
normalNotifyCharacteristic = getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_NOTIFY_NORMAL);
healthWriteCharacteristic = getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_WRITE_HEALTH);
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
builder.setGattCallback(this);
builder.notify(normalNotifyCharacteristic, true);
setTime(builder)
.setDisplayMode(builder, false)
.setInitialized(builder);
@ -62,7 +75,7 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
@Override
public void onNotification(NotificationSpec notificationSpec) {
sendMessageNotification(notificationSpec);
}
@Override
@ -87,7 +100,11 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
@Override
public void onSetCallState(CallSpec callSpec) {
if (callSpec.command == CallSpec.CALL_INCOMING) {
sendCallNotification(callSpec);
} else {
sendStopCallNotification();
}
}
@Override
@ -219,6 +236,40 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
if (super.onCharacteristicChanged(gatt, characteristic)) {
return true;
}
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if (!characteristicUUID.equals(ID115Constants.UUID_CHARACTERISTIC_NOTIFY_NORMAL)) {
return false;
}
if (data.length < 2) {
LOG.warn("short GATT response");
return false;
}
if (data[0] == ID115Constants.CMD_ID_NOTIFY) {
if (data.length < 4) {
LOG.warn("short GATT response for NOTIFY");
return false;
}
if (data[1] == currentNotificationType) {
if (data[3] == currentNotificationIndex) {
if (currentNotificationIndex != currentNotificationSize) {
sendNotificationChunk(currentNotificationIndex + 1);
return true;
}
}
}
}
return false;
}
private void setInitialized(TransactionBuilder builder) {
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
}
@ -258,4 +309,140 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
});
return this;
}
void sendCallNotification(CallSpec callSpec) {
String number = "";
if (callSpec.number != null) {
number = callSpec.number;
}
String name = "";
if (callSpec.name != null) {
name = callSpec.name;
}
currentNotificationBuffer = encodeCallNotification(name, number);
currentNotificationSize = (currentNotificationBuffer.length + 15) / 16;
currentNotificationType = ID115Constants.CMD_KEY_NOTIFY_CALL;
sendNotificationChunk(1);
}
void sendStopCallNotification() {
try {
TransactionBuilder builder = performInitialized("stop_call_notification");
builder.write(normalWriteCharacteristic, new byte[] {
ID115Constants.CMD_ID_NOTIFY,
ID115Constants.CMD_KEY_NOTIFY_STOP,
1
});
performConnected(builder.getTransaction());
} catch(IOException e) {
}
}
void sendMessageNotification(NotificationSpec notificationSpec) {
String phone = "";
if (notificationSpec.phoneNumber != null) {
phone = notificationSpec.phoneNumber;
}
String title = "";
if (notificationSpec.sender != null) {
title = notificationSpec.sender;
} else if (notificationSpec.title != null) {
title = notificationSpec.title;
} else if (notificationSpec.subject != null) {
title = notificationSpec.subject;
}
String text = "";
if (notificationSpec.body != null) {
text = notificationSpec.body;
}
currentNotificationBuffer = encodeMessageNotification(notificationSpec.type, title, phone, text);
currentNotificationSize = (currentNotificationBuffer.length + 15) / 16;
currentNotificationType = ID115Constants.CMD_KEY_NOTIFY_MSG;
sendNotificationChunk(1);
}
void sendNotificationChunk(int chunkIndex) {
currentNotificationIndex = chunkIndex;
int offset = (chunkIndex - 1) * 16;
int tailSize = currentNotificationBuffer.length - offset;
int chunkSize = (tailSize > 16)? 16 : tailSize;
byte raw[] = new byte[16];
System.arraycopy(currentNotificationBuffer, offset, raw, 0, chunkSize);
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(ID115Constants.CMD_ID_NOTIFY);
outputStream.write(currentNotificationType);
outputStream.write((byte)currentNotificationSize);
outputStream.write((byte)currentNotificationIndex);
outputStream.write(raw);
byte cmd[] = outputStream.toByteArray();
TransactionBuilder builder = performInitialized("notification");
builder.write(normalWriteCharacteristic, cmd);
performConnected(builder.getTransaction());
} catch (IOException e) {
}
}
byte[] encodeCallNotification(String name, String phone) {
if (name.length() > 20) {
name = name.substring(0, 20);
}
if (phone.length() > 20) {
phone = phone.substring(0, 20);
}
byte[] name_bytes = name.getBytes(StandardCharsets.UTF_8);
byte[] phone_bytes = phone.getBytes(StandardCharsets.UTF_8);
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write((byte) phone_bytes.length);
outputStream.write((byte) name_bytes.length);
outputStream.write(phone_bytes);
outputStream.write(name_bytes);
return outputStream.toByteArray();
} catch (IOException e) {
return null;
}
}
byte[] encodeMessageNotification(NotificationType type, String title, String phone, String text) {
if (title.length() > 20) {
title = title.substring(0, 20);
}
if (phone.length() > 20) {
phone = phone.substring(0, 20);
}
if (text.length() > 20) {
text = text.substring(0, 20);
}
byte[] title_bytes = title.getBytes(StandardCharsets.UTF_8);
byte[] phone_bytes = phone.getBytes(StandardCharsets.UTF_8);
byte[] text_bytes = text.getBytes(StandardCharsets.UTF_8);
byte nativeType = ID115Constants.getNotificationType(type);
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(nativeType);
outputStream.write((byte) text_bytes.length);
outputStream.write((byte) phone_bytes.length);
outputStream.write((byte) title_bytes.length);
outputStream.write(phone_bytes);
outputStream.write(title_bytes);
outputStream.write(text_bytes);
return outputStream.toByteArray();
} catch (IOException e) {
return null;
}
}
}