diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractBTDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractBTDeviceSupport.java
index abb3ca3d..ea209f2b 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractBTDeviceSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractBTDeviceSupport.java
@@ -150,6 +150,12 @@ public abstract class AbstractBTDeviceSupport extends AbstractDeviceSupport {
sendToDevice(bytes);
}
+ @Override
+ public void onSynchronizeActivityData() {
+ byte[] bytes = gbDeviceProtocol.encodeSynchronizeActivityData();
+ sendToDevice(bytes);
+ }
+
@Override
public void onReboot() {
byte[] bytes = gbDeviceProtocol.encodeReboot();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java
index ed69d603..a4efbbe2 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java
@@ -27,6 +27,8 @@ public interface EventHandler {
void onPhoneVersion(byte os);
+ void onSynchronizeActivityData();
+
void onReboot();
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBDevice.java
index 9ddbdb28..9e600963 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBDevice.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBDevice.java
@@ -36,6 +36,7 @@ public class GBDevice implements Parcelable {
private short mBatteryLevel = BATTERY_UNKNOWN;
private String mBatteryState;
private short mRssi = RSSI_UNKNOWN;
+ private String mBusyTask;
public GBDevice(String address, String name, DeviceType deviceType) {
mAddress = address;
@@ -54,6 +55,8 @@ public class GBDevice implements Parcelable {
mBatteryLevel = (short) in.readInt();
mBatteryState = in.readString();
mRssi = (short) in.readInt();
+ mBusyTask = in.readString();
+
validate();
}
@@ -68,6 +71,7 @@ public class GBDevice implements Parcelable {
dest.writeInt(mBatteryLevel);
dest.writeString(mBatteryState);
dest.writeInt(mRssi);
+ dest.writeString(mBusyTask);
}
private void validate() {
@@ -116,6 +120,42 @@ public class GBDevice implements Parcelable {
return mState == State.CONNECTING;
}
+ public boolean isBusy() {
+ return mBusyTask != null;
+ }
+
+ public String getBusyTask() {
+ return mBusyTask;
+ }
+
+ /**
+ * Marks the device as busy, performing a certain task. While busy, no other operations will
+ * be performed on the device.
+ *
+ * Note that nested busy tasks are not supported, every single call to #setBusyTask()
+ * or unsetBusy() has an effect.
+ * @param task a textual name of the task to be performed, possibly displayed to the user
+ */
+ public void setBusyTask(String task) {
+ if (task == null) {
+ throw new IllegalArgumentException("busy task must not be null");
+ }
+ if (mBusyTask != null) {
+ LOG.warn("Attempt to mark device as busy with: " + task + ", but is already busy with: " + mBusyTask);
+ }
+ mBusyTask = task;
+ }
+
+ /**
+ * Marks the device as not busy anymore.
+ */
+ public void unsetBusyTask() {
+ if (mBusyTask == null) {
+ LOG.error("Attempt to mark device as not busy anymore, but was not busy before.");
+ }
+ mBusyTask = null;
+ }
+
public State getState() {
return mState;
}
@@ -132,6 +172,7 @@ public class GBDevice implements Parcelable {
setBatteryState(null);
setFirmwareVersion(null);
setRssi(RSSI_UNKNOWN);
+ unsetBusyTask();
}
public String getStateString() {
@@ -249,5 +290,4 @@ public class GBDevice implements Parcelable {
INITIALIZING,
INITIALIZED
}
-
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/BtLEAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/BtLEAction.java
index 3d07647a..6985aa5a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/BtLEAction.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/BtLEAction.java
@@ -14,10 +14,6 @@ import android.bluetooth.BluetoothGattCharacteristic;
public abstract class BtLEAction {
private final BluetoothGattCharacteristic characteristic;
- public BtLEAction() {
- this(null);
- }
-
public BtLEAction(BluetoothGattCharacteristic characteristic) {
this.characteristic = characteristic;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java
index eafdf750..3ecd7bb1 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java
@@ -12,7 +12,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBDevice;
* sequence (transaction). It will abort the entire initialization sequence
* by returning false, when the device is already initialized.
*/
-public class CheckInitializedAction extends BtLEAction {
+public class CheckInitializedAction extends PlainAction {
private static final Logger LOG = LoggerFactory.getLogger(CheckInitializedAction.class);
private final GBDevice device;
@@ -29,9 +29,4 @@ public class CheckInitializedAction extends BtLEAction {
}
return continueWithOtherInitActions;
}
-
- @Override
- public boolean expectsResult() {
- return false;
- }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/PlainAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/PlainAction.java
new file mode 100644
index 00000000..d5455fed
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/PlainAction.java
@@ -0,0 +1,19 @@
+package nodomain.freeyourgadget.gadgetbridge.btle;
+
+import android.bluetooth.BluetoothGatt;
+
+/**
+ * An abstract non-BTLE action. It performs no bluetooth operation,
+ * does not have a BluetoothGattCharacteristic instance and expects no result.
+ */
+public abstract class PlainAction extends BtLEAction {
+
+ public PlainAction() {
+ super(null);
+ }
+
+ @Override
+ public boolean expectsResult() {
+ return false;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/SetDeviceBusyAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/SetDeviceBusyAction.java
new file mode 100644
index 00000000..05f68013
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/SetDeviceBusyAction.java
@@ -0,0 +1,36 @@
+package nodomain.freeyourgadget.gadgetbridge.btle;
+
+import android.bluetooth.BluetoothGatt;
+import android.content.Context;
+
+import nodomain.freeyourgadget.gadgetbridge.GBDevice;
+
+public class SetDeviceBusyAction extends PlainAction {
+ private final GBDevice device;
+ private final Context context;
+ private final String busyTask;
+
+ /**
+ * When run, will mark the device as busy (or not busy).
+ * @param device the device to mark
+ * @param busyTask the task name to set as busy task, or null to mark as not busy
+ * @param context
+ */
+ public SetDeviceBusyAction(GBDevice device, String busyTask, Context context) {
+ this.device = device;
+ this.busyTask = busyTask;
+ this.context = context;
+ }
+
+ @Override
+ public boolean run(BluetoothGatt gatt) {
+ device.setBusyTask(busyTask);
+ device.sendDeviceUpdateIntent(context);
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + ": " + busyTask;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/WaitAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/WaitAction.java
index f8e4484d..29fb412e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/WaitAction.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/WaitAction.java
@@ -2,7 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.btle;
import android.bluetooth.BluetoothGatt;
-public class WaitAction extends BtLEAction {
+public class WaitAction extends PlainAction {
private int mMillis;
@@ -19,10 +19,4 @@ public class WaitAction extends BtLEAction {
return false;
}
}
-
- @Override
- public boolean expectsResult() {
- // no BT communication at all, no result
- return false;
- }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java
index 633be8ea..a181c692 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java
@@ -132,12 +132,13 @@ public class MiBandService {
public static final byte COMMAND_STOP_MOTOR_VIBRATE = 0x13;
public static final byte COMMAND_CONFIRM_ACTIVITY_DATA_TRANSFER_COMPLETE = 0xa;
+
+ public static final byte COMMAND_FETCH_DATA = 0x6;
/*
public static final byte COMMAND_FACTORY_RESET = 0x9t;
- public static final byte COMMAND_FETCH_DATA = 0x6t;
public static final byte COMMAND_GET_SENSOR_DATA = 0x12t
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java
index 08b461cb..e4e1c925 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java
@@ -20,7 +20,9 @@ import nodomain.freeyourgadget.gadgetbridge.GBActivitySample;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBCommand;
import nodomain.freeyourgadget.gadgetbridge.GBDevice.State;
+import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.btle.AbstractBTLEDeviceSupport;
+import nodomain.freeyourgadget.gadgetbridge.btle.SetDeviceBusyAction;
import nodomain.freeyourgadget.gadgetbridge.btle.TransactionBuilder;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR;
@@ -147,10 +149,10 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
builder.queue(getQueue());
}
- private static final byte[] startVibrate = new byte[]{MiBandService.COMMAND_SEND_NOTIFICATION, 1};
- private static final byte[] stopVibrate = new byte[]{MiBandService.COMMAND_STOP_MOTOR_VIBRATE};
- private static final byte[] reboot = new byte[]{MiBandService.COMMAND_REBOOT};
- private static final byte[] fetch = new byte[]{6};
+ private static final byte[] startVibrate = new byte[] { MiBandService.COMMAND_SEND_NOTIFICATION, 1 };
+ private static final byte[] stopVibrate = new byte[] { MiBandService.COMMAND_STOP_MOTOR_VIBRATE };
+ private static final byte[] reboot = new byte[]{ MiBandService.COMMAND_REBOOT };
+ private static final byte[] fetch = new byte[]{ MiBandService.COMMAND_FETCH_DATA };
private byte[] getNotification(long vibrateDuration, int vibrateTimes, int flashTimes, int flashColour, int originalColour, long flashDuration) {
byte[] vibrate = new byte[]{MiBandService.COMMAND_SEND_NOTIFICATION, (byte) 1};
@@ -357,8 +359,20 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
@Override
public void onReboot() {
+ try {
+ TransactionBuilder builder = performInitialized("Reboot");
+ builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), reboot);
+ builder.queue(getQueue());
+ } catch (IOException ex) {
+ LOG.error("Unable to reboot MI", ex);
+ }
+ }
+
+ @Override
+ public void onSynchronizeActivityData() {
try {
TransactionBuilder builder = performInitialized("fetch activity data");
+ builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.busy_task_fetch_activity_data), getContext()));
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), fetch);
builder.queue(getQueue());
} catch (IOException ex) {
@@ -487,7 +501,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
flushActivityDataHolder();
}
} else {
- // the lenght of the chunk is not what we expect. We need to make sense of this data
+ // the length of the chunk is not what we expect. We need to make sense of this data
LOG.warn("GOT UNEXPECTED ACTIVITY DATA WITH LENGTH: " + value.length + ", EXPECTED LENGTH: " + this.activityDataRemainingBytes);
for (byte b: value){
LOG.warn("DATA: " + String.format("0x%8x", b));
@@ -530,11 +544,31 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
LOG.warn("Could not write to the control point.");
}
LOG.info("handleControlPoint got status:" + status);
+
+ if (getDevice().isBusy()) {
+ if (isActivityDataSyncFinished(value)) {
+ unsetBusy();
+ }
+ }
for (byte b: value){
LOG.info("handleControlPoint GOT DATA:" + String.format("0x%8x", b));
}
-
}
+
+ private boolean isActivityDataSyncFinished(byte[] value) {
+ if (value.length == 9) {
+ if (value[0] == 0xa && value[1] == 0xf && value[2] == 5 && value[7] == 0 && value[8] == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void unsetBusy() {
+ getDevice().unsetBusyTask();
+ getDevice().sendDeviceUpdateIntent(getContext());
+ }
+
private void sendAckDataTransfer(Calendar time, int bytesTransferred) {
byte[] ack = new byte[]{
MiBandService.COMMAND_CONFIRM_ACTIVITY_DATA_TRANSFER_COMPLETE,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/SetDeviceStateAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/SetDeviceStateAction.java
index 673e2a46..194d9e54 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/SetDeviceStateAction.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/SetDeviceStateAction.java
@@ -5,8 +5,9 @@ import android.content.Context;
import nodomain.freeyourgadget.gadgetbridge.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.btle.BtLEAction;
+import nodomain.freeyourgadget.gadgetbridge.btle.PlainAction;
-public class SetDeviceStateAction extends BtLEAction {
+public class SetDeviceStateAction extends PlainAction {
private final GBDevice device;
private final GBDevice.State deviceState;
private final Context context;
@@ -24,11 +25,6 @@ public class SetDeviceStateAction extends BtLEAction {
return true;
}
- @Override
- public boolean expectsResult() {
- return false;
- }
-
public Context getContext() {
return context;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceProtocol.java
index 8bc295f5..f91e777c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceProtocol.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceProtocol.java
@@ -54,6 +54,10 @@ public abstract class GBDeviceProtocol {
return null;
}
+ public byte[] encodeSynchronizeActivityData() {
+ return null;
+ }
+
public byte[] encodeReboot() {
return null;
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 52d30e88..70025e0c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -105,5 +105,6 @@
LogToFile
Write Log Files (needs restart)
initializing
+ Fetching Activity Data