Merge branch 'master' into background-javascript

This commit is contained in:
Andreas Shimokawa 2017-03-08 10:24:31 +01:00
commit 942984dec2
19 changed files with 253 additions and 352 deletions

View File

@ -152,6 +152,7 @@ Feel free to open an issue on our issue tracker, but please:
## Having problems?
0. Phone crashing during device discovery? Disable Privacy Guard (or similarly named functionality) during discovery.
1. Open Gadgetbridge's settings and check the option to write log files
2. Reproduce the problem you encountered
3. Check the logfile at /sdcard/Android/data/nodomain.freeyourgadget.gadgetbridge/files/gadgetbridge.log

View File

@ -60,12 +60,12 @@ dependencies {
// testCompile 'ch.qos.logback:logback-core:1.1.3'
testCompile 'junit:junit:4.12'
testCompile "org.mockito:mockito-core:1.9.5"
testCompile "org.robolectric:robolectric:3.1.2"
testCompile "org.robolectric:robolectric:3.2.2"
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.1.1'
compile 'com.android.support:support-v4:25.1.1'
compile 'com.android.support:design:25.1.1'
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support:support-v4:25.2.0'
compile 'com.android.support:design:25.2.0'
compile 'com.github.tony19:logback-android-classic:1.1.1-4'
compile 'org.slf4j:slf4j-api:1.7.7'
compile 'com.github.PhilJay:MPAndroidChart:v3.0.1'

View File

@ -89,6 +89,26 @@
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
<data android:pathPattern="/.*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\.ft" />
<data android:pathPattern="/.*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\.pbw" />
<data android:pathPattern="/.*\\..*\\.pbw" />
<data android:pathPattern="/.*\\..*\\..*\\.pbw" />
@ -142,6 +162,26 @@
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
<data android:pathPattern="/.*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
<data android:pathPattern="/.*\\.ft" />
<data android:pathPattern="/.*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
<data android:pathPattern="/.*\\.pbw" />
<data android:pathPattern="/.*\\..*\\.pbw" />
<data android:pathPattern="/.*\\..*\\..*\\.pbw" />

View File

@ -109,8 +109,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
if (device != null && device.getAddress().equals(bondingAddress)) {
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
if (bondState == BluetoothDevice.BOND_BONDED) {
GB.toast(DiscoveryActivity.this, "Successfully bonded with: " + bondingAddress, Toast.LENGTH_SHORT, GB.INFO);
finish();
handleDeviceBonded();
}
}
}
@ -118,6 +117,11 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
}
};
private void handleDeviceBonded() {
GB.toast(DiscoveryActivity.this, "Successfully bonded with: " + bondingAddress, Toast.LENGTH_SHORT, GB.INFO);
finish();
}
private final BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
@ -516,9 +520,21 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
} else {
try {
BluetoothDevice btDevice = adapter.getRemoteDevice(deviceCandidate.getMacAddress());
if (btDevice.createBond()) {
// async, wait for bonding event to finish this activity
bondingAddress = btDevice.getAddress();
switch (btDevice.getBondState()) {
case BluetoothDevice.BOND_NONE: {
if (btDevice.createBond()) {
// async, wait for bonding event to finish this activity
bondingAddress = btDevice.getAddress();
}
break;
}
case BluetoothDevice.BOND_BONDING:
// async, wait for bonding event to finish this activity
bondingAddress = btDevice.getAddress();
break;
case BluetoothDevice.BOND_BONDED:
handleDeviceBonded();
break;
}
} catch (Exception e) {
LOG.error("Error pairing device: " + deviceCandidate.getMacAddress());

View File

@ -94,12 +94,16 @@ public class MiBand2Service {
public static final byte[] COMMAND_SET_FITNESS_GOAL_END = new byte[] { 0, 0 };
public static byte COMMAND_DATEFORMAT = 0x06;
public static byte ENDPOINT_DISPLAY = 0x06;
public static final byte[] DATEFORMAT_DATE_TIME = new byte[] { COMMAND_DATEFORMAT, 0x0a, 0x0, 0x03 };
public static final byte[] DATEFORMAT_TIME = new byte[] { COMMAND_DATEFORMAT, 0x0a, 0x0, 0x0 };
public static final byte[] DATEFORMAT_TIME_12_HOURS = new byte[] { COMMAND_DATEFORMAT, 0x02, 0x0, 0x0 };
public static final byte[] DATEFORMAT_TIME_24_HOURS = new byte[] { COMMAND_DATEFORMAT, 0x02, 0x0, 0x1 };
public static final byte[] DATEFORMAT_DATE_TIME = new byte[] {ENDPOINT_DISPLAY, 0x0a, 0x0, 0x03 };
public static final byte[] DATEFORMAT_TIME = new byte[] {ENDPOINT_DISPLAY, 0x0a, 0x0, 0x0 };
public static final byte[] DATEFORMAT_TIME_12_HOURS = new byte[] {ENDPOINT_DISPLAY, 0x02, 0x0, 0x0 };
public static final byte[] DATEFORMAT_TIME_24_HOURS = new byte[] {ENDPOINT_DISPLAY, 0x02, 0x0, 0x1 };
public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01};
public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x00};
public static final byte[] DISPLAY_XXX = new byte[] {ENDPOINT_DISPLAY, 0x03, 0x0, 0x0 };
public static final byte[] DISPLAY_YYY = new byte[] {ENDPOINT_DISPLAY, 0x10, 0x0, 0x1, 0x0 };
public static final byte RESPONSE = 0x10;
@ -111,14 +115,14 @@ public class MiBand2Service {
public static final byte COMMAND_FIRMWARE_START_DATA = 0x03; // to UUID_CHARACTERISTIC_FIRMWARE
public static final byte COMMAND_FIRMWARE_UPDATE_SYNC = 0x00; // to UUID_CHARACTERISTIC_FIRMWARE
public static final byte COMMAND_FIRMWARE_CHECKSUM = 0x04; // to UUID_CHARACTERISTIC_FIRMWARE
public static final byte COMMAND_FIRMWARE_APPLY_REBOOT = 0x05; // or is it REBOOT? to UUID_CHARACTERISTIC_FIRMWARE
public static final byte COMMAND_FIRMWARE_REBOOT = 0x05; // to UUID_CHARACTERISTIC_FIRMWARE
public static final byte[] RESPONSE_FINISH_SUCCESS = new byte[] {RESPONSE, 2, SUCCESS };
public static final byte[] RESPONSE_FIRMWARE_DATA_SUCCESS = new byte[] {RESPONSE, COMMAND_FIRMWARE_START_DATA, SUCCESS };
/**
* Received in response to any dateformat configuration request (byte 0 in the byte[] value.
*/
public static final byte[] RESPONSE_DATEFORMAT_SUCCESS = new byte[] { RESPONSE, COMMAND_DATEFORMAT, 0x0a, 0x0, 0x01 };
public static final byte[] RESPONSE_DATEFORMAT_SUCCESS = new byte[] { RESPONSE, ENDPOINT_DISPLAY, 0x0a, 0x0, 0x01 };
public static final byte[] RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS = new byte[] { RESPONSE, COMMAND_ACTIVITY_DATA_START_DATE, SUCCESS};
public static final byte[] WEAR_LOCATION_LEFT_WRIST = new byte[] { 0x20, 0x00, 0x00, 0x02 };
@ -127,34 +131,13 @@ public class MiBand2Service {
public static final byte[] COMMAND_ENABLE_HR_SLEEP_MEASUREMENT = new byte[]{0x15, 0x00, 0x01};
public static final byte[] COMMAND_DISABLE_HR_SLEEP_MEASUREMENT = new byte[]{0x15, 0x00, 0x00};
public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{0x06, 0x05, 0x00, 0x01};
public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{0x06, 0x05, 0x00, 0x00};
public static final byte[] COMMAND_TEXT_NOTIFICATION = new byte[] {0x05, 0x01};
public static final byte[] COMMAND_TEXT_NOTIFICATION_CONTINUATION = new byte[] {(byte) 0xfa, 0x01, 0x00};
public static final byte COMMAND_ALERT_CATEGORY_CHAT = (byte) 0xfa;
static {
MIBAND_DEBUG = new HashMap<>();
MIBAND_DEBUG.put(UUID_SERVICE_MIBAND_SERVICE, "MiBand Service");
MIBAND_DEBUG.put(UUID_SERVICE_HEART_RATE, "MiBand HR Service");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_INFO, "Device Info");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_NAME, "Device Name");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_NOTIFICATION, "Notification");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_USER_INFO, "User Info");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_CONTROL_POINT, "Control Point");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_REALTIME_STEPS, "Realtime Steps");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_ACTIVITY_DATA, "Activity Data");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_FIRMWARE_DATA, "Firmware Data");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_LE_PARAMS, "LE Params");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DATE_TIME, "Date/Time");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_STATISTICS, "Statistics");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_BATTERY, "Battery");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_TEST, "Test");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_SENSOR_DATA, "Sensor Data");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_PAIR, "Pair");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT, "Heart Rate Control Point");
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT, "Heart Rate Measure");
}
public static String lookup(UUID uuid, String fallback) {

View File

@ -4,6 +4,9 @@ import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
public class MiBandService {
@ -14,7 +17,7 @@ public class MiBandService {
public static final UUID UUID_SERVICE_MIBAND_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE0"));
public static final UUID UUID_SERVICE_MIBAND2_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE1"));
public static final UUID UUID_SERVICE_HEART_RATE = UUID.fromString(String.format(BASE_UUID, "180D"));
public static final UUID UUID_SERVICE_HEART_RATE = GattService.UUID_SERVICE_HEART_RATE;
public static final String UUID_SERVICE_WEIGHT_SERVICE = "00001530-0000-3512-2118-0009af100700";
public static final UUID UUID_CHARACTERISTIC_DEVICE_INFO = UUID.fromString(String.format(BASE_UUID, "FF01"));
@ -47,8 +50,8 @@ public class MiBandService {
public static final UUID UUID_CHARACTERISTIC_PAIR = UUID.fromString(String.format(BASE_UUID, "FF0F"));
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT = UUID.fromString(String.format(BASE_UUID, "2A39"));
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT = UUID.fromString(String.format(BASE_UUID, "2A37"));
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT = GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT;
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT = GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT;

View File

@ -53,7 +53,7 @@ public class V2NotificationStrategy implements NotificationStrategy {
}
}
}
sendAlert(simpleNotification, builder);
// sendAlert(simpleNotification, builder);
}
protected void sendAlert(SimpleNotification simpleNotification, TransactionBuilder builder) {

View File

@ -0,0 +1,19 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2;
public enum FirmwareType {
FIRMWARE((byte) 0),
FONT((byte) 1),
UNKNOWN1((byte) 2),
UNKNOWN2((byte) 3),
INVALID(Byte.MIN_VALUE);
private final byte value;
FirmwareType(byte value) {
this.value = value;
}
public byte getValue() {
return value;
}
}

View File

@ -27,13 +27,23 @@ public class Mi2FirmwareInfo {
(byte) 0xf3,
(byte) 0xe7,
};
private static final int FW_HEADER_OFFSET = 0x150;
private static final byte[] FT_HEADER = new byte[] { // HMZK font file (*.ft, *.ft.xx)
0x48,
0x4d,
0x5a,
0x4b
};
private static Map<Integer,String> crcToVersion = new HashMap<>();
static {
crcToVersion.put(41899, "1.0.0.39");
}
private FirmwareType firmwareType = FirmwareType.FIRMWARE;
public static String toVersion(int crc16) {
return crcToVersion.get(crc16);
}
@ -51,6 +61,18 @@ public class Mi2FirmwareInfo {
this.bytes = bytes;
crc16 = CheckSums.getCRC16(bytes);
firmwareVersion = crcToVersion.get(crc16);
firmwareType = determineFirmwareType(bytes);
}
private FirmwareType determineFirmwareType(byte[] bytes) {
if (ArrayUtils.startsWith(bytes, FT_HEADER)) {
return FirmwareType.FONT;
}
if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET)) {
// TODO: this is certainly not a correct validation, but it works for now
return FirmwareType.FIRMWARE;
}
return FirmwareType.INVALID;
}
public boolean isGenerallyCompatibleWith(GBDevice device) {
@ -58,8 +80,7 @@ public class Mi2FirmwareInfo {
}
public boolean isHeaderValid() {
// TODO: this is certainly not a correct validation, but it works for now
return ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET);
return getFirmwareType() != FirmwareType.INVALID;
}
public void checkValid() throws IllegalArgumentException {
@ -84,4 +105,8 @@ public class Mi2FirmwareInfo {
public int getFirmwareVersion() {
return getCrc16(); // HACK until we know how to determine the version from the fw bytes
}
public FirmwareType getFirmwareType() {
return firmwareType;
}
}

View File

@ -41,7 +41,7 @@ public class Mi2NotificationStrategy extends V2NotificationStrategy {
}
}
sendAlert(simpleNotification, builder);
}
@Override

View File

@ -17,7 +17,6 @@ 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;
@ -37,7 +36,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2SampleProvider
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
@ -45,7 +43,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
@ -67,20 +64,16 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile;
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.CheckAuthenticationNeededAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions.StopNotificationAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.FetchActivityOperation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -123,8 +116,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
private volatile boolean telephoneRinging;
private volatile boolean isLocatingDevice;
private DeviceInfo mDeviceInfo;
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
private RealtimeSamplesSupport realtimeSamplesSupport;
@ -171,26 +162,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
} catch (IOException e) {
GB.toast(getContext(), "Initializing Mi Band 2 failed", Toast.LENGTH_SHORT, GB.ERROR, e);
}
// builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
// enableNotifications(builder, true)
// .setLowLatency(builder)
// .readDate(builder) // without reading the data, we get sporadic connection problems, especially directly after turning on BT
// this is apparently not needed anymore, and actually causes problems when bonding is not used/does not work
// so we simply not use the UUID_PAIR characteristic.
// .pair(builder)
//.requestDeviceInfo(builder)
//.requestBatteryInfo(builder);
// .sendUserInfo(builder)
// .checkAuthenticationNeeded(builder, getDevice())
// .setWearLocation(builder)
// .setHeartrateSleepSupport(builder)
// .setFitnessGoal(builder)
// .enableFurtherNotifications(builder, true)
// .setCurrentTime(builder)
// .requestBatteryInfo(builder)
// .setHighLatency(builder)
// .setInitialized(builder);
return builder;
}
@ -226,27 +197,13 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
return this;
}
private MiBand2Support readDate(TransactionBuilder builder) {
// NAVL
// builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_DATE_TIME));
// TODO: handle result
builder.read(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME));
return this;
}
// NAVL
public MiBand2Support setLowLatency(TransactionBuilder builder) {
// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getLowLatency());
return this;
}
// NAVL
public MiBand2Support setHighLatency(TransactionBuilder builder) {
// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getHighLatency());
// TODO: low latency?
return this;
}
private MiBand2Support checkAuthenticationNeeded(TransactionBuilder builder, GBDevice device) {
builder.add(new CheckAuthenticationNeededAction(device));
public MiBand2Support setHighLatency(TransactionBuilder builder) {
// TODO: high latency?
return this;
}
@ -272,13 +229,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
}
public MiBand2Support enableFurtherNotifications(TransactionBuilder builder, boolean enable) {
// builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable)
// .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable)
// .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable);
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), enable);
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO), enable);
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON), enable);
BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT);
BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT);
if (heartrateCharacteristic != null) {
builder.notify(heartrateCharacteristic, enable);
}
@ -301,10 +255,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
}
}
public DeviceInfo getDeviceInfo() {
return mDeviceInfo;
}
private MiBand2Support sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) {
LOG.info("Sending notification to MiBand: (" + repeat + " times)");
NotificationStrategy strategy = getNotificationStrategy();
@ -335,44 +285,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
return new Mi2NotificationStrategy(this);
}
static final byte[] reboot = new byte[]{MiBandService.COMMAND_REBOOT};
static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1};
static final byte[] stopHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 0};
static final byte[] startHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 1};
static final byte[] stopHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 0};
static final byte[] startHeartMeasurementSleep = new byte[]{0x15, MiBandService.COMMAND_SET_HR_SLEEP, 1};
static final byte[] stopHeartMeasurementSleep = new byte[]{0x15, MiBandService.COMMAND_SET_HR_SLEEP, 0};
static final byte[] startRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 1};
static final byte[] stopRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 0};
/**
* Part of device initialization process. Do not call manually.
*
* @param builder
* @return
*/
private MiBand2Support sendUserInfo(TransactionBuilder builder) {
LOG.debug("Writing User Info!");
// Use a custom action instead of just builder.write() because mDeviceInfo
// is set by handleDeviceInfo *after* this action is created.
builder.add(new BtLEAction(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_USER_INFO)) {
@Override
public boolean expectsResult() {
return true;
}
@Override
public boolean run(BluetoothGatt gatt) {
// at this point, mDeviceInfo should be set
return new WriteAction(getCharacteristic(),
MiBandCoordinator.getAnyUserInfo(getDevice().getAddress()).getData(mDeviceInfo)
).run(gatt);
}
});
return this;
}
private static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1};
private static final byte[] stopHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 0};
private static final byte[] startHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 1};
private static final byte[] stopHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 0};
private MiBand2Support requestBatteryInfo(TransactionBuilder builder) {
LOG.debug("Requesting Battery Info!");
@ -387,31 +303,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
return this;
}
/* private MiBandSupport requestHRInfo(TransactionBuilder builder) {
LOG.debug("Requesting HR Info!");
BluetoothGattCharacteristic HRInfo = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT);
builder.read(HRInfo);
BluetoothGattCharacteristic HR_Point = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT);
builder.read(HR_Point);
return this;
}
*//**
* Part of HR test. Do not call manually.
*
* @param transaction
* @return
*//*
private MiBandSupport heartrate(TransactionBuilder transaction) {
LOG.info("Attempting to read HR ...");
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT);
if (characteristic != null) {
transaction.write(characteristic, new byte[]{MiBandService.COMMAND_SET__HR_CONTINUOUS});
} else {
LOG.info("Unable to read HR from MI device -- characteristic not available");
}
return this;
}*/
/**
* Part of device initialization process. Do not call manually.
*
@ -684,7 +575,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
public void onReboot() {
try {
TransactionBuilder builder = performInitialized("Reboot");
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), reboot);
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_FIRMWARE), new byte[] { MiBand2Service.COMMAND_FIRMWARE_REBOOT});
builder.queue(getQueue());
} catch (IOException ex) {
LOG.error("Unable to reboot MI", ex);
@ -695,9 +586,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
public void onHeartRateTest() {
try {
TransactionBuilder builder = performInitialized("HeartRateTest");
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual);
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual);
builder.queue(getQueue());
} catch (IOException ex) {
LOG.error("Unable to read HearRate with MI2", ex);
@ -709,8 +600,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
try {
TransactionBuilder builder = performInitialized("Enable realtime heart rateM measurement");
if (enable) {
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous);
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous);
} else {
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
}
@ -753,19 +644,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
@Override
public void onEnableRealtimeSteps(boolean enable) {
// try {
// BluetoothGattCharacteristic controlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
// if (enable) {
// TransactionBuilder builder = performInitialized("Read realtime steps");
// builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS)).queue(getQueue());
// }
// performInitialized(enable ? "Enabling realtime steps notifications" : "Disabling realtime steps notifications")
// .write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), enable ? getLowLatency() : getHighLatency())
// .write(controlPoint, enable ? startRealTimeStepsNotifications : stopRealTimeStepsNotifications).queue(getQueue());
// enableRealtimeSamplesTimer(enable);
// } catch (IOException e) {
// LOG.error("Unable to change realtime steps notification to: " + enable, e);
// }
}
private byte[] getHighLatency() {
@ -854,13 +732,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
if (MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
handleBatteryInfo(characteristic.getValue(), BluetoothGatt.GATT_SUCCESS);
return true;
} else if (MiBandService.UUID_CHARACTERISTIC_NOTIFICATION.equals(characteristicUUID)) {
handleNotificationNotif(characteristic.getValue());
return true;
} else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) {
handleRealtimeSteps(characteristic.getValue());
return true;
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
} else if (GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
handleHeartrate(characteristic.getValue());
return true;
// } else if (MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0.equals(characteristicUUID)) {
@ -901,12 +776,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
} else if (MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
handleBatteryInfo(characteristic.getValue(), status);
return true;
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
} else if (GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
logHeartrate(characteristic.getValue(), status);
return true;
} else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) {
logDate(characteristic.getValue(), status);
return true;
} else if (MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON.equals(characteristicUUID)) {
handleButtonPressed(characteristic.getValue());
return true;
@ -921,16 +793,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
public boolean onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
UUID characteristicUUID = characteristic.getUuid();
if (MiBandService.UUID_CHARACTERISTIC_PAIR.equals(characteristicUUID)) {
handlePairResult(characteristic.getValue(), status);
return true;
} else if (MiBandService.UUID_CHARACTERISTIC_USER_INFO.equals(characteristicUUID)) {
handleUserInfoResult(characteristic.getValue(), status);
return true;
} else if (MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT.equals(characteristicUUID)) {
handleControlPointResult(characteristic.getValue(), status);
return true;
} else if (MiBand2Service.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
if (MiBand2Service.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
LOG.info("KEY AES SEND");
logMessageContent(characteristic.getValue());
return true;
@ -938,15 +801,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
return false;
}
public void logDate(byte[] value, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value);
LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime()));
} else {
logMessageContent(value);
}
}
public void logHeartrate(byte[] value, int status) {
if (status == BluetoothGatt.GATT_SUCCESS && value != null) {
LOG.info("Got heartrate:");
@ -1046,66 +900,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
return realtimeSamplesSupport;
}
/**
* React to unsolicited messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION
* characteristic,
* These messages appear to be always 1 byte long, with values that are listed in MiBandService.
* It is not excluded that there are further values which are still unknown.
* <p/>
* Upon receiving known values that request further action by GB, the appropriate method is called.
*
* @param value
*/
private void handleNotificationNotif(byte[] value) {
if (value.length != 1) {
LOG.error("Notifications should be 1 byte long.");
LOG.info("RECEIVED DATA WITH LENGTH: " + value.length);
for (byte b : value) {
LOG.warn("DATA: " + String.format("0x%2x", b));
}
return;
}
switch (value[0]) {
case MiBandService.NOTIFY_AUTHENTICATION_FAILED:
// we get first FAILED, then NOTIFY_STATUS_MOTOR_AUTH (0x13)
// which means, we need to authenticate by tapping
getDevice().setState(State.AUTHENTICATION_REQUIRED);
getDevice().sendDeviceUpdateIntent(getContext());
GB.toast(getContext(), "Band needs pairing", Toast.LENGTH_LONG, GB.ERROR);
break;
case MiBandService.NOTIFY_AUTHENTICATION_SUCCESS: // fall through -- not sure which one we get
case MiBandService.NOTIFY_RESET_AUTHENTICATION_SUCCESS: // for Mi 1A
case MiBandService.NOTIFY_STATUS_MOTOR_AUTH_SUCCESS:
LOG.info("Band successfully authenticated");
// maybe we can perform the rest of the initialization from here
doInitialize();
break;
case MiBandService.NOTIFY_STATUS_MOTOR_AUTH:
LOG.info("Band needs authentication (MOTOR_AUTH)");
getDevice().setState(State.AUTHENTICATING);
getDevice().sendDeviceUpdateIntent(getContext());
break;
case MiBandService.NOTIFY_SET_LATENCY_SUCCESS:
LOG.info("Setting latency succeeded.");
break;
default:
for (byte b : value) {
LOG.warn("DATA: " + String.format("0x%2x", b));
}
}
}
private void doInitialize() {
try {
TransactionBuilder builder = performInitialized("just initializing after authentication");
builder.queue(getQueue());
} catch (IOException ex) {
LOG.error("Unable to initialize device after authentication", ex);
}
}
private void handleDeviceName(byte[] value, int status) {
// if (status == BluetoothGatt.GATT_SUCCESS) {
// versionCmd.hwVersion = new String(value);
@ -1151,21 +945,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
// TODO: react on 0x10, 0x02, 0x01 on notification (success)
}
private void handleControlPointResult(byte[] value, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) {
LOG.warn("Could not write to the control point.");
}
LOG.info("handleControlPoint write status:" + status + "; length: " + (value != null ? value.length : "(null)"));
if (value != null) {
for (byte b : value) {
LOG.info("handleControlPoint WROTE DATA:" + String.format("0x%8x", b));
}
} else {
LOG.warn("handleControlPoint WROTE null");
}
}
private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) {
// if (getDeviceInfo().supportsHeartrate()) {
// getDevice().addDeviceInfo(new GenericItem(
@ -1190,44 +969,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
}
}
private void handleUserInfoResult(byte[] value, int status) {
// successfully transferred user info means we're initialized
// commented out, because we have SetDeviceStateAction which sets initialized
// state on every successful initialization.
// if (status == BluetoothGatt.GATT_SUCCESS) {
// setConnectionState(State.INITIALIZED);
// }
}
private void setConnectionState(State newState) {
getDevice().setState(newState);
getDevice().sendDeviceUpdateIntent(getContext());
}
private void handlePairResult(byte[] pairResult, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) {
LOG.info("Pairing MI device failed: " + status);
return;
}
String value = null;
if (pairResult != null) {
if (pairResult.length == 1) {
try {
if (pairResult[0] == 2) {
LOG.info("Successfully paired MI device");
return;
}
} catch (Exception ex) {
LOG.warn("Error identifying pairing result", ex);
return;
}
}
value = Arrays.toString(pairResult);
}
LOG.info("MI Band pairing result: " + value);
}
/**
* Fetch the events from the android device calendars and set the alarms on the miband.
* @param builder

View File

@ -17,14 +17,15 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
import nodomain.freeyourgadget.gadgetbridge.devices.miband2.MiBand2FWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.FirmwareType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2FirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.miband2.MiBand2FWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -113,10 +114,15 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
break;
}
case MiBand2Service.COMMAND_FIRMWARE_CHECKSUM: {
sendApplyReboot(getFirmwareInfo());
if (getFirmwareInfo().getFirmwareType() == FirmwareType.FIRMWARE) {
getSupport().onReboot();
} else {
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
done();
}
break;
}
case MiBand2Service.COMMAND_FIRMWARE_APPLY_REBOOT: {
case MiBand2Service.COMMAND_FIRMWARE_REBOOT: {
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
// getSupport().onReboot();
done();
@ -152,12 +158,20 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext()));
int fwSize = getFirmwareInfo().getSize();
byte[] sizeBytes = BLETypeConversions.fromUint24(fwSize);
byte[] bytes = new byte[]{
MiBand2Service.COMMAND_FIRMWARE_INIT,
sizeBytes[0],
sizeBytes[1],
sizeBytes[2],
};
int arraySize = 4;
boolean isFirmwareCode = getFirmwareInfo().getFirmwareType() == FirmwareType.FIRMWARE;
if (!isFirmwareCode) {
arraySize++;
}
byte[] bytes = new byte[arraySize];
int i = 0;
bytes[i++] = MiBand2Service.COMMAND_FIRMWARE_INIT;
bytes[i++] = sizeBytes[0];
bytes[i++] = sizeBytes[1];
bytes[i++] = sizeBytes[2];
if (!isFirmwareCode) {
bytes[i++] = getFirmwareInfo().getFirmwareType().getValue();
}
builder.write(fwCControlChar, bytes);
builder.queue(getQueue());
@ -176,7 +190,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
*
* @param info
* @return whether the transfer succeeded or not. Only a BT layer exception will cause the transmission to fail.
* @see MiBand2Support#handleNotificationNotif
* @see #handleNotificationNotif
*/
private boolean sendFirmwareData(Mi2FirmwareInfo info) {
byte[] fwbytes = info.getBytes();
@ -237,21 +251,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
builder.queue(getQueue());
}
private void sendApplyReboot(Mi2FirmwareInfo firmwareInfo) throws IOException {
TransactionBuilder builder = performInitialized("send firmware apply/reboot");
builder.write(fwCControlChar, new byte[] { MiBand2Service.COMMAND_FIRMWARE_APPLY_REBOOT });
builder.queue(getQueue());
}
private Mi2FirmwareInfo getFirmwareInfo() {
return firmwareInfo;
}
enum State {
INITIAL,
SEND_FW2,
SEND_FW1,
FINISHED,
UNKNOWN
}
}

View File

@ -52,4 +52,14 @@ public class ArrayUtils {
}
return result;
}
/**
* Returns true if the given byte array starts with the given values
* @param array the array to check
* @param values the values which the other array is checked to start with
* @return
*/
public static boolean startsWith(byte[] array, byte[] values) {
return equals(array, values, 0);
}
}

View File

@ -20,7 +20,12 @@ public class LanguageUtils {
put('п', "p"); put('р', "r"); put('с', "s"); put('т', "t"); put('у', "u"); put('ф', "f"); put('х', "kh"); put('ц', "c");
put('ч', "ch");put('ш', "sh");put('щ', "shh");put('ъ', "\"");put('ы', "y"); put('ь', "'"); put('э', "eh"); put('ю', "ju");
put('я', "ja");
//hebrew chars
put('א', "a"); put('ב', "b"); put('ג', "g"); put('ד', "d"); put('ה', "h"); put('ו', "u"); put('ז', "z"); put('ח', "kh");
put('ט', "t"); put('י', "y"); put('כ', "c"); put('ל', "l"); put('מ', "m"); put('נ', "n"); put('ס', "s"); put('ע', "'");
put('פ', "p"); put('צ', "ts"); put('ק', "k"); put('ר', "r"); put('ש', "sh"); put('ת', "th"); put('ף', "f"); put('ץ', "ts");
put('ך', "ch");put('ם', "m");put('ן', "n");
//continue for other languages...
}
};

View File

@ -213,7 +213,7 @@
<string name="miband_pairing_using_dummy_userdata">No valid user data given, using dummy user data for now.</string>
<string name="miband_pairing_tap_hint">When your Mi Band vibrates and blinks, tap it a few times in a row.</string>
<string name="appinstaller_install">Install</string>
<string name="discovery_connected_devices_hint">Make your device discoverable. Currently connected devices will likely not be discovered. On Android 6 or later, you need to activate location (e.g. GPS). If your device does not show up after two minutes, try again after rebooting your mobile device.</string>
<string name="discovery_connected_devices_hint">Make your device discoverable. Currently connected devices will likely not be discovered. Activate location (e.g. GPS) on Android 6+. Disable Privacy Guard for Gadgetbridge, because it may crash and reboot your phone. If no device is found after a few minutes, try again after rebooting your mobile device.</string>
<string name="discovery_note">Note:</string>
<string name="candidate_item_device_image">Device Image</string>
<string name="miband_prefs_alias">Name/Alias</string>

View File

@ -112,6 +112,6 @@ public class DeviceCommunicationServiceTestCase extends TestBase {
mDeviceService.invokeService(intent);
String result = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
assertTrue("Transliteration support fail!", result.equals("Prosto tekct"));
assertEquals("Transliteration support fail!", "Prosto tekct", result);
}
}

View File

@ -111,6 +111,46 @@ public class ArrayUtilsTest extends TestBase {
assertFalse(ArrayUtils.equals(DATA_5, new byte[] {3, 4, 6}, 2));
}
@Test
public void testStartsWith1() throws Exception {
assertTrue(ArrayUtils.startsWith(DATA_5, new byte[] {1}));
}
@Test
public void testStartsWith2() throws Exception {
assertTrue(ArrayUtils.startsWith(DATA_5, new byte[] {1, 2}));
}
@Test
public void testStartsWithAll() throws Exception {
assertTrue(ArrayUtils.startsWith(DATA_5, DATA_5.clone()));
}
@Test
public void testStartsWithEmpty() throws Exception {
assertTrue(ArrayUtils.startsWith(DATA_5, EMPTY));
}
@Test
public void testStartsWithFail1() throws Exception {
try {
ArrayUtils.startsWith(DATA_5, null);
fail("should have thrown an exception");
} catch (IllegalArgumentException ex) {
// expected
}
}
@Test
public void testStartsWithFail3() throws Exception {
assertFalse(ArrayUtils.startsWith(DATA_5, new byte[] {2, 3}));
}
@Test
public void testStartsWithFail4() throws Exception {
assertFalse(ArrayUtils.startsWith(DATA_5, new byte[] {1, 2, 3, 4, 5, 6}));
}
private byte[] b(int b) {
return new byte[] {(byte) b};
}

View File

@ -7,6 +7,7 @@ import org.junit.Test;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@ -15,13 +16,23 @@ import static org.junit.Assert.assertTrue;
*/
public class LanguageUtilsTest extends TestBase {
@Test
public void testStringTransliterate() throws Exception {
public void testStringTransliterateCyrillic() throws Exception {
//input with cyrillic and diacritic letters
String input = "Прõсто текčт";
String output = LanguageUtils.transliterate(input);
String result = "Prosto tekct";
assertTrue(String.format("Transliteration fail! Expected '%s', but found '%s'}", result, output), output.equals(result));
assertEquals("Transliteration failed", result, output);
}
@Test
public void testStringTransliterateHebrew() throws Exception {
//input with cyrillic and diacritic letters
String input = "בדיקה עברית";
String output = LanguageUtils.transliterate(input);
String result = "bdykh 'bryth";
assertEquals("Transliteration failed", result, output);
}
@Test
@ -31,7 +42,7 @@ public class LanguageUtilsTest extends TestBase {
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("transliteration", true);
editor.commit();
editor.apply();
assertTrue("Transliteration option fail! Expected 'On', but result is 'Off'", LanguageUtils.transliterate());
}

View File

@ -26,6 +26,13 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import static org.junit.Assert.assertNotNull;
/**
* Base class for all testcases in Gadgetbridge that are supposed to run locally
* with robolectric.
*
* Important: To run them, create a run configuration and execute them in the Gadgetbridge/app/
* directory.
*/
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 19)
// need sdk 19 because "WITHOUT ROWID" is not supported in robolectric/sqlite4java