mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-24 19:06:53 +01:00
Huami devices: Fix seldom fetch failures (better support for app level ble feedback)
Fixes #1264
This commit is contained in:
parent
b6d68207cb
commit
d6f5e36e12
@ -108,6 +108,16 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLEDeviceSupport>
|
||||
return builder;
|
||||
}
|
||||
|
||||
public TransactionBuilder createTransactionBuilder(String taskName) {
|
||||
TransactionBuilder builder = getSupport().createTransactionBuilder(taskName);
|
||||
builder.setGattCallback(this);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public void performImmediately(TransactionBuilder builder) throws IOException {
|
||||
mSupport.performImmediately(builder);
|
||||
}
|
||||
|
||||
protected Context getContext() {
|
||||
return mSupport.getContext();
|
||||
}
|
||||
|
@ -107,6 +107,11 @@ public final class BtLEQueue {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("About to run action: " + action);
|
||||
}
|
||||
if (action instanceof GattListenerAction) {
|
||||
// this special action overwrites the transaction gatt listener (if any), it must
|
||||
// always be the last action in the transaction
|
||||
internalGattCallback.setTransactionGattCallback(((GattListenerAction)action).getGattCallback());
|
||||
}
|
||||
if (action.run(mBluetoothGatt)) {
|
||||
// check again, maybe due to some condition, action did not need to write, so we can't wait
|
||||
boolean waitForResult = action.expectsResult();
|
||||
|
@ -0,0 +1,5 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
||||
|
||||
public interface GattListenerAction {
|
||||
GattCallback getGattCallback();
|
||||
}
|
@ -110,4 +110,8 @@ public class TransactionBuilder {
|
||||
public Transaction getTransaction() {
|
||||
return mTransaction;
|
||||
}
|
||||
|
||||
public String getTaskName() {
|
||||
return mTransaction.getTaskName();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractGattCallback;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattListenerAction;
|
||||
|
||||
public abstract class AbstractGattListenerWriteAction extends WriteAction implements GattListenerAction {
|
||||
private final BtLEQueue queue;
|
||||
|
||||
public AbstractGattListenerWriteAction(BtLEQueue queue, BluetoothGattCharacteristic characteristic, byte[] value) {
|
||||
super(characteristic, value);
|
||||
this.queue = queue;
|
||||
Objects.requireNonNull(queue, "queue must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public GattCallback getGattCallback() {
|
||||
return new AbstractGattCallback() {
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
return AbstractGattListenerWriteAction.this.onCharacteristicChanged(gatt, characteristic);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
|
||||
}
|
@ -18,6 +18,11 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
|
||||
/**
|
||||
* An action that will cause the queue to sleep for the specified time.
|
||||
* Note that this is usually a bad idea, since it will not be able to process messages
|
||||
* during that time. It is also likely to cause race conditions.
|
||||
*/
|
||||
public class WaitAction extends PlainAction {
|
||||
|
||||
private final int mMillis;
|
||||
|
@ -16,6 +16,8 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.widget.Toast;
|
||||
|
||||
@ -30,17 +32,21 @@ import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbstractGattListenerWriteAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.AbstractFetchOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipFetchLogsOperation.class);
|
||||
@ -72,15 +78,38 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
|
||||
return;
|
||||
}
|
||||
|
||||
final String taskName = StringUtils.ensureNotNull(builder.getTaskName());
|
||||
GregorianCalendar sinceWhen = BLETypeConversions.createCalendar();
|
||||
sinceWhen.add(Calendar.DAY_OF_MONTH, -10);
|
||||
builder.write(characteristicFetch, BLETypeConversions.join(new byte[]{
|
||||
byte[] fetchBytes = BLETypeConversions.join(new byte[]{
|
||||
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
|
||||
AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_DEBUGLOGS},
|
||||
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
|
||||
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
|
||||
builder.notify(characteristicActivityData, true);
|
||||
builder.write(characteristicFetch, new byte[]{HuamiService.COMMAND_FETCH_DATA});
|
||||
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES));
|
||||
builder.add(new AbstractGattListenerWriteAction(getQueue(), characteristicFetch, fetchBytes) {
|
||||
@Override
|
||||
protected boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (HuamiService.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) {
|
||||
byte[] value = characteristic.getValue();
|
||||
|
||||
if (ArrayUtils.equals(value, HuamiService.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) {
|
||||
handleActivityMetadata(value);
|
||||
TransactionBuilder newBuilder = createTransactionBuilder(taskName + " Step 2");
|
||||
newBuilder.notify(characteristicActivityData, true);
|
||||
newBuilder.write(characteristicFetch, new byte[]{HuamiService.COMMAND_FETCH_DATA});
|
||||
try {
|
||||
performImmediately(newBuilder);
|
||||
} catch (IOException ex) {
|
||||
GB.toast(getContext(), "Error fetching debug logs: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
handleActivityMetadata(value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,6 +16,8 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.text.format.DateUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
@ -24,9 +26,11 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
@ -34,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
@ -41,10 +46,14 @@ import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbstractGattListenerWriteAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An operation that fetches activity data. For every fetch, a new operation must
|
||||
@ -68,11 +77,34 @@ public class FetchActivityOperation extends AbstractFetchOperation {
|
||||
|
||||
@Override
|
||||
protected void startFetching(TransactionBuilder builder) {
|
||||
final String taskName = StringUtils.ensureNotNull(builder.getTaskName());
|
||||
GregorianCalendar sinceWhen = getLastSuccessfulSyncTime();
|
||||
builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { HuamiService.COMMAND_ACTIVITY_DATA_START_DATE, HuamiService.COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
|
||||
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
|
||||
builder.notify(characteristicActivityData, true);
|
||||
builder.write(characteristicFetch, new byte[] { HuamiService.COMMAND_FETCH_DATA});
|
||||
byte[] fetchBytes = BLETypeConversions.join(new byte[] { HuamiService.COMMAND_ACTIVITY_DATA_START_DATE, HuamiService.COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES));
|
||||
builder.add(new AbstractGattListenerWriteAction(getQueue(), characteristicFetch, fetchBytes) {
|
||||
@Override
|
||||
protected boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (HuamiService.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) {
|
||||
byte[] value = characteristic.getValue();
|
||||
|
||||
if (ArrayUtils.equals(value, HuamiService.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) {
|
||||
handleActivityMetadata(value);
|
||||
TransactionBuilder newBuilder = createTransactionBuilder(taskName + " Step 2");
|
||||
newBuilder.notify(characteristicActivityData, true);
|
||||
newBuilder.write(characteristicFetch, new byte[] { HuamiService.COMMAND_FETCH_DATA});
|
||||
try {
|
||||
performImmediately(newBuilder);
|
||||
} catch (IOException ex) {
|
||||
GB.toast(getContext(), "Error fetching activity data: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
handleActivityMetadata(value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void handleActivityFetchFinish(boolean success) {
|
||||
|
@ -137,12 +137,6 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
|
||||
}
|
||||
}
|
||||
|
||||
private TransactionBuilder createTransactionBuilder(String task) {
|
||||
TransactionBuilder builder = getSupport().createTransactionBuilder(task);
|
||||
builder.setGattCallback(this);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private byte[] getMD5(byte[] message) throws NoSuchAlgorithmException {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
return md5.digest(message);
|
||||
|
Loading…
Reference in New Issue
Block a user