mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-29 20:15:50 +01:00
Xiaomi: refactor XiaomiCharacteristic to use callback per message
This commit is contained in:
parent
592a52054f
commit
e5c2bd51c2
@ -70,8 +70,6 @@ public class XiaomiCharacteristic {
|
||||
|
||||
private XiaomiChannelHandler channelHandler = null;
|
||||
|
||||
private SendCallback callback;
|
||||
|
||||
public XiaomiCharacteristic(final XiaomiBleSupport support,
|
||||
final BluetoothGattCharacteristic bluetoothGattCharacteristic,
|
||||
@Nullable final XiaomiAuthService authService) {
|
||||
@ -90,10 +88,6 @@ public class XiaomiCharacteristic {
|
||||
this.channelHandler = handler;
|
||||
}
|
||||
|
||||
public void setCallback(final SendCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public void setEncrypted(final boolean encrypted) {
|
||||
this.isEncrypted = encrypted;
|
||||
}
|
||||
@ -113,11 +107,29 @@ public class XiaomiCharacteristic {
|
||||
this.currentPayload = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes to this characteristic, encrypting and splitting it into chunks if necessary.
|
||||
* Callback will be notified when a (n)ack has been received by the remote device.
|
||||
*/
|
||||
public void write(final String taskName, final byte[] value, final SendCallback callback) {
|
||||
write(null, new Payload(taskName, value, callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes to this characteristic, encrypting and splitting it into chunks if necessary.
|
||||
*/
|
||||
public void write(final String taskName, final byte[] value) {
|
||||
write(null, new Payload(taskName, value));
|
||||
write(taskName, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes to this characteristic, encrypting and splitting it into chunks if necessary. Uses
|
||||
* the provided builder if we need to schedule something, otherwise it will be queued as other
|
||||
* commands. The callback will be notified when a (n)ack has been received from the remote
|
||||
* device in response to the payload being sent.
|
||||
*/
|
||||
public void write(final TransactionBuilder builder, final byte[] value, final SendCallback callback) {
|
||||
write(builder, new Payload(builder.getTaskName(), value, callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,7 +137,7 @@ public class XiaomiCharacteristic {
|
||||
* the provided if we need to schedule something, otherwise it will be queued as other commands.
|
||||
*/
|
||||
public void write(final TransactionBuilder builder, final byte[] value) {
|
||||
write(builder, new Payload(builder.getTaskName(), value));
|
||||
write(builder, value, null);
|
||||
}
|
||||
|
||||
private void write(final TransactionBuilder builder, final Payload payload) {
|
||||
@ -134,17 +146,6 @@ public class XiaomiCharacteristic {
|
||||
}
|
||||
|
||||
public void onCharacteristicChanged(final byte[] value) {
|
||||
if (Arrays.equals(value, PAYLOAD_ACK)) {
|
||||
LOG.debug("Got ack");
|
||||
currentPayload = null;
|
||||
waitingAck = false;
|
||||
if (callback != null) {
|
||||
callback.onSend(payloadQueue.size());
|
||||
}
|
||||
sendNext(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final ByteBuffer buf = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
final int chunk = buf.getShort();
|
||||
@ -201,11 +202,11 @@ public class XiaomiCharacteristic {
|
||||
switch (subtype) {
|
||||
case 0:
|
||||
LOG.debug("Got chunked ack end");
|
||||
if (currentPayload != null && currentPayload.getCallback() != null) {
|
||||
currentPayload.getCallback().onSend();
|
||||
}
|
||||
currentPayload = null;
|
||||
sendingChunked = false;
|
||||
if (callback != null) {
|
||||
callback.onSend(payloadQueue.size());
|
||||
}
|
||||
sendNext(null);
|
||||
return;
|
||||
case 1:
|
||||
@ -226,17 +227,16 @@ public class XiaomiCharacteristic {
|
||||
return;
|
||||
case 2:
|
||||
LOG.warn("Got chunked nack for {}", currentPayload.getTaskName());
|
||||
if (currentPayload != null && currentPayload.getCallback() != null) {
|
||||
currentPayload.getCallback().onNack();
|
||||
}
|
||||
currentPayload = null;
|
||||
sendingChunked = false;
|
||||
if (callback != null) {
|
||||
callback.onSend(payloadQueue.size());
|
||||
}
|
||||
sendNext(null);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.warn("Unknown chunked ack subtype {} for {}", subtype, currentPayload.getTaskName());
|
||||
|
||||
return;
|
||||
case 2:
|
||||
// Single command
|
||||
@ -261,8 +261,28 @@ public class XiaomiCharacteristic {
|
||||
return;
|
||||
case 3:
|
||||
// ack
|
||||
LOG.debug("Got ack");
|
||||
final byte result = buf.get();
|
||||
|
||||
if (result == 0) {
|
||||
LOG.debug("Got ack for {}", currentPayload.getTaskName());
|
||||
|
||||
if (currentPayload != null && currentPayload.getCallback() != null) {
|
||||
currentPayload.getCallback().onSend();
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Got single cmd NACK ({}) for {}", result, currentPayload.getTaskName());
|
||||
|
||||
if (currentPayload != null && currentPayload.getCallback() != null) {
|
||||
currentPayload.getCallback().onNack();
|
||||
}
|
||||
}
|
||||
currentPayload = null;
|
||||
waitingAck = false;
|
||||
sendNext(null);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.warn("Unhandled command type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,10 +395,16 @@ public class XiaomiCharacteristic {
|
||||
|
||||
// Bytes that will actually be sent (might be encrypted)
|
||||
private byte[] bytesToSend;
|
||||
private final SendCallback callback;
|
||||
|
||||
public Payload(final String taskName, final byte[] bytes) {
|
||||
public Payload(final String taskName, final byte[] bytes, final SendCallback callback) {
|
||||
this.taskName = taskName;
|
||||
this.bytes = bytes;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public Payload(final String taskName, final byte[] bytes) {
|
||||
this(taskName, bytes, null);
|
||||
}
|
||||
|
||||
public String getTaskName() {
|
||||
@ -392,9 +418,11 @@ public class XiaomiCharacteristic {
|
||||
public byte[] getBytesToSend() {
|
||||
return bytesToSend != null ? bytesToSend : bytes;
|
||||
}
|
||||
public SendCallback getCallback() { return this.callback; }
|
||||
}
|
||||
|
||||
public interface SendCallback {
|
||||
void onSend(int remaining);
|
||||
void onSend();
|
||||
void onNack();
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +65,7 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
|
||||
|
||||
if (dataUploadAck.getUnknown2() != 0 || dataUploadAck.getResumePosition() != 0) {
|
||||
LOG.warn("Unexpected response");
|
||||
this.currentType = 0;
|
||||
this.currentBytes = null;
|
||||
onUploadFinish(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -76,6 +75,7 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
|
||||
chunkSize = 2048;
|
||||
}
|
||||
|
||||
LOG.debug("Using chunk size of {} bytes", chunkSize);
|
||||
doUpload(currentType, currentBytes);
|
||||
return;
|
||||
}
|
||||
@ -143,38 +143,35 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
|
||||
final int partSize = chunkSize - 4; // 2 + 2 at beginning of each for total and progress
|
||||
final int totalParts = (int) Math.ceil(payload.length / (float) partSize);
|
||||
|
||||
characteristic.setCallback(remainingParts -> {
|
||||
final int totalBytes = totalParts * 4 + payload.length;
|
||||
int progressBytes = totalParts * 4 + payload.length;
|
||||
if (remainingParts > 1) {
|
||||
progressBytes -= (remainingParts - 1) * partSize;
|
||||
}
|
||||
if (remainingParts > 0) {
|
||||
progressBytes -= (payload.length % partSize);
|
||||
}
|
||||
|
||||
final int progressPercent = Math.round((100.0f * progressBytes) / totalBytes);
|
||||
|
||||
LOG.debug("Data upload progress: {} parts remaining ({}%)", remainingParts, progressPercent);
|
||||
|
||||
if (remainingParts > 0) {
|
||||
if (callback != null) {
|
||||
callback.onUploadProgress(progressPercent);
|
||||
}
|
||||
} else {
|
||||
onUploadFinish(true);
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i * partSize < payload.length; i++) {
|
||||
final int currentPart = i + 1;
|
||||
final int startIndex = i * partSize;
|
||||
final int endIndex = Math.min((i + 1) * partSize, payload.length);
|
||||
LOG.debug("Uploading part {} of {}, from {} to {}", (i + 1), totalParts, startIndex, endIndex);
|
||||
final int endIndex = Math.min(currentPart * partSize, payload.length);
|
||||
LOG.debug("Uploading part {} of {}, from {} to {}", currentPart, totalParts, startIndex, endIndex);
|
||||
final byte[] chunkToSend = new byte[4 + endIndex - startIndex];
|
||||
BLETypeConversions.writeUint16(chunkToSend, 0, totalParts);
|
||||
BLETypeConversions.writeUint16(chunkToSend, 2, i + 1);
|
||||
BLETypeConversions.writeUint16(chunkToSend, 2, currentPart);
|
||||
System.arraycopy(payload, startIndex, chunkToSend, 4, endIndex - startIndex);
|
||||
characteristic.write("upload part " + (i + 1) + " of " + totalParts, chunkToSend);
|
||||
|
||||
characteristic.write("upload part " + currentPart + " of " + totalParts, chunkToSend, new XiaomiCharacteristic.SendCallback() {
|
||||
@Override
|
||||
public void onSend() {
|
||||
final int progressPercent = Math.round((100.0f * currentPart) / totalParts);
|
||||
LOG.debug("Data upload progress: {}/{} parts sent ({}% done)", currentPart, totalParts, progressPercent);
|
||||
|
||||
if (currentPart >= totalParts) {
|
||||
onUploadFinish(true);
|
||||
} else if (callback != null) {
|
||||
callback.onUploadProgress(progressPercent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNack() {
|
||||
LOG.warn("NACK received while uploading part {}/{}", currentPart, totalParts);
|
||||
// TODO callback.onUploadFinish(false); ?
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,8 +186,6 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
|
||||
if (callback != null) {
|
||||
callback.onUploadFinish(success);
|
||||
}
|
||||
|
||||
characteristic.setCallback(null);
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
|
Loading…
Reference in New Issue
Block a user