mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-27 02:55:50 +01:00
ID115: use SendNotificationOperation for message and call notifications
This commit is contained in:
parent
4ee1e6cfca
commit
caabe0ed0a
@ -0,0 +1,80 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.id115;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.id115.ID115Constants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public abstract class AbstractID115Operation extends AbstractBTLEOperation<ID115Support> {
|
||||
protected BluetoothGattCharacteristic controlCharacteristic = null;
|
||||
protected BluetoothGattCharacteristic notifyCharacteristic = null;
|
||||
|
||||
protected AbstractID115Operation(ID115Support support) {
|
||||
super(support);
|
||||
|
||||
if (isHealthOperation()) {
|
||||
controlCharacteristic = getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_WRITE_HEALTH);
|
||||
notifyCharacteristic = getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_NOTIFY_HEALTH);
|
||||
} else {
|
||||
controlCharacteristic = getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_WRITE_NORMAL);
|
||||
notifyCharacteristic = getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_NOTIFY_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prePerform() throws IOException {
|
||||
super.prePerform();
|
||||
getDevice().setBusyTask("AbstractID115Operation starting..."); // mark as busy quickly to avoid interruptions from the outside
|
||||
TransactionBuilder builder = performInitialized("disabling some notifications");
|
||||
enableNotifications(builder, true);
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void operationFinished() {
|
||||
operationStatus = OperationStatus.FINISHED;
|
||||
if (getDevice() != null && getDevice().isConnected()) {
|
||||
unsetBusy();
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("reenabling disabled notifications");
|
||||
enableNotifications(builder, false);
|
||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
GB.toast(getContext(), "Error enabling ID115 notifications, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic) {
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (notifyCharacteristic.getUuid().equals(characteristicUUID)) {
|
||||
handleResponse(characteristic.getValue());
|
||||
return true;
|
||||
} else {
|
||||
return super.onCharacteristicChanged(gatt, characteristic);
|
||||
}
|
||||
}
|
||||
|
||||
void enableNotifications(TransactionBuilder builder, boolean enable) {
|
||||
if (isHealthOperation()) {
|
||||
builder.notify(getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_NOTIFY_HEALTH), enable);
|
||||
} else {
|
||||
builder.notify(getCharacteristic(ID115Constants.UUID_CHARACTERISTIC_NOTIFY_NORMAL), enable);
|
||||
}
|
||||
}
|
||||
|
||||
abstract boolean isHealthOperation();
|
||||
|
||||
abstract void handleResponse(byte[] data);
|
||||
}
|
@ -37,7 +37,6 @@ 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;
|
||||
@ -55,14 +54,10 @@ 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)
|
||||
.setWrist(builder)
|
||||
.setScreenOrientation(builder)
|
||||
@ -79,7 +74,11 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
sendMessageNotification(notificationSpec);
|
||||
try {
|
||||
new SendNotificationOperation(this, notificationSpec).perform();
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to send ID115 notification", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -106,7 +105,11 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
if (callSpec.command == CallSpec.CALL_INCOMING) {
|
||||
sendCallNotification(callSpec);
|
||||
try {
|
||||
new SendNotificationOperation(this, callSpec).perform();
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to send ID115 notification", ex);
|
||||
}
|
||||
} else {
|
||||
sendStopCallNotification();
|
||||
}
|
||||
@ -241,40 +244,6 @@ 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()));
|
||||
}
|
||||
@ -359,23 +328,6 @@ 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");
|
||||
@ -389,111 +341,4 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
|
||||
LOG.warn("Unable to stop call notification", 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) {
|
||||
LOG.warn("Unable to send notification chunk", 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,193 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.id115;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.id115.ID115Constants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class SendNotificationOperation extends AbstractID115Operation {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SendNotificationOperation.class);
|
||||
|
||||
byte[] currentNotificationBuffer;
|
||||
int currentNotificationSize;
|
||||
int currentNotificationIndex;
|
||||
byte currentNotificationType;
|
||||
|
||||
SendNotificationOperation(ID115Support support, NotificationSpec notificationSpec)
|
||||
{
|
||||
super(support);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SendNotificationOperation(ID115Support support, CallSpec callSpec)
|
||||
{
|
||||
super(support);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isHealthOperation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPerform() throws IOException {
|
||||
sendNotificationChunk(1);
|
||||
}
|
||||
|
||||
void sendNotificationChunk(int chunkIndex) throws IOException {
|
||||
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);
|
||||
|
||||
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("send notification chunk");
|
||||
builder.write(controlCharacteristic, cmd);
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
void handleResponse(byte[] data) {
|
||||
if (!isOperationRunning()) {
|
||||
LOG.error("ignoring notification because operation is not running. Data length: " + data.length);
|
||||
getSupport().logMessageContent(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.length < 2) {
|
||||
LOG.warn("short GATT response");
|
||||
return;
|
||||
}
|
||||
if (data[0] == ID115Constants.CMD_ID_NOTIFY) {
|
||||
if (data.length < 4) {
|
||||
LOG.warn("short GATT response for NOTIFY");
|
||||
return;
|
||||
}
|
||||
if (data[1] == currentNotificationType) {
|
||||
if (data[3] == currentNotificationIndex) {
|
||||
if (currentNotificationIndex != currentNotificationSize) {
|
||||
try {
|
||||
sendNotificationChunk(currentNotificationIndex + 1);
|
||||
} catch (IOException ex) {
|
||||
GB.toast(getContext(), "Error sending ID115 notification, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
}
|
||||
} else {
|
||||
LOG.info("Notification transfer has finished.");
|
||||
operationFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user