From 0076bbf5728b1ceead9e1f3b01754df90abf3705 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 30 Oct 2016 23:04:21 +0100 Subject: [PATCH 001/147] Remove all Mi2 DeviceInfo stuff -- they're not used together #365 This might fix firmware downgrading on certain Mi1A devices with hw revision 8 --- .../service/devices/miband/DeviceInfo.java | 9 +-- .../devices/miband/MiBand2Support.java | 64 +++++-------------- 2 files changed, 18 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index c1c36258..3ba149df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -83,7 +83,7 @@ public class DeviceInfo extends AbstractInfo { } public boolean supportsHeartrate() { - return isMiliPro() || isMili1S() || (test1AHRMode && isMili1A()); + return isMili1S() || (test1AHRMode && isMili1A()); } @Override @@ -116,10 +116,6 @@ public class DeviceInfo extends AbstractInfo { return hwVersion == 6; } - public boolean isMiliPro() { - return hwVersion == 8 || (feature == 8 && appearance == 0); - } - public String getHwVersion() { if (isMili1()) { return MiBandConst.MI_1; @@ -133,9 +129,6 @@ public class DeviceInfo extends AbstractInfo { if (isAmazFit()) { return MiBandConst.MI_AMAZFIT; } - if (isMiliPro()) { - return MiBandConst.MI_PRO; - } return "?"; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index 20de3193..937780ca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -30,7 +30,6 @@ 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.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; @@ -456,9 +455,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { builder.add(new ConditionalWriteAction(characteristic) { @Override protected byte[] checkCondition() { - if (!supportsHeartRate()) { - return null; - } if (MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { LOG.info("Enabling heartrate sleep support..."); return startHeartMeasurementSleep; @@ -675,45 +671,35 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onHeartRateTest() { - if (supportsHeartRate()) { - try { - TransactionBuilder builder = performInitialized("HeartRateTest"); - heartRateProfile.requestHeartRateMeasurement(builder); + try { + TransactionBuilder builder = performInitialized("HeartRateTest"); + heartRateProfile.requestHeartRateMeasurement(builder); // profile.resetEnergyExpended(builder); // 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.queue(getQueue()); - } catch (IOException ex) { - LOG.error("Unable to read HearRate with MI2", ex); - } - } else { - GB.toast(getContext(), "Heart rate is not supported on this device", Toast.LENGTH_LONG, GB.ERROR); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to read HearRate with MI2", ex); } } @Override public void onEnableRealtimeHeartRateMeasurement(boolean enable) { - if (supportsHeartRate()) { - try { - TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); - if (enable) { - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); - } else { - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); - } - builder.queue(getQueue()); - } catch (IOException ex) { - LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); + try { + TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); + if (enable) { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); + } else { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); } + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); } } - public boolean supportsHeartRate() { - return true; - } - @Override public void onFindDevice(boolean start) { isLocatingDevice = start; @@ -880,10 +866,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { super.onCharacteristicRead(gatt, characteristic, status); UUID characteristicUUID = characteristic.getUuid(); - if (MiBandService.UUID_CHARACTERISTIC_DEVICE_INFO.equals(characteristicUUID)) { - handleDeviceInfo(characteristic.getValue(), status); - return true; - } else if (GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME.equals(characteristicUUID)) { + if (GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME.equals(characteristicUUID)) { handleDeviceName(characteristic.getValue(), status); return true; } else if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { @@ -1028,19 +1011,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } } - private void handleDeviceInfo(byte[] value, int status) { - if (status == BluetoothGatt.GATT_SUCCESS) { - mDeviceInfo = new DeviceInfo(value); - if (getDeviceInfo().supportsHeartrate()) { - getDevice().setFirmwareVersion2(MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getHeartrateFirmwareVersion())); - } - LOG.warn("Device info: " + mDeviceInfo); - versionCmd.hwVersion = mDeviceInfo.getHwVersion(); - versionCmd.fwVersion = MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getFirmwareVersion()); - handleGBDeviceEvent(versionCmd); - } - } - private void handleDeviceName(byte[] value, int status) { // if (status == BluetoothGatt.GATT_SUCCESS) { // versionCmd.hwVersion = new String(value); From 14ef5202e197bddb6a92a90662720716a4707e79 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Nov 2016 11:07:15 +0100 Subject: [PATCH 002/147] some internal testing which does not hurt in master --- .../devices/pebble/PebbleIoThread.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index dbfe3171..89f45c95 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -21,6 +21,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.net.InetAddress; import java.net.Socket; import java.nio.ByteBuffer; @@ -177,17 +179,25 @@ class PebbleIoThread extends GBDeviceIoThread { } else { mIsTCP = false; BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(btDeviceAddress); - ParcelUuid uuids[] = btDevice.getUuids(); - if (uuids == null) { - return false; + if (btDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE) { + LOG.info("Ok this seems to be a LE Pebble, will try something that does not work :P"); + mInStream = new PipedInputStream(new PipedOutputStream()); // fake so that io blocks + mOutStream = new PipedOutputStream(new PipedInputStream()); // fake so that io blocks + //new PebbleLESupport(this.getContext(),btDeviceAddress); // secret branch :P } - for (ParcelUuid uuid : uuids) { - LOG.info("found service UUID " + uuid); + else { + ParcelUuid uuids[] = btDevice.getUuids(); + if (uuids == null) { + return false; + } + for (ParcelUuid uuid : uuids) { + LOG.info("found service UUID " + uuid); + } + mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid()); + mBtSocket.connect(); + mInStream = mBtSocket.getInputStream(); + mOutStream = mBtSocket.getOutputStream(); } - mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid()); - mBtSocket.connect(); - mInStream = mBtSocket.getInputStream(); - mOutStream = mBtSocket.getOutputStream(); } } catch (IOException e) { e.printStackTrace(); @@ -361,7 +371,7 @@ class PebbleIoThread extends GBDeviceIoThread { e.printStackTrace(); } } catch (IOException e) { - if (e.getMessage().contains("socket closed")) { //FIXME: this does not feel right + if (e.getMessage() != null && e.getMessage().contains("socket closed")) { //FIXME: this does not feel right LOG.info(e.getMessage()); mIsConnected = false; int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10); From 55f036c104a37d4a1ad8bc7db27e1c392181c848 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 4 Nov 2016 22:11:07 +0100 Subject: [PATCH 003/147] Move heart rate notification enamblement to after authentication Might fix #408 and #425 --- .../service/devices/miband/MiBand2Support.java | 11 +++++------ .../devices/miband2/operations/InitOperation.java | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index 937780ca..dae5952a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -233,15 +233,14 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_AUTH), enable); builder.notify(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), enable); builder.notify(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4), enable); - builder.notify(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT), enable); return this; } - private 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_BATTERY), enable) - .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); + 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_BATTERY), enable) +// .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); // cannot use supportsHeartrate() here because we don't have that information yet BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT); if (heartrateCharacteristic != null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java index 7e8335db..ab5f73e9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java @@ -101,6 +101,7 @@ public class InitOperation extends AbstractBTLEOperation { TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the band"); builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); getSupport().requestDeviceInfo(builder); + getSupport().enableFurtherNotifications(builder, true); getSupport().setInitialized(builder); getSupport().performImmediately(builder); } else { From 4c1b7e0328e3470247fff24227c658c0ea78e618 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 4 Nov 2016 22:17:27 +0100 Subject: [PATCH 004/147] Release 0.13.8 --- CHANGELOG.md | 3 +++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fec508c..2023361f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version 0.13.8 +* Mi Band2: attempt at fixing connection issues for users of Mi Fit (#408, #425) + ####Version 0.13.7 * Pebble: Fix configuration of certain pebble apps (eg. QR Generator, Squared 4.0) * Pebble: Add context menu option in app manager to search a watchapp in the pebble appstore diff --git a/app/build.gradle b/app/build.gradle index da90c4b1..6ebd536e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.13.7" - versionCode 69 + versionName "0.13.8" + versionCode 70 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 7ca5092e..28a8722f 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + Mi Band 2: attempt at fixing connection issues for users of Mi Fit + Pebble: Fix configuration of certain pebble apps (eg. QR Generator, Squared 4.0) Pebble: Add context menu option in app manager to search a watchapp in the pebble appstore From 119c225ec4d56b94fc4806cc6fa790b4e92c99f0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 4 Nov 2016 22:29:58 +0100 Subject: [PATCH 005/147] Updated translation from transifex (thanks!) --- app/src/main/res/values-es/strings.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 9d013624..b6e242f3 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -22,6 +22,7 @@ Borrar Borrar y quitar de la caché Reinstalar + Buscar en la Appstore de Pebble Activar Desactivar Configurar @@ -126,6 +127,10 @@ Usar el menú de emparejar Bluetooth de Android para emparejar el dispositivo. Emparejar tu MiBand Emparejando con %s... + Creando emparejamiento con %1$s (%2$s) + No se ha podido emparejar con %1$s (%2$s) + Emparejamiento en progreso: %1$s (%2$s) + Emparejado con %1$s (%2$s), conectando… Ninguna dirección MAC aprobada, no se puede emparejar. Ajustes específicos del dispositivo Ajustes de MiBand @@ -137,7 +142,7 @@ No se han proporcionado datos de usuario válidos, se usarán datos de usuario por defecto. Cuando tu MiBand vibre y parpadee, púlsala repetidas veces. Instalar - Haz tu dispositivo detectable. Los dipositivos ya conectados es probable que no se encuentren. Si tu dispositivo no aparece en dos minutos, prueba de nuevo reiniciando tu móvil. + Haga su dispositivo detectable. Actualmente los dispositivos conectados no serán detectables. En Android 6 o superior, necesita activar la localización (ej. GPS). Si su dispositivo no aparece en dos minutos, vuelva a intentarlo tras reiniciar su teléfono. Nota: Imagen del dispositivo Nombre/Apodo From 16c4f1a5cadcd2726e0a5768caefeea8f904316c Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 6 Nov 2016 15:42:52 +0100 Subject: [PATCH 006/147] Pebble: add toggle to use last known location for sunrise and sunset This adds the feature discussed in #415, the used location is the last recorded by the network location provider, if it's not available then the stored location is used. --- CHANGELOG.md | 3 +++ .../externalevents/AlarmReceiver.java | 21 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/changelog_master.xml | 3 +++ app/src/main/res/xml/preferences.xml | 6 ++++++ 5 files changed, 35 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2023361f..80cb16a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version next +* Pebble: use the last known location for setting sunrise and sunset + ####Version 0.13.8 * Mi Band2: attempt at fixing connection issues for users of Mi Fit (#408, #425) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java index e9470d31..9c4a581a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java @@ -1,11 +1,17 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; +import android.Manifest; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationManager; +import android.support.v4.app.ActivityCompat; import net.e175.klaus.solarpositioning.DeltaT; import net.e175.klaus.solarpositioning.SPA; @@ -64,6 +70,21 @@ public class AlarmReceiver extends BroadcastReceiver { float latitude = prefs.getFloat("location_latitude", 0); float longitude = prefs.getFloat("location_longitude", 0); LOG.info("got longitude/latitude from preferences: " + latitude + "/" + longitude); + + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED && + prefs.getBoolean("use_updated_location_if_available", false)) { + LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + Criteria criteria = new Criteria(); + String provider = locationManager.getBestProvider(criteria, false); + if (provider != null) { + Location lastKnownLocation = locationManager.getLastKnownLocation(provider); + if (lastKnownLocation != null) { + latitude = (float) lastKnownLocation.getLatitude(); + longitude = (float) lastKnownLocation.getLongitude(); + LOG.info("got longitude/latitude from last known location: " + latitude + "/" + longitude); + } + } + } GregorianCalendar[] sunriseTransitSetTomorrow = SPA.calculateSunriseTransitSet(dateTimeTomorrow, latitude, longitude, DeltaT.estimate(dateTimeTomorrow)); CalendarEventSpec calendarEventSpec = new CalendarEventSpec(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62a5c8b2..00ece7d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,6 +106,8 @@ Acquire Location Latitude Longitude + Keep location updated + Try to get the current location at runtime, use the stored location as fallback Please enable network location location acquired diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 28a8722f..158ab742 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + Pebble: use the last known location for setting sunrise and sunset + Mi Band 2: attempt at fixing connection issues for users of Mi Fit diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 663ca61f..483f6deb 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -206,6 +206,12 @@ android:defaultValue="0" android:key="location_longitude" android:title="@string/pref_title_location_longitude" /> + From 705912172d3af05962b197dde2bff7d586b09a39 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Nov 2016 22:21:03 +0100 Subject: [PATCH 007/147] Adjust changelog to confirmed fixed bugreports --- CHANGELOG.md | 3 ++- app/src/main/res/xml/changelog_master.xml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80cb16a8..88df4c0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ * Pebble: use the last known location for setting sunrise and sunset ####Version 0.13.8 -* Mi Band2: attempt at fixing connection issues for users of Mi Fit (#408, #425) +* Mi Band 2: fix connection issues for users of Mi Fit (#408, #425) +* Mi Band 1A: fix firmware update for certain 1A models ####Version 0.13.7 * Pebble: Fix configuration of certain pebble apps (eg. QR Generator, Squared 4.0) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 158ab742..555b71a4 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -4,7 +4,8 @@ Pebble: use the last known location for setting sunrise and sunset - Mi Band 2: attempt at fixing connection issues for users of Mi Fit + Mi Band 2: fix connection issues for users of Mi Fit + Mi Band 1A: fix firmware update on certain 1A models Pebble: Fix configuration of certain pebble apps (eg. QR Generator, Squared 4.0) From d0beee3df61a4dd60c60c54971cdedd90c967411 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Nov 2016 20:17:51 +0100 Subject: [PATCH 008/147] Updated documentation about features and how to use with Mi Bands --- README.md | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c54814c6..296d1f98 100644 --- a/README.md +++ b/README.md @@ -56,38 +56,53 @@ need to create an account and transmit any of your data to the vendor's servers. For more information read [this wiki article](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble-Getting-Started) -## Features (Mi Band) +## Features (Mi Band 1x) -* Mi Band notifications (LEDs + vibration) for - * Discovery and pairing +* Discovery and pairing +* Mi Band notifications (LEDs + vibration) for * Incoming calls * SMS received * K-9 mails received + * Conversations messages * Generic Android notifications * Synchronize the time to the Mi Band * Display firmware version and battery state * Firmware Update -* Heartrate Measurement (alpha) +* Heart rate measurement * Synchronize activity data * Display sleep data (alpha) * Display sports data (step count) (alpha) * Display live activity data (alpha) * Set alarms on the Mi Band -## How to use (Mi Band) +## Features (Mi Band 2) -* When starting Gadgetbridge and no device is visible, it will automatically - attempt to discover and pair your Mi Band. Alternatively you can invoke this - manually via the menu button. It will ask you for some personal info that appears +* Discovery and pairing +* Mi Band notifications (Display + vibration) for + * Incoming calls + * SMS received + * K-9 mails received + * Conversations messages + * Generic Android notifications +* Synchronize the time to the Mi Band +* Display firmware version +* Heart rate measurement (alpha) +* Set alarms on the Mi Band 2 + +## How to use (Mi Band 1+2) + +* When starting Gadgetbridge the first time, it will automatically + attempt to discover and pair your Mi Band. Alternatively you can invoke discovery + manually via the "+" button. It will ask you for some personal info that appears to be needed for proper steps calculation on the band. If you do not provide these, some hardcoded default "dummy" values will be used instead. - When your Mi Band starts to vibrate and blink with all three LEDs during the pairing process, + When your Mi Band starts to vibrate and blink during the pairing process, tap it quickly a few times in a row to confirm the pairing with the band. 1. Configure other notifications as desired 2. Go back to the "Gadgetbridge" Activity -3. Tap the "MI" item to connect if you're not connected yet. +3. Tap the Mi Band item to connect if you're not connected yet. 4. To test, chose "Debug" from the menu and play around Known Issues: From 4843f7fc2bd0482cd331eed32a6a51ba2c13eafd Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Nov 2016 20:19:47 +0100 Subject: [PATCH 009/147] More details regarding Mi hr features --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 296d1f98..289b7440 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/ * Synchronize the time to the Mi Band * Display firmware version and battery state * Firmware Update -* Heart rate measurement +* Heart rate measurement on demand and during sleep * Synchronize activity data * Display sleep data (alpha) * Display sports data (step count) (alpha) From 3b474bb5a97c05a82672fb6b41e2f1f0d4a784e7 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Nov 2016 22:55:31 +0100 Subject: [PATCH 010/147] Move another notify registration to after initialization #408 --- .../gadgetbridge/service/devices/miband/MiBand2Support.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index dae5952a..4078fc8b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -231,8 +231,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { builder.notify(getCharacteristic(GattService.UUID_SERVICE_CURRENT_TIME), enable); // Notify CHARACTERISTIC9 to receive random auth code builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_AUTH), enable); - builder.notify(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), enable); - builder.notify(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4), enable); return this; } @@ -241,7 +239,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable) // .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_BATTERY), enable) // .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); - // cannot use supportsHeartrate() here because we don't have that information yet + builder.notify(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), enable); BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT); if (heartrateCharacteristic != null) { builder.notify(heartrateCharacteristic, enable); From 837dfd5917a6e3d0f40cf2cc938c5437e8b55077 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 9 Nov 2016 12:06:13 +0100 Subject: [PATCH 011/147] Pebble: fix Pebble Health vanishing when deactivating --- .../activities/appmanager/AbstractAppManagerFragment.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index 18be286e..95a1b76a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -310,7 +310,6 @@ public abstract class AbstractAppManagerFragment extends Fragment { public boolean onContextItemSelected(MenuItem item, GBDeviceApp selectedApp) { switch (item.getItemId()) { - case R.id.appmanager_health_deactivate: case R.id.appmanager_app_delete_cache: String baseName; try { @@ -354,6 +353,9 @@ public abstract class AbstractAppManagerFragment extends Fragment { case R.id.appmanager_health_activate: GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); return true; + case R.id.appmanager_health_deactivate: + GBApplication.deviceService().onAppDelete(selectedApp.getUUID()); + return true; case R.id.appmanager_app_configure: GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true); From eb7646d26afdcaa22680b166a4a110016bf85303 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 9 Nov 2016 19:20:37 +0100 Subject: [PATCH 012/147] Pebble: Fix vanished Health system apps (for affected users) This code also allows us to add new system apps which will then appended to the current list of previous Gadgetbridge users. --- .../AbstractAppManagerFragment.java | 42 ++++++++----------- .../appmanager/AppManagerFragmentCache.java | 7 ++++ .../AppManagerFragmentInstalledApps.java | 30 ++++++++----- ...AppManagerFragmentInstalledWatchfaces.java | 19 ++++----- 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index 95a1b76a..bd4670c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -46,7 +46,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { = "nodomain.freeyourgadget.gadgetbridge.appmanager.action.refresh_applist"; private static final Logger LOG = LoggerFactory.getLogger(AbstractAppManagerFragment.class); - protected abstract void refreshList(); + protected abstract List getSystemAppsInCategory(); protected abstract String getSortFilename(); @@ -62,6 +62,23 @@ public abstract class AbstractAppManagerFragment extends Fragment { AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuidList); } + protected void refreshList() { + appList.clear(); + ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename()); + List systemApps = getSystemAppsInCategory(); + boolean needsRewrite = false; + for (GBDeviceApp systemApp : systemApps) { + if (!uuids.contains(systemApp.getUUID())) { + uuids.add(systemApp.getUUID()); + needsRewrite = true; + } + } + if (needsRewrite) { + AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuids); + } + appList.addAll(getCachedApps(uuids)); + } + private void refreshListFromPebble(Intent intent) { appList.clear(); int appCount = intent.getIntExtra("app_count", 0); @@ -103,29 +120,6 @@ public abstract class AbstractAppManagerFragment extends Fragment { private GBDeviceAppAdapter mGBDeviceAppAdapter; protected GBDevice mGBDevice = null; - protected List getSystemApps() { - List systemApps = new ArrayList<>(); - //systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - //systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - systemApps.add(new GBDeviceApp(UUID.fromString("1f03293d-47af-4f28-b960-f2b02a6dd757"), "Music (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - systemApps.add(new GBDeviceApp(UUID.fromString("b2cae818-10f8-46df-ad2b-98ad2254a3c1"), "Notifications (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - systemApps.add(new GBDeviceApp(UUID.fromString("67a32d95-ef69-46d4-a0b9-854cc62f97f9"), "Alarms (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - systemApps.add(new GBDeviceApp(UUID.fromString("18e443ce-38fd-47c8-84d5-6d0c775fbe55"), "Watchfaces (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - - if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getModel()))) { - systemApps.add(new GBDeviceApp(UUID.fromString("0863fc6a-66c5-4f62-ab8a-82ed00a98b5d"), "Send Text (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - } - - return systemApps; - } - - protected List getSystemWatchfaces() { - List systemWatchfaces = new ArrayList<>(); - systemWatchfaces.add(new GBDeviceApp(UUID.fromString("8f3c8686-31a1-4f5f-91f5-01600c9bdc59"), "Tic Toc (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); - return systemWatchfaces; - } - protected List getCachedApps(List uuids) { List cachedAppList = new ArrayList<>(); File cachePath; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java index fbf42e94..8423ffbe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; +import java.util.List; + import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; public class AppManagerFragmentCache extends AbstractAppManagerFragment { @@ -14,6 +16,11 @@ public class AppManagerFragmentCache extends AbstractAppManagerFragment { return true; } + @Override + protected List getSystemAppsInCategory() { + return null; + } + @Override public String getSortFilename() { return "pbwcacheorder.txt"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java index d968f713..d49e9e21 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java @@ -1,23 +1,31 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; +import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment { + @Override - protected void refreshList() { - appList.clear(); - ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename()); - if (uuids.isEmpty()) { - appList.addAll(getSystemApps()); - for (GBDeviceApp gbDeviceApp : appList) { - uuids.add(gbDeviceApp.getUUID()); - } - AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuids); - } else { - appList.addAll(getCachedApps(uuids)); + protected List getSystemAppsInCategory() { + List systemApps = new ArrayList<>(); + //systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + //systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + systemApps.add(new GBDeviceApp(UUID.fromString("1f03293d-47af-4f28-b960-f2b02a6dd757"), "Music (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + systemApps.add(new GBDeviceApp(UUID.fromString("b2cae818-10f8-46df-ad2b-98ad2254a3c1"), "Notifications (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + systemApps.add(new GBDeviceApp(UUID.fromString("67a32d95-ef69-46d4-a0b9-854cc62f97f9"), "Alarms (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + systemApps.add(new GBDeviceApp(UUID.fromString("18e443ce-38fd-47c8-84d5-6d0c775fbe55"), "Watchfaces (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + + if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getModel()))) { + systemApps.add(new GBDeviceApp(UUID.fromString("0863fc6a-66c5-4f62-ab8a-82ed00a98b5d"), "Send Text (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); } + + return systemApps; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java index 289c6656..a0df8d89 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java @@ -1,23 +1,18 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; public class AppManagerFragmentInstalledWatchfaces extends AbstractAppManagerFragment { + @Override - protected void refreshList() { - appList.clear(); - ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename()); - if (uuids.isEmpty()) { - appList.addAll(getSystemWatchfaces()); - for (GBDeviceApp gbDeviceApp : appList) { - uuids.add(gbDeviceApp.getUUID()); - } - AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuids); - } else { - appList.addAll(getCachedApps(uuids)); - } + protected List getSystemAppsInCategory() { + List systemWatchfaces = new ArrayList<>(); + systemWatchfaces.add(new GBDeviceApp(UUID.fromString("8f3c8686-31a1-4f5f-91f5-01600c9bdc59"), "Tic Toc (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); + return systemWatchfaces; } @Override From 1fcd7d81441f2b28fb970afc73ef121651f0c0be Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 10 Nov 2016 10:51:04 +0100 Subject: [PATCH 013/147] bump version, update changelogs --- CHANGELOG.md | 4 +++- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88df4c0a..3faf21c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ###Changelog -####Version next +####Version 0.13.9 * Pebble: use the last known location for setting sunrise and sunset +* Pebble: fix Health disappering forever when deactivating through app manager (and get it back for affected users) +* Mi Band 2: More fixes for connection issues (#408) ####Version 0.13.8 * Mi Band 2: fix connection issues for users of Mi Fit (#408, #425) diff --git a/app/build.gradle b/app/build.gradle index 6ebd536e..6f61b3bc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.13.8" - versionCode 70 + versionName "0.13.9" + versionCode 71 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 555b71a4..f6220697 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,7 +1,9 @@ - + Pebble: use the last known location for setting sunrise and sunset + Pebble: fix Health disappering forever when deactivating through app manager (and get it back for affected users) + Mi Band 2: More fixes for connection issues (#408) Mi Band 2: fix connection issues for users of Mi Fit From f68bbe453b966a0d76d1d739c658109bd015cb63 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 10 Nov 2016 11:05:32 +0100 Subject: [PATCH 014/147] Pebble: bump flags and version we report Plus some small preparations for P2 support which do not hurt in master --- .../service/devices/pebble/PebbleIoThread.java | 16 ++++++---------- .../service/devices/pebble/PebbleProtocol.java | 8 ++++---- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 89f45c95..84b977e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -181,9 +181,9 @@ class PebbleIoThread extends GBDeviceIoThread { BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(btDeviceAddress); if (btDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE) { LOG.info("Ok this seems to be a LE Pebble, will try something that does not work :P"); - mInStream = new PipedInputStream(new PipedOutputStream()); // fake so that io blocks - mOutStream = new PipedOutputStream(new PipedInputStream()); // fake so that io blocks - //new PebbleLESupport(this.getContext(),btDeviceAddress); // secret branch :P + mInStream = new PipedInputStream(); // fake so that io blocks + mOutStream = new PipedOutputStream(); // fake so that io blocks + //new PebbleLESupport(this.getContext(),btDeviceAddress,(PipedInputStream)mInStream,(PipedOutputStream)mOutStream); // secret branch :P } else { ParcelUuid uuids[] = btDevice.getUuids(); @@ -323,9 +323,10 @@ class PebbleIoThread extends GBDeviceIoThread { } int bytes = mInStream.read(buffer, 0, 4); - if (bytes < 4) { - continue; + while (bytes < 4) { + bytes += mInStream.read(buffer, bytes, 4 - bytes); } + ByteBuffer buf = ByteBuffer.wrap(buffer); buf.order(ByteOrder.BIG_ENDIAN); short length = buf.getShort(); @@ -340,11 +341,6 @@ class PebbleIoThread extends GBDeviceIoThread { bytes = mInStream.read(buffer, 4, length); while (bytes < length) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } bytes += mInStream.read(buffer, bytes + 4, length - bytes); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index 57c58b70..eca9afa9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -1297,11 +1297,11 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putInt(os); buf.put(PHONEVERSION_APPVERSION_MAGIC); - buf.put((byte) 3); // major - buf.put((byte) 12); // minor - buf.put((byte) 0); // patch + buf.put((byte) 4); // major + buf.put((byte) 1); // minor + buf.put((byte) 1); // patch buf.order(ByteOrder.LITTLE_ENDIAN); - buf.putLong(0x00000000000001af); //flags + buf.putLong(0x00000000000029af); //flags return buf.array(); } From 37eb60b60ca77d12e36437b50b6784f2db80954c Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Fri, 11 Nov 2016 02:39:01 +0100 Subject: [PATCH 015/147] Updated documentation ("Supported Devices" section, Mi Band 2 info) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 289b7440..df641a95 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ need to create an account and transmit any of your data to the vendor's servers. ## Supported Devices * Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round * Mi Band, Mi Band 1A, Mi Band 1S -* Mi Band 2 (only notifications) +* Mi Band 2 (notifications, alarms and heart rate measurement only) * Vibratissimo (experimental) ***THE PEBBLE 2 AND PEBBLE TIME 2 ARE CURRENTLY NOT SUPPORTED. ADDING SUPPORT IS HIGH-PRIORITY BUT WE CANNOT GIVE YOU AN ETA!*** From 3cc8d887ca1611b74389e8bf4046c51c0a96e150 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 13 Nov 2016 01:42:55 +0100 Subject: [PATCH 016/147] Mi2: Initial support for wear location, hr during sleep and date format #323 - wear location from prefs is sent to the band - hr measurement during sleep is sent to the band - date format is sent to the band (date+time or just time) (TODO: needs preference option) --- .../devices/miband/MiBand2Service.java | 19 +++++ .../devices/miband/MiBand2Support.java | 77 +++++++++++-------- .../service/devices/miband2/DateDisplay.java | 6 ++ .../miband2/operations/InitOperation.java | 2 +- .../gadgetbridge/util/.gitignore | 2 + 5 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/.gitignore diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index 064ac7b6..6f397364 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -279,6 +279,25 @@ public class MiBand2Service { */ public static final byte AUTH_BYTE = 0x8; + public static byte COMMAND_DATEFORMAT = 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 RESPONSE = 0x10; + + /** + * 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[] WEAR_LOCATION_LEFT_WRIST = new byte[] { 0x20, 0x00, 0x00, 0x02 }; + public static final byte[] WEAR_LOCATION_RIGHT_WRIST = new byte[] { 0x20, 0x00, 0x00, (byte) 0x82}; + + 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}; + + static { MIBAND_DEBUG = new HashMap<>(); MIBAND_DEBUG.put(UUID_SERVICE_MIBAND_SERVICE, "MiBand Service"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index 4078fc8b..d8754e80 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -52,11 +52,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; 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.ConditionalWriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.DateDisplay; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; @@ -394,28 +394,22 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { /** * Part of device initialization process. Do not call manually. * - * @param transaction + * @param builder * @return */ - private MiBand2Support setWearLocation(TransactionBuilder transaction) { + private MiBand2Support setWearLocation(TransactionBuilder builder) { LOG.info("Attempting to set wear location..."); - BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC8); if (characteristic != null) { - transaction.add(new ConditionalWriteAction(characteristic) { - @Override - protected byte[] checkCondition() { - if (getDeviceInfo() != null && getDeviceInfo().isAmazFit()) { - return null; - } - int location = MiBandCoordinator.getWearLocation(getDevice().getAddress()); - return new byte[]{ - MiBandService.COMMAND_SET_WEAR_LOCATION, - (byte) location - }; - } - }); - } else { - LOG.info("Unable to set Wear Location"); + int location = MiBandCoordinator.getWearLocation(getDevice().getAddress()); + switch (location) { + case 0: // left hand + builder.write(characteristic, MiBand2Service.WEAR_LOCATION_LEFT_WRIST); + break; + case 1: // write hand + builder.write(characteristic, MiBand2Service.WEAR_LOCATION_RIGHT_WRIST); + break; + } } return this; } @@ -447,20 +441,16 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { * @param builder */ private MiBand2Support setHeartrateSleepSupport(TransactionBuilder builder) { - BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); - if (characteristic != null) { - builder.add(new ConditionalWriteAction(characteristic) { - @Override - protected byte[] checkCondition() { - if (MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { - LOG.info("Enabling heartrate sleep support..."); - return startHeartMeasurementSleep; - } else { - LOG.info("Disabling heartrate sleep support..."); - return stopHeartMeasurementSleep; - } - } - }); + BluetoothGattCharacteristic characteristicHRControlPoint = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); + final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress()); + if (characteristicHRControlPoint != null) { + if (enableHrSleepSupport) { + LOG.info("Enabling heartrate sleep support..."); + builder.write(characteristicHRControlPoint, MiBand2Service.COMMAND_ENABLE_HR_SLEEP_MEASUREMENT); + } else { + LOG.info("Disabling heartrate sleep support..."); + builder.write(characteristicHRControlPoint, MiBand2Service.COMMAND_DISABLE_HR_SLEEP_MEASUREMENT); + } } return this; } @@ -1167,4 +1157,25 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { } + + public MiBand2Support setDateDisplay(DateDisplay displayConfig, TransactionBuilder builder) { + LOG.info("Setting date display to " + displayConfig); + switch (displayConfig) { + case TIME: + builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), MiBand2Service.DATEFORMAT_TIME); + break; + case DATE_TIME: + builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), MiBand2Service.DATEFORMAT_DATE_TIME); + break; + } + return this; + } + + public void phase2Initialize(TransactionBuilder builder) { + LOG.info("phase2Initialize..."); + enableFurtherNotifications(builder, true); + setDateDisplay(DateDisplay.TIME, builder); + setWearLocation(builder); + setHeartrateSleepSupport(builder); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java new file mode 100644 index 00000000..0ac7de53 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java @@ -0,0 +1,6 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; + +public enum DateDisplay { + TIME, + DATE_TIME +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java index ab5f73e9..0d59c4b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java @@ -101,7 +101,7 @@ public class InitOperation extends AbstractBTLEOperation { TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the band"); builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); getSupport().requestDeviceInfo(builder); - getSupport().enableFurtherNotifications(builder, true); + getSupport().phase2Initialize(builder); getSupport().setInitialized(builder); getSupport().performImmediately(builder); } else { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/.gitignore b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/.gitignore new file mode 100644 index 00000000..8242cf9f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/.gitignore @@ -0,0 +1,2 @@ +small/ +SmallHelper.java From ddaf51768db6b0ae6c706009c15235800916688e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 13 Nov 2016 20:20:32 +0100 Subject: [PATCH 017/147] Improved logging --- .../gadgetbridge/service/btle/actions/NotifyAction.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java index 79773031..4e5b4eb9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java @@ -9,7 +9,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; -import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import static nodomain.freeyourgadget.gadgetbridge.service.btle.GattDescriptor.UUID_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION; @@ -20,7 +19,7 @@ import static nodomain.freeyourgadget.gadgetbridge.service.btle.GattDescriptor.U */ public class NotifyAction extends BtLEAction { - private static final Logger LOG = LoggerFactory.getLogger(TransactionBuilder.class); + private static final Logger LOG = LoggerFactory.getLogger(NotifyAction.class); protected final boolean enableFlag; private boolean hasWrittenDescriptor = true; @@ -49,7 +48,7 @@ public class NotifyAction extends BtLEAction { hasWrittenDescriptor = false; } } else { - LOG.warn("sleep descriptor null"); + LOG.warn("Descriptor CLIENT_CHARACTERISTIC_CONFIGURATION for characteristic " + getCharacteristic().getUuid() + " is null"); hasWrittenDescriptor = false; } } else { From d89899557c9459ab70553a5e9ed0a102e2d7c1f0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 13 Nov 2016 20:47:24 +0100 Subject: [PATCH 018/147] Mi2: Add config option to configure date/time display (added a new EventHandler method to set a specific configuration option) --- .../gadgetbridge/devices/EventHandler.java | 7 ++++ .../devices/miband/DateTimeDisplay.java | 6 ++++ .../devices/miband/MiBand2Coordinator.java | 12 +++++++ .../devices/miband/MiBandConst.java | 1 + .../miband/MiBandPreferencesActivity.java | 15 +++++++++ .../gadgetbridge/impl/GBDeviceService.java | 7 ++++ .../gadgetbridge/model/DeviceService.java | 2 ++ .../service/DeviceCommunicationService.java | 7 ++++ .../service/ServiceDeviceSupport.java | 8 +++++ .../devices/miband/MiBand2Support.java | 33 ++++++++++++++++--- .../service/devices/miband/MiBandSupport.java | 5 +++ .../service/devices/miband2/DateDisplay.java | 6 ---- .../service/devices/pebble/PebbleSupport.java | 7 ++++ .../vibratissimo/VibratissimoSupport.java | 5 +++ .../serial/AbstractSerialDeviceSupport.java | 6 ++++ .../service/serial/GBDeviceProtocol.java | 5 +++ app/src/main/res/values/arrays.xml | 8 +++++ app/src/main/res/values/strings.xml | 3 ++ app/src/main/res/values/values.xml | 3 ++ app/src/main/res/xml/miband_preferences.xml | 9 +++++ .../service/TestDeviceSupport.java | 5 +++ 21 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index a3af9b20..7c7002ee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -67,5 +67,12 @@ public interface EventHandler { void onDeleteCalendarEvent(byte type, long id); + /** + * Sets the given option in the device, typically with values from the preferences. + * The config name is device specific. + * @param config the device specific option to set on the device + */ + void onSendConfiguration(String config); + void onTestNewFunction(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java new file mode 100644 index 00000000..40857aa4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java @@ -0,0 +1,6 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.miband; + +public enum DateTimeDisplay { + TIME, + DATE_TIME +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 32957537..94070a19 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -7,10 +7,13 @@ import android.net.Uri; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class MiBand2Coordinator extends MiBandCoordinator { private static final Logger LOG = LoggerFactory.getLogger(MiBand2Coordinator.class); @@ -55,4 +58,13 @@ public class MiBand2Coordinator extends MiBandCoordinator { public InstallHandler findInstallHandler(Uri uri, Context context) { return null; // not supported at the moment } + + public static DateTimeDisplay getDateDisplay(Context context) throws IllegalArgumentException { + Prefs prefs = GBApplication.getPrefs(); + String dateFormatTime = context.getString(R.string.p_dateformat_time); + if (dateFormatTime.equals(prefs.getString(MiBandConst.PREF_MI2_DATEFORMAT, dateFormatTime))) { + return DateTimeDisplay.TIME; + } + return DateTimeDisplay.DATE_TIME; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java index fce2f84d..145f8179 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java @@ -17,6 +17,7 @@ public final class MiBandConst { public static final String PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR = "mi_reserve_alarm_calendar"; public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection"; public static final String PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS = "mi_device_time_offset_hours"; + public static final String PREF_MI2_DATEFORMAT = "mi2_dateformat"; public static final String PREF_TRY_SMS = "mi_try_sms"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java index 4c8de0aa..cb289b1c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java @@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.GB; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_INCOMING_CALL; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DATEFORMAT; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ADDRESS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_FITNESS_GOAL; @@ -44,8 +45,22 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { return true; } }); + final Preference setDateFormat = findPreference(PREF_MI2_DATEFORMAT); + setDateFormat.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + getListView().post(new Runnable() { // delayed execution so that the preferences are applied first + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_DATEFORMAT); + } + }); + return true; + } + }); } + @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); final Preference developmentMiaddr = findPreference(PREF_MIBAND_ADDRESS); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index a4673655..8fd47d31 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -281,6 +281,13 @@ public class GBDeviceService implements DeviceService { invokeService(intent); } + @Override + public void onSendConfiguration(String config) { + Intent intent = createIntent().setAction(ACTION_SEND_CONFIGURATION) + .putExtra(EXTRA_CONFIG, config); + invokeService(intent); + } + @Override public void onTestNewFunction() { Intent intent = createIntent().setAction(ACTION_TEST_NEW_FUNCTION); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index 6afa7e34..82fd3958 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -48,6 +48,7 @@ public interface DeviceService extends EventHandler { String ACTION_HEARTRATE_MEASUREMENT = PREFIX + ".action.hr_measurement"; String ACTION_ADD_CALENDAREVENT = PREFIX + ".action.add_calendarevent"; String ACTION_DELETE_CALENDAREVENT = PREFIX + ".action.delete_calendarevent"; + String ACTION_SEND_CONFIGURATION = PREFIX + ".action.send_configuration"; String ACTION_TEST_NEW_FUNCTION = PREFIX + ".action.test_new_function"; String EXTRA_DEVICE_ADDRESS = "device_address"; String EXTRA_NOTIFICATION_BODY = "notification_body"; @@ -80,6 +81,7 @@ public interface DeviceService extends EventHandler { String EXTRA_APP_START = "app_start"; String EXTRA_APP_CONFIG = "app_config"; String EXTRA_URI = "uri"; + String EXTRA_CONFIG = "config"; String EXTRA_ALARMS = "alarms"; String EXTRA_PERFORM_PAIR = "perform_pair"; String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 82222bcf..ef36a80b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -72,6 +72,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_RE import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_APPINFO; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_DEVICEINFO; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_SCREENSHOT; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SEND_CONFIGURATION; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETCANNEDMESSAGES; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETMUSICINFO; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETMUSICSTATE; @@ -96,6 +97,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CAL import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CANNEDMESSAGES; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CANNEDMESSAGES_TYPE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONFIG; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM; @@ -485,6 +487,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mDeviceSupport.onEnableRealtimeHeartRateMeasurement(enable); break; } + case ACTION_SEND_CONFIGURATION: { + String config = intent.getStringExtra(EXTRA_CONFIG); + mDeviceSupport.onSendConfiguration(config); + break; + } case ACTION_TEST_NEW_FUNCTION: { mDeviceSupport.onTestNewFunction(); break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index 0772633d..a90ee63c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -320,6 +320,14 @@ public class ServiceDeviceSupport implements DeviceSupport { delegate.onDeleteCalendarEvent(type, id); } + @Override + public void onSendConfiguration(String config) { + if (checkBusy("send configuration: " + config)) { + return; + } + delegate.onSendConfiguration(config); + } + @Override public void onTestNewFunction() { if (checkBusy("test new function event")) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index d8754e80..e8602b76 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -26,6 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; @@ -56,7 +57,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateA import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.DateDisplay; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; @@ -401,6 +402,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { LOG.info("Attempting to set wear location..."); BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC8); if (characteristic != null) { + builder.notify(characteristic, true); int location = MiBandCoordinator.getWearLocation(getDevice().getAddress()); switch (location) { case 0: // left hand @@ -410,6 +412,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { builder.write(characteristic, MiBand2Service.WEAR_LOCATION_RIGHT_WRIST); break; } + builder.notify(characteristic, false); // TODO: this should actually be in some kind of finally-block in the queue } return this; } @@ -444,6 +447,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { BluetoothGattCharacteristic characteristicHRControlPoint = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress()); if (characteristicHRControlPoint != null) { + builder.notify(characteristicHRControlPoint, true); if (enableHrSleepSupport) { LOG.info("Enabling heartrate sleep support..."); builder.write(characteristicHRControlPoint, MiBand2Service.COMMAND_ENABLE_HR_SLEEP_MEASUREMENT); @@ -451,6 +455,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { LOG.info("Disabling heartrate sleep support..."); builder.write(characteristicHRControlPoint, MiBand2Service.COMMAND_DISABLE_HR_SLEEP_MEASUREMENT); } + builder.notify(characteristicHRControlPoint, false); // TODO: this should run in some kind of finally-block in the queue } return this; } @@ -1154,13 +1159,30 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } } + @Override + public void onSendConfiguration(String config) { + TransactionBuilder builder = null; + try { + builder = performInitialized("Sending configuration for option: " + config); + switch (config) { + case MiBandConst.PREF_MI2_DATEFORMAT: + setDateDisplay(builder); + break; + } + builder.queue(getQueue()); + } catch (IOException e) { + GB.toast("Error setting configuration", Toast.LENGTH_LONG, GB.ERROR, e); + } + } + @Override public void onTestNewFunction() { } - public MiBand2Support setDateDisplay(DateDisplay displayConfig, TransactionBuilder builder) { - LOG.info("Setting date display to " + displayConfig); - switch (displayConfig) { + private MiBand2Support setDateDisplay(TransactionBuilder builder) { + DateTimeDisplay dateTimeDisplay = MiBand2Coordinator.getDateDisplay(getContext()); + LOG.info("Setting date display to " + dateTimeDisplay); + switch (dateTimeDisplay) { case TIME: builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), MiBand2Service.DATEFORMAT_TIME); break; @@ -1174,8 +1196,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void phase2Initialize(TransactionBuilder builder) { LOG.info("phase2Initialize..."); enableFurtherNotifications(builder, true); - setDateDisplay(DateDisplay.TIME, builder); + setDateDisplay(builder); setWearLocation(builder); setHeartrateSleepSupport(builder); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index 35cf1e0e..fafab86f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -1190,6 +1190,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + @Override + public void onSendConfiguration(String config) { + // nothing yet + } + @Override public void onTestNewFunction() { try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java deleted file mode 100644 index 0ac7de53..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/DateDisplay.java +++ /dev/null @@ -1,6 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; - -public enum DateDisplay { - TIME, - DATE_TIME -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java index dcf14ada..2b37852a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java @@ -157,6 +157,13 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { } } + @Override + public void onSendConfiguration(String config) { + if (reconnect()) { + super.onSendConfiguration(config); + } + } + @Override public void onTestNewFunction() { if (reconnect()) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java index e53d63b8..a5f3e0d3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java @@ -272,6 +272,11 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport { return false; } + @Override + public void onSendConfiguration(String config) { + + } + @Override public void onTestNewFunction() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 71acbb87..9c8add33 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -215,6 +215,12 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport sendToDevice(bytes); } + @Override + public void onSendConfiguration(String config) { + byte[] bytes = gbDeviceProtocol.encodeSendConfiguration(config); + sendToDevice(bytes); + } + @Override public void onTestNewFunction() { byte[] bytes = gbDeviceProtocol.encodeTestNewFunction(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index 55d4c10c..c4fe1e73 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -94,6 +94,10 @@ public abstract class GBDeviceProtocol { return null; } + public byte[] encodeSendConfiguration(String config) { + return null; + } + public byte[] encodeTestNewFunction() { return null; } public GBDeviceEvent[] decodeResponse(byte[] responseData) { @@ -103,4 +107,5 @@ public abstract class GBDeviceProtocol { public GBDevice getDevice() { return mDevice; } + } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 830f7002..e2aff8f5 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -113,5 +113,13 @@ 3 1 + + @string/dateformat_time + @string/dateformat_date_time + + + @string/p_dateformat_time + @string/p_dateformat_datetime + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00ece7d2..7e9e7dd2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -345,4 +345,7 @@ Vibration + Mi2: Date Format + Time + diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index a89f911c..df90cb5b 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -9,4 +9,7 @@ ring alarm_clock + dateformat_time + dateformat_datetime + diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index faf1d732..b2a5c363 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -43,6 +43,15 @@ android:maxLength="2" android:title="@string/miband_prefs_device_time_offset_hours" /> + + + Date: Sun, 13 Nov 2016 21:29:58 +0100 Subject: [PATCH 019/147] Pebble 2: add not yet working code (at least not without weird workarounds) This is is a pain because of tons of weird pairing issues --- .../devices/pebble/PebbleIoThread.java | 20 +- .../devices/pebble/ble/PebbleGATTClient.java | 219 ++++++++++++++++++ .../devices/pebble/ble/PebbleGATTServer.java | 159 +++++++++++++ .../devices/pebble/ble/PebbleLESupport.java | 140 +++++++++++ 4 files changed, 532 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 84b977e1..a67d44f1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -42,6 +42,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PBWReader; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleInstallable; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; +import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble.PebbleLESupport; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -75,6 +76,8 @@ class PebbleIoThread extends GBDeviceIoThread { private Socket mTCPSocket = null; // for emulator private InputStream mInStream = null; private OutputStream mOutStream = null; + private PebbleLESupport mPebbleLESupport; + private boolean mQuit = false; private boolean mIsConnected = false; private boolean mIsInstalling = false; @@ -180,12 +183,11 @@ class PebbleIoThread extends GBDeviceIoThread { mIsTCP = false; BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(btDeviceAddress); if (btDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE) { - LOG.info("Ok this seems to be a LE Pebble, will try something that does not work :P"); - mInStream = new PipedInputStream(); // fake so that io blocks - mOutStream = new PipedOutputStream(); // fake so that io blocks - //new PebbleLESupport(this.getContext(),btDeviceAddress,(PipedInputStream)mInStream,(PipedOutputStream)mOutStream); // secret branch :P - } - else { + LOG.info("Ok this seems to be a LE Pebble, try LE Support, trouble ahead!"); + mInStream = new PipedInputStream(); + mOutStream = new PipedOutputStream(); + mPebbleLESupport = new PebbleLESupport(this.getContext(),btDeviceAddress,(PipedInputStream)mInStream,(PipedOutputStream)mOutStream); // secret branch :P + } else { ParcelUuid uuids[] = btDevice.getUuids(); if (uuids == null) { return false; @@ -708,6 +710,7 @@ class PebbleIoThread extends GBDeviceIoThread { } catch (IOException e) { e.printStackTrace(); } + mBtSocket = null; } if (mTCPSocket != null) { try { @@ -715,6 +718,11 @@ class PebbleIoThread extends GBDeviceIoThread { } catch (IOException e) { e.printStackTrace(); } + mTCPSocket = null; + } + if (mPebbleLESupport != null) { + mPebbleLESupport.close(); + mPebbleLESupport = null; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java new file mode 100644 index 00000000..942409f4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -0,0 +1,219 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.content.Context; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE; + + +class PebbleGATTClient extends BluetoothGattCallback { + + private static final Logger LOG = LoggerFactory.getLogger(PebbleGATTClient.class); + + private static final UUID SERVICE_UUID = UUID.fromString("0000fed9-0000-1000-8000-00805f9b34fb"); + private static final UUID CONNECTIVITY_CHARACTERISTIC = UUID.fromString("00000001-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID PAIRING_TRIGGER_CHARACTERISTIC = UUID.fromString("00000002-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID MTU_CHARACTERISTIC = UUID.fromString("00000003-328e-0fbb-c642-1aa6699bdada"); + private static final UUID CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.fromString("00000005-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID CHARACTERISTIC_CONFIGURATION_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + + private final String mBtDeviceAddress; + private final BluetoothDevice mBtDevice; + private final Context mContext; + + private boolean oldPebble = false; + private boolean doPairing = true; + private boolean removeBond = false; + private BluetoothGatt mBluetoothGatt; + + PebbleGATTClient(Context context, BluetoothDevice btDevice) { + mContext = context; + mBtDevice = btDevice; + mBtDeviceAddress = btDevice.getAddress(); + } + + boolean initialize() { + connectToPebble(mBtDevice); + return true; + } + + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { + LOG.info("onCharacteristicChanged() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + return; + } + LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); + } + + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { + LOG.info("onCharacteristicRead() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + return; + } + LOG.info("onCharacteristicRead() status = " + status); + if (status == BluetoothGatt.GATT_SUCCESS) { + LOG.info("onCharacteristicRead()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); + + if (oldPebble) { + subscribeToConnectivity(gatt); + } else { + subscribeToConnectionParams(gatt); + } + } + } + + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { + LOG.info("onConnectionStateChange() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + return; + } + LOG.info("onConnectionStateChange() status = " + status + " newState = " + newState); + if (newState == BluetoothGatt.STATE_CONNECTED) { + LOG.info("calling discoverServices()"); + gatt.discoverServices(); + + } + } + + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { + LOG.info("onCharacteristcsWrite unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + return; + } + LOG.info("onCharacteristicWrite() " + characteristic.getUuid()); + if (characteristic.getUuid().equals(PAIRING_TRIGGER_CHARACTERISTIC) || characteristic.getUuid().equals(CONNECTIVITY_CHARACTERISTIC)) { + mBtDevice.createBond(); // did not work when last tried + + if (oldPebble) { + subscribeToConnectivity(gatt); + } else { + subscribeToConnectionParams(gatt); + } + } + } + + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor bluetoothGattDescriptor, int status) { + if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { + LOG.info("onDescriptorWrite() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + return; + } + LOG.info("onDescriptorWrite() status=" + status); + + UUID CHARACTERISTICUUID = bluetoothGattDescriptor.getCharacteristic().getUuid(); + + if (CHARACTERISTICUUID.equals(CONNECTION_PARAMETERS_CHARACTERISTIC)) { + subscribeToConnectivity(gatt); + } else if (CHARACTERISTICUUID.equals(CONNECTIVITY_CHARACTERISTIC)) { + subscribeToMTU(gatt); + } else if (CHARACTERISTICUUID.equals(MTU_CHARACTERISTIC)) { + setMTU(gatt); + } + } + + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { + LOG.info("onServicesDiscovered() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + return; + } + LOG.info("onServicesDiscovered() status = " + status); + if (status == BluetoothGatt.GATT_SUCCESS) { + BluetoothGattCharacteristic connectionPararmharacteristic = gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTION_PARAMETERS_CHARACTERISTIC); + oldPebble = connectionPararmharacteristic == null; + + if (oldPebble) { + LOG.info("This seems to be an older le enabled pebble"); + } + + if (doPairing) { + BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(PAIRING_TRIGGER_CHARACTERISTIC); + if ((characteristic.getProperties() & PROPERTY_WRITE) != 0) { + characteristic.setValue(new byte[]{0, 1}); // bits 0=1 1=no slave sec 2=kk 3=samsung kk + gatt.writeCharacteristic(characteristic); + } + else { + LOG.info("This seems to be some <4.0 FW Pebble, reading pairing trigger"); + gatt.readCharacteristic(characteristic); + } + } else { + if (oldPebble) { + subscribeToConnectivity(gatt); + } else { + subscribeToConnectionParams(gatt); + } + } + } + } + + private void connectToPebble(BluetoothDevice btDevice) { + if (removeBond) { + try { + Method m = btDevice.getClass() + .getMethod("removeBond", (Class[]) null); + m.invoke(btDevice, (Object[]) null); + } catch (Exception e) { + LOG.warn(e.getMessage()); + } + try { + Thread.sleep(1000); + } catch (InterruptedException ignore) { + } + } + if (mBluetoothGatt != null) { + this.close(); + } + mBtDevice.createBond(); + mBluetoothGatt = btDevice.connectGatt(mContext, false, this); + } + + private void subscribeToConnectivity(BluetoothGatt gatt) { + LOG.info("subscribing to connectivity characteristic"); + BluetoothGattDescriptor descriptor = gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTIVITY_CHARACTERISTIC).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + gatt.writeDescriptor(descriptor); + gatt.setCharacteristicNotification(gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTIVITY_CHARACTERISTIC), true); + } + + private void subscribeToMTU(BluetoothGatt gatt) { + LOG.info("subscribing to mtu characteristic"); + BluetoothGattDescriptor descriptor = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + gatt.writeDescriptor(descriptor); + gatt.setCharacteristicNotification(gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC), true); + } + + private void subscribeToConnectionParams(BluetoothGatt gatt) { + LOG.info("subscribing to connection parameters characteristic"); + BluetoothGattDescriptor descriptor = gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTION_PARAMETERS_CHARACTERISTIC).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + gatt.writeDescriptor(descriptor); + gatt.setCharacteristicNotification(gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTION_PARAMETERS_CHARACTERISTIC), true); + } + + private void setMTU(BluetoothGatt gatt) { + LOG.info("setting MTU"); + BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC); + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); + descriptor.setValue(new byte[]{0x0b, 0x01}); // unknown + gatt.writeCharacteristic(characteristic); + } + + public void close() { + if (mBluetoothGatt != null) { + mBluetoothGatt.disconnect(); + mBluetoothGatt.close(); + mBluetoothGatt = null; + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java new file mode 100644 index 00000000..81edbc60 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -0,0 +1,159 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattServer; +import android.bluetooth.BluetoothGattServerCallback; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.content.Context; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +class PebbleGATTServer extends BluetoothGattServerCallback { + private static final Logger LOG = LoggerFactory.getLogger(PebbleGATTServer.class); + private static final UUID WRITE_CHARACTERISTICS = UUID.fromString("10000001-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID READ_CHARACTERISTICS = UUID.fromString("10000002-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID CHARACTERISTICS_CONFIGURATION_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + private static final UUID SERVER_SERVICE = UUID.fromString("10000000-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID SERVER_SERVICE_BADBAD = UUID.fromString("BADBADBA-DBAD-BADB-ADBA-BADBADBADBAD"); + private final BluetoothDevice mBtDevice; + private final PebbleLESupport mPebbleLESupport; + private Context mContext; + private BluetoothGattServer mBluetoothGattServer; + private BluetoothGattCharacteristic writeCharacteristics; + + PebbleGATTServer(PebbleLESupport pebbleLESupport, Context context, BluetoothDevice btDevice) { + mContext = context; + mBtDevice = btDevice; + mPebbleLESupport = pebbleLESupport; + } + + boolean initialize() { + BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE); + + mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this); + + BluetoothGattService pebbleGATTService = new BluetoothGattService(SERVER_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY); + pebbleGATTService.addCharacteristic(new BluetoothGattCharacteristic(READ_CHARACTERISTICS, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ)); + + writeCharacteristics = new BluetoothGattCharacteristic(WRITE_CHARACTERISTICS, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_WRITE); + + writeCharacteristics.addDescriptor(new BluetoothGattDescriptor(CHARACTERISTICS_CONFIGURATION_DESCRIPTOR, BluetoothGattDescriptor.PERMISSION_WRITE)); + pebbleGATTService.addCharacteristic(writeCharacteristics); + mBluetoothGattServer.addService(pebbleGATTService); + + + final BluetoothGattService badbadService = new BluetoothGattService(SERVER_SERVICE_BADBAD, BluetoothGattService.SERVICE_TYPE_PRIMARY); + badbadService.addCharacteristic(new BluetoothGattCharacteristic(SERVER_SERVICE_BADBAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ)); + mBluetoothGattServer.addService(badbadService); + return true; + } + + synchronized void sendDataToPebble(byte[] data) { + LOG.info("send data to pebble " + GB.hexdump(data, 0, -1)); + writeCharacteristics.setValue(data.clone()); + + + mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false); + + try { + Thread.sleep(100); // FIXME: bad bad, I mean BAAAD + } catch (InterruptedException ignore) { + } + } + + synchronized private void sendAckToPebble(int serial) { + LOG.info("send ack to pebble for serial " + serial); + + writeCharacteristics.setValue(new byte[]{(byte) (((serial << 3) | 1) & 0xff)}); + + mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false); + + try { + Thread.sleep(100); // FIXME: bad bad, I mean BAAAD + } catch (InterruptedException ignore) { + } + } + + public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { + if (!characteristic.getUuid().equals(READ_CHARACTERISTICS)) { + LOG.warn("unexpected read request"); + return; + } + + LOG.info("will send response to read request from device: " + device.getAddress()); + if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, new byte[]{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})) { + LOG.warn("error sending response"); + } + } + + + public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { + if (!characteristic.getUuid().equals(WRITE_CHARACTERISTICS)) { + LOG.warn("unexpected write request"); + return; + } + LOG.info("write request: offset = " + offset + " value = " + GB.hexdump(value, 0, -1)); + int header = value[0] & 0xff; + int command = header & 7; + int serial = header >> 3; + if (command == 0x01) { + LOG.info("got ACK for serial = " + serial); + } + if (command == 0x02) { // some request? + LOG.info("got command 0x02"); + if (value.length > 1) { + sendDataToPebble(new byte[]{0x03, 0x19, 0x19}); // no we dont know what that means + mPebbleLESupport.createPipedInputReader(); // FIXME: maybe not here + } else { + sendDataToPebble(new byte[]{0x03}); // no we dont know what that means + } + } else if (command == 0) { // normal package + LOG.info("got PPoGATT package serial = " + serial + " sending ACK"); + + sendAckToPebble(serial); + + mPebbleLESupport.writeToPipedOutputStream(value, 1, value.length - 1); + } + } + + public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { + LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState); + } + + public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { + + if (!descriptor.getCharacteristic().getUuid().equals(WRITE_CHARACTERISTICS)) { + LOG.warn("unexpected write request"); + return; + } + + LOG.info("onDescriptorWriteRequest() notifications enabled = " + (value[0] == 1)); + if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, value)) { + LOG.warn("onDescriptorWriteRequest() error sending response!"); + } + } + + public void onServiceAdded(int status, BluetoothGattService service) { + LOG.info("onServiceAdded() status = " + status + " service = " + service.getUuid()); + } + + public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) { + //LOG.info("onNotificationSent() status = " + status + " to device " + mmBtDevice.getAddress()); + } + + void close() { + mBluetoothGattServer.cancelConnection(mBtDevice); + mBluetoothGattServer.clearServices(); + mBluetoothGattServer.close(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java new file mode 100644 index 00000000..1d600019 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -0,0 +1,140 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattServerCallback; +import android.bluetooth.BluetoothManager; +import android.content.Context; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +public class PebbleLESupport extends BluetoothGattServerCallback { + private static final Logger LOG = LoggerFactory.getLogger(PebbleLESupport.class); + private PipeReader mPipeReader; + private PebbleGATTServer mPebbleGATTServer; + private PebbleGATTClient mPebbleGATTClient; + private PipedInputStream mPipedInputStream; + private PipedOutputStream mPipedOutputStream; + + public PebbleLESupport(Context context, final String btDeviceAddress, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) { + + mPipedInputStream = new PipedInputStream(); + mPipedOutputStream = new PipedOutputStream(); + try { + pipedOutputStream.connect(mPipedInputStream); + pipedInputStream.connect(mPipedOutputStream); + } catch (IOException e) { + LOG.warn("could not connect input stream"); + } + + BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); + BluetoothAdapter adapter = manager.getAdapter(); + BluetoothDevice btDevice = adapter.getRemoteDevice(btDeviceAddress); + mPebbleGATTServer = new PebbleGATTServer(this, context, btDevice); + mPebbleGATTServer.initialize(); + + mPebbleGATTClient = new PebbleGATTClient(context, btDevice); + mPebbleGATTClient.initialize(); + } + + void writeToPipedOutputStream(byte[] value, int offset, int count) { + try { + mPipedOutputStream.write(value, offset, count); + } catch (IOException e) { + LOG.warn("error writing to output stream"); + } + } + + public void close() { + destroyPipedInputReader(); + if (mPebbleGATTServer != null) { + mPebbleGATTServer.close(); + mPebbleGATTServer = null; + } + if (mPebbleGATTClient != null) { + mPebbleGATTClient.close(); + mPebbleGATTClient = null; + } + } + + void createPipedInputReader() { + if (mPipeReader == null) { + mPipeReader = new PipeReader(); + } + if (!mPipeReader.isAlive()) { + mPipeReader.start(); + } + } + + private void destroyPipedInputReader() { + if (mPipeReader != null) { + mPipeReader.quit(); + mPipeReader.interrupt(); + try { + mPipeReader.join(); + } catch (InterruptedException e) { + LOG.error(e.getMessage()); + } + mPipeReader = null; + } + } + + private class PipeReader extends Thread { + int mmSequence = 0; + private boolean mQuit = false; + + @Override + public void run() { + int MTU = 339 - 3; + byte[] buf = new byte[8192]; + int bytesRead; + while (!mQuit) { + try { + // this code is very similar to iothread, that is bad + // because we are the ones who prepared the buffer, there should be no + // need to do crazy stuff just to find out the PP boundaries again. + bytesRead = mPipedInputStream.read(buf, 0, 4); + while (bytesRead < 4) { + bytesRead += mPipedInputStream.read(buf, bytesRead, 4 - bytesRead); + } + + int length = (buf[0] & 0xff) << 8 | (buf[1] & 0xff); + bytesRead = mPipedInputStream.read(buf, 4, length); + + while (bytesRead < length) { + bytesRead += mPipedInputStream.read(buf, bytesRead + 4, length - bytesRead); + } + + + int payloadToSend = bytesRead + 4; + int srcPos = 0; + while (payloadToSend > 0) { + int chunkSize = (payloadToSend < (MTU - 1)) ? payloadToSend : MTU - 1; + byte[] outBuf = new byte[chunkSize + 1]; + outBuf[0] = (byte) ((mmSequence++ << 3) & 0xff); + System.arraycopy(buf, srcPos, outBuf, 1, chunkSize); + mPebbleGATTServer.sendDataToPebble(outBuf); + srcPos += chunkSize; + payloadToSend -= chunkSize; + } + + } catch (IOException e) { + LOG.warn("IO exception"); + mQuit = true; + break; + } + } + } + + void quit() { + mQuit = true; + } + } + +} + From d8145a52f90ea31c8996617fd88a270a31d6dbd9 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 13 Nov 2016 21:33:43 +0100 Subject: [PATCH 020/147] Activate the display when lifting the wrist #323 Configurable, defaults to true --- .../devices/miband/MiBand2Coordinator.java | 5 ++++ .../devices/miband/MiBand2Service.java | 3 +++ .../devices/miband/MiBandConst.java | 3 +-- .../miband/MiBandPreferencesActivity.java | 25 ++++++++++++++++++- .../devices/miband/MiBand2Support.java | 15 +++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/miband_preferences.xml | 5 ++++ 7 files changed, 54 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 94070a19..6cf308f8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -67,4 +67,9 @@ public class MiBand2Coordinator extends MiBandCoordinator { } return DateTimeDisplay.DATE_TIME; } + + public static boolean getActivateDisplayOnLiftWrist() { + Prefs prefs = GBApplication.getPrefs(); + return prefs.getBoolean(MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT, true); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index 6f397364..c29451e9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -297,6 +297,9 @@ 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}; + static { MIBAND_DEBUG = new HashMap<>(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java index 145f8179..f4d5ec9f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java @@ -18,8 +18,7 @@ public final class MiBandConst { public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection"; public static final String PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS = "mi_device_time_offset_hours"; public static final String PREF_MI2_DATEFORMAT = "mi2_dateformat"; - - public static final String PREF_TRY_SMS = "mi_try_sms"; + public static final String PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT = "mi2_activate_display_on_lift_wrist"; public static final String ORIGIN_INCOMING_CALL = "incoming_call"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java index cb289b1c..af1d07a8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java @@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.GB; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_INCOMING_CALL; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DATEFORMAT; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ADDRESS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS; @@ -45,11 +46,12 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { return true; } }); + final Preference setDateFormat = findPreference(PREF_MI2_DATEFORMAT); setDateFormat.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - getListView().post(new Runnable() { // delayed execution so that the preferences are applied first + invokeLater(new Runnable() { @Override public void run() { GBApplication.deviceService().onSendConfiguration(PREF_MI2_DATEFORMAT); @@ -58,6 +60,27 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { return true; } }); + + final Preference activateDisplayOnLift = findPreference(PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT); + activateDisplayOnLift.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT); + } + }); + return true; + } + }); + } + + /** + * delayed execution so that the preferences are applied first + */ + private void invokeLater(Runnable runnable) { + getListView().post(runnable); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index e8602b76..10e6de68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -1168,6 +1168,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { case MiBandConst.PREF_MI2_DATEFORMAT: setDateDisplay(builder); break; + case MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT: + setActivateDisplayOnLiftWrist(builder); + break; } builder.queue(getQueue()); } catch (IOException e) { @@ -1193,11 +1196,23 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return this; } + private MiBand2Support setActivateDisplayOnLiftWrist(TransactionBuilder builder) { + boolean enable = MiBand2Coordinator.getActivateDisplayOnLiftWrist(); + LOG.info("Setting activate display on lift wrist to " + enable); + if (enable) { + builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), MiBand2Service.COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST); + } else { + builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC3), MiBand2Service.COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST); + } + return this; + } + public void phase2Initialize(TransactionBuilder builder) { LOG.info("phase2Initialize..."); enableFurtherNotifications(builder, true); setDateDisplay(builder); setWearLocation(builder); + setActivateDisplayOnLiftWrist(builder); setHeartrateSleepSupport(builder); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7e9e7dd2..7981aa24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -348,4 +348,5 @@ Mi2: Date Format Time + Activate display upon lift diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index b2a5c363..6b0e1032 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -43,6 +43,11 @@ android:maxLength="2" android:title="@string/miband_prefs_device_time_offset_hours" /> + + Date: Mon, 14 Nov 2016 17:51:33 +0100 Subject: [PATCH 021/147] Pebble: enable localStorage LocalStorage native functions are overridden to keep each watchface's settings separated. It's possible the bind method do not work on older versions of android. --- .../app_config/js/gadgetbridge_boilerplate.js | 15 +++++++++++++-- .../activities/ExternalPebbleJSActivity.java | 2 ++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index d3021c31..24de66f5 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -1,5 +1,16 @@ -//clay stores the values in the localStorage -localStorage.clear(); +if (window.Storage){ + var prefix = GBjs.getAppUUID(); + GBjs.gbLog("redefining local storage with prefix: " + prefix); + + Storage.prototype.setItem = (function(key, value) { + this.call(localStorage,prefix + key, value); + }).bind(Storage.prototype.setItem); + + Storage.prototype.getItem = (function(key) { + //GBjs.gbLog("I am about to return " + prefix + key); + return this.call(localStorage,prefix + key); + }).bind(Storage.prototype.getItem); +} function loadScript(url, callback) { // Adding the script tag to the head as suggested before diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 44d5d93a..d26691b7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -69,6 +69,8 @@ public class ExternalPebbleJSActivity extends GBActivity { webSettings.setJavaScriptEnabled(true); //needed to access the DOM webSettings.setDomStorageEnabled(true); + //needed for localstorage + webSettings.setDatabaseEnabled(true); JSInterface gbJSInterface = new JSInterface(this); myWebView.addJavascriptInterface(gbJSInterface, "GBjs"); From 4b7f47ba6c4c0d01f6198d037baad7f011a81953 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 15 Nov 2016 11:56:14 +0100 Subject: [PATCH 022/147] Pebble 2: add Workout system app to app manager on P2 and hexdump incoming HRM datalog --- .../AbstractAppManagerFragment.java | 16 ++++++++--- .../AppManagerFragmentInstalledApps.java | 11 +++++--- .../pebble/DatalogSessionHealthHR.java | 27 +++++++++++++++++++ .../DatalogSessionHealthOverlayData.java | 6 ++--- .../pebble/DatalogSessionHealthSleep.java | 6 ++--- .../pebble/DatalogSessionHealthSteps.java | 6 ++--- .../pebble/DatalogSessionPebbleHealth.java | 4 +-- .../devices/pebble/PebbleProtocol.java | 3 +++ .../gadgetbridge/util/PebbleUtils.java | 10 +++++++ 9 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index bd4670c3..451d505e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -182,10 +182,18 @@ public abstract class AbstractAppManagerFragment extends Fragment { cachedAppList.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); } */ - if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getModel()))) { - if (baseName.equals(PebbleProtocol.UUID_PEBBLE_HEALTH.toString())) { - cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - continue; + if (mGBDevice != null) { + if (PebbleUtils.hasHealth(mGBDevice.getModel())) { + if (baseName.equals(PebbleProtocol.UUID_PEBBLE_HEALTH.toString())) { + cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + continue; + } + } + if (PebbleUtils.hasHRM(mGBDevice.getModel())) { + if (baseName.equals(PebbleProtocol.UUID_WORKOUT.toString())) { + cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + continue; + } } } if (uuids == null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java index d49e9e21..6cf8bfbb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java @@ -20,9 +20,14 @@ public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment systemApps.add(new GBDeviceApp(UUID.fromString("67a32d95-ef69-46d4-a0b9-854cc62f97f9"), "Alarms (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); systemApps.add(new GBDeviceApp(UUID.fromString("18e443ce-38fd-47c8-84d5-6d0c775fbe55"), "Watchfaces (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getModel()))) { - systemApps.add(new GBDeviceApp(UUID.fromString("0863fc6a-66c5-4f62-ab8a-82ed00a98b5d"), "Send Text (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + if (mGBDevice != null) { + if (PebbleUtils.hasHealth(mGBDevice.getModel())) { + systemApps.add(new GBDeviceApp(UUID.fromString("0863fc6a-66c5-4f62-ab8a-82ed00a98b5d"), "Send Text (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } + if (PebbleUtils.hasHRM(mGBDevice.getModel())) { + systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } } return systemApps; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java new file mode 100644 index 00000000..2f70e1f4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java @@ -0,0 +1,27 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +class DatalogSessionHealthHR extends DatalogSessionPebbleHealth { + + private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthHR.class); + + DatalogSessionHealthHR(byte id, UUID uuid, int tag, byte item_type, short item_size, GBDevice device) { + super(id, uuid, tag, item_type, item_size, device); + taginfo = "(Health - HR " + tag + " )"; + } + + @Override + public boolean handleMessage(ByteBuffer datalogMessage, int length) { + LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); + + return isPebbleHealthEnabled(); + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index 98e13261..e41d8f32 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -24,7 +24,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { public DatalogSessionHealthOverlayData(byte id, UUID uuid, int tag, byte item_type, short item_size, GBDevice device) { super(id, uuid, tag, item_type, item_size, device); - taginfo = "(health - overlay data " + tag + " )"; + taginfo = "(Health - overlay data " + tag + " )"; } @Override @@ -85,7 +85,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { int durationSeconds; byte[] rawData; - public OverlayRecord(byte[] rawData) { + OverlayRecord(byte[] rawData) { this.rawData = rawData; ByteBuffer record = ByteBuffer.wrap(rawData); record.order(ByteOrder.LITTLE_ENDIAN); @@ -99,7 +99,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { this.durationSeconds = record.getInt(); } - public byte[] getRawData() { + byte[] getRawData() { if (storePebbleHealthRawRecord()) { return rawData; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index fef497f2..d6534ef7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -24,7 +24,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size, GBDevice device) { super(id, uuid, tag, item_type, item_size, device); - taginfo = "(health - sleep " + tag + " )"; + taginfo = "(Health - sleep " + tag + " )"; } @Override @@ -87,7 +87,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { int deepSleepSeconds; byte[] rawData; - public SleepRecord(byte[] rawData) { + SleepRecord(byte[] rawData) { this.rawData = rawData; ByteBuffer record = ByteBuffer.wrap(rawData); record.order(ByteOrder.LITTLE_ENDIAN); @@ -101,7 +101,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { this.deepSleepSeconds = record.getInt(); } - public byte[] getRawData() { + byte[] getRawData() { if (storePebbleHealthRawRecord()) { return rawData; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index b5bd1678..86409d51 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -22,7 +22,7 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { public DatalogSessionHealthSteps(byte id, UUID uuid, int tag, byte item_type, short item_size, GBDevice device) { super(id, uuid, tag, item_type, item_size, device); - taginfo = "(health - steps)"; + taginfo = "(Health - steps)"; } @Override @@ -110,7 +110,7 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { int light_intensity; byte[] rawData; - public StepsRecord(int timestamp, short version, byte[] rawData) { + StepsRecord(int timestamp, short version, byte[] rawData) { this.timestamp = timestamp; this.rawData = rawData; ByteBuffer record = ByteBuffer.wrap(rawData); @@ -125,7 +125,7 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { this.light_intensity = record.get() & 0xff; } - public byte[] getRawData() { + byte[] getRawData() { if (storePebbleHealthRawRecord()) { return rawData; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java index 3d26bf68..2aae2cb9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java @@ -19,12 +19,12 @@ abstract class DatalogSessionPebbleHealth extends DatalogSession { return mDevice; } - protected boolean isPebbleHealthEnabled() { + boolean isPebbleHealthEnabled() { Prefs prefs = GBApplication.getPrefs(); return prefs.getBoolean("pebble_sync_health", true); } - protected boolean storePebbleHealthRawRecord() { + boolean storePebbleHealthRawRecord() { Prefs prefs = GBApplication.getPrefs(); return prefs.getBoolean("pebble_health_store_raw", true); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index eca9afa9..34c85a6d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -366,6 +366,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private final ArrayList tmpUUIDS = new ArrayList<>(); public static final UUID UUID_PEBBLE_HEALTH = UUID.fromString("36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c"); // FIXME: store somewhere else, this is also accessed by other code + public static final UUID UUID_WORKOUT = UUID.fromString("fef82c82-7176-4e22-88de-35a3fc18d43f"); // FIXME: store somewhere else, this is also accessed by other code private static final UUID UUID_GBPEBBLE = UUID.fromString("61476764-7465-7262-6469-656775527a6c"); private static final UUID UUID_MORPHEUZ = UUID.fromString("5be44f1d-d262-4ea6-aa30-ddbec1e3cab2"); private static final UUID UUID_WHETHERNEAT = UUID.fromString("3684003b-a685-45f9-a713-abc6364ba051"); @@ -2020,6 +2021,8 @@ public class PebbleProtocol extends GBDeviceProtocol { mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size, getDevice())); } else if (uuid.equals(UUID_ZERO) && log_tag == 84) { mDatalogSessions.put(id, new DatalogSessionHealthOverlayData(id, uuid, log_tag, item_type, item_size, getDevice())); + } else if (uuid.equals(UUID_ZERO) && log_tag == 85) { + mDatalogSessions.put(id, new DatalogSessionHealthHR(id, uuid, log_tag, item_type, item_size, getDevice())); } else { mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java index bd18cde2..f6597807 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -37,4 +37,14 @@ public class PebbleUtils { public static int getFwMajor(String fwString) { return fwString.charAt(1) - 48; } + + public static boolean hasHRM(String hwRev) { + String platformName = getPlatformName(hwRev); + return "diorite".equals(platformName) || "emery".equals(platformName); + } + + public static boolean hasHealth(String hwRev) { + String platformName = getPlatformName(hwRev); + return !"aplite".equals(platformName); + } } From 82a47022fa9714f8dd96044697c63fc4c3427213 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 15 Nov 2016 12:17:51 +0100 Subject: [PATCH 023/147] Pebble 2: allow to disable/enable HRM in context menu of Workout app Okay this might be stupid since Workout seems to work also works without HRM! I assumed (wrongly) that I will disable Workout by disabling HRM anyway, better than not being able to toggle it at all! --- .../AbstractAppManagerFragment.java | 8 ++++++++ .../devices/pebble/PebbleIoThread.java | 4 ++++ .../devices/pebble/PebbleProtocol.java | 19 +++++++++++++------ app/src/main/res/menu/appmanager_context.xml | 6 ++++++ app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index 451d505e..50b86ced 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -283,6 +283,10 @@ public abstract class AbstractAppManagerFragment extends Fragment { menu.removeItem(R.id.appmanager_health_activate); menu.removeItem(R.id.appmanager_health_deactivate); } + if (!PebbleProtocol.UUID_WORKOUT.equals(selectedApp.getUUID())) { + menu.removeItem(R.id.appmanager_hrm_activate); + menu.removeItem(R.id.appmanager_hrm_deactivate); + } if (selectedApp.getType() == GBDeviceApp.Type.APP_SYSTEM || selectedApp.getType() == GBDeviceApp.Type.WATCHFACE_SYSTEM) { menu.removeItem(R.id.appmanager_app_delete); } @@ -355,7 +359,11 @@ public abstract class AbstractAppManagerFragment extends Fragment { case R.id.appmanager_health_activate: GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); return true; + case R.id.appmanager_hrm_activate: + GBApplication.deviceService().onInstallApp(Uri.parse("fake://hrm")); + return true; case R.id.appmanager_health_deactivate: + case R.id.appmanager_hrm_deactivate: GBApplication.deviceService().onAppDelete(selectedApp.getUUID()); return true; case R.id.appmanager_app_configure: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index a67d44f1..32238019 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -596,6 +596,10 @@ class PebbleIoThread extends GBDeviceIoThread { write(mPebbleProtocol.encodeSetSaneDistanceUnit(true)); return; } + if (uri.equals(Uri.parse("fake://hrm"))) { + write(mPebbleProtocol.encodeActivateHRM(true)); + return; + } String platformName = PebbleUtils.getPlatformName(gbDevice.getModel()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index 34c85a6d..530badcd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -721,10 +721,8 @@ public class PebbleProtocol extends GBDeviceProtocol { return buf.array(); } - public byte[] encodeActivateHealth(boolean activate) { + byte[] encodeActivateHealth(boolean activate) { byte[] blob; - byte command; - command = BLOBDB_INSERT; if (activate) { ByteBuffer buf = ByteBuffer.allocate(9); @@ -744,10 +742,10 @@ public class PebbleProtocol extends GBDeviceProtocol { } else { blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } - return encodeBlobdb("activityPreferences", command, BLOBDB_PREFERENCES, blob); + return encodeBlobdb("activityPreferences", BLOBDB_INSERT, BLOBDB_PREFERENCES, blob); } - public byte[] encodeSetSaneDistanceUnit(boolean sane) { + byte[] encodeSetSaneDistanceUnit(boolean sane) { byte value; if (sane) { value = 0x00; @@ -757,7 +755,13 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb("unitsDistance", BLOBDB_INSERT, BLOBDB_PREFERENCES, new byte[]{value}); } - public byte[] encodeReportDataLogSessions() { + + byte[] encodeActivateHRM(boolean activate) { + return encodeBlobdb("hrmPreferences", BLOBDB_INSERT, BLOBDB_PREFERENCES, + activate ? new byte[]{0x01} : new byte[]{0x00}); + } + + byte[] encodeReportDataLogSessions() { return encodeSimpleMessage(ENDPOINT_DATALOG, DATALOG_REPORTSESSIONS); } @@ -1249,6 +1253,9 @@ public class PebbleProtocol extends GBDeviceProtocol { if (UUID_PEBBLE_HEALTH.equals(uuid)) { return encodeActivateHealth(false); } + if (UUID_WORKOUT.equals(uuid)) { + return encodeActivateHRM(false); + } return encodeBlobdb(uuid, BLOBDB_DELETE, BLOBDB_APP, null); } else { ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_REMOVEAPP_2X); diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml index 8a2cadca..43645438 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -15,6 +15,12 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7981aa24..330d0ad7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,6 +28,8 @@ Search in Pebble Appstore Activate Deactivate + Activate HRM + Deactivate HRM Configure Move to top From 4a243ff361d8a8330e507777b1be8ce5db0c6db7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 15 Nov 2016 12:22:41 +0100 Subject: [PATCH 024/147] update Japanese and German from transifex (THANKS!) --- app/src/main/res/values-de/strings.xml | 2 ++ app/src/main/res/values-ja/strings.xml | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 4275efca..68d92581 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -284,10 +284,12 @@ Wenn Du schon deine Daten importiert hast und mit dem Ergebnis zufrieden bist, k Import erfolgreich. Fehler beim Importieren der DB: %1$s Führe Aktivitätsdaten zusammen + Beim Importieren der alten Aktivitätsdaten ist ein Fehler aufgetreten! Alte Daten löschen? Daten erfolgreich gelöscht. Löschen der Datenbank fehlgeschlagen. Alte Aktivitätsdatenbank löschen? + Wirklich die alte Aktivitätsdatenbank löschen? Nicht importierte Aktivitätsdaten gehen verloren. Alte Aktivitätsdatenbank erfolgreich gelöscht. Löschen der alten Aktivitätsdatenbank fehlgeschlagen. Überschreiben diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 34eee75a..49a80800 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -22,6 +22,7 @@ 削除 キャッシュから削除 再インストール + Pebble Appstore で検索 アクティベート 非アクティベート 設定 @@ -85,6 +86,8 @@ 場所の取得 緯度 経度 + 場所の更新を継続する + 実行時に現在位置の取得を試みます。フォールバックとして保存されている場所を使用します ネットワークの場所を有効にしてください 場所を取得しました 通知プロトコルを強制する @@ -127,6 +130,10 @@ デバイスをペアリングするには、AndroidのBluetoothペアリングのダイアログを使用します。 お使いのMi Bandをペアリング %sとペアリング… + %1$s (%2$s) と結合を作成中 + %1$s (%2$s) とペアにすることができません + 結合の進行中: %1$s (%2$s) + 既に %1$s (%2$s) と結合しています。接続中… MACアドレスが渡されませんでした。ペアリングできません。 デバイス固有の設定 Mi Bandの設定 @@ -302,7 +309,8 @@ Mi Band、Pebble Health、Morpheuz からデータをインポートすること 削除 バイブレーション - %1$s (%2$s) と結合を作成中 - 結合の進行中: %1$s (%2$s) - 既に %1$s (%2$s) と結合しています。接続中… + Mi2: 日付形式 + 時間 + + 持ち上げ時に表示を有効にする From 10122369899ce70fe9df8d0d64b7d094684950f9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 15 Nov 2016 12:29:18 +0100 Subject: [PATCH 025/147] bump version, not yet release time! --- CHANGELOG.md | 3 +++ app/build.gradle | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3faf21c6..c7e25358 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version 0.14.0 (next) +* Pebble 2: Initial unstable pre-alpha support for P2/PT2 using BLE (won't be usable without weird workarounds for pairing) + ####Version 0.13.9 * Pebble: use the last known location for setting sunrise and sunset * Pebble: fix Health disappering forever when deactivating through app manager (and get it back for affected users) diff --git a/app/build.gradle b/app/build.gradle index 6f61b3bc..a3031eb9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.13.9" - versionCode 71 + versionName "0.14.0" + versionCode 72 } buildTypes { release { From 163a7bdf151faf0270c9804c713f5847491a68de Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 15 Nov 2016 23:21:13 +0100 Subject: [PATCH 026/147] Pebble 2: save heart rate values to database (hopefully) --- .../gadgetbridge/daogen/GBDaoGenerator.java | 20 +++++++------- .../schema/GadgetbridgeUpdate_14.java | 26 +++++++++++++++++++ .../database/schema/SchemaMigration.java | 2 +- .../pebble/DatalogSessionHealthSteps.java | 12 ++++++++- 4 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 9a834ca4..3a197772 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -26,20 +26,21 @@ import de.greenrobot.daogenerator.Schema; */ public class GBDaoGenerator { - public static final String VALID_FROM_UTC = "validFromUTC"; - public static final String VALID_TO_UTC = "validToUTC"; + private static final String VALID_FROM_UTC = "validFromUTC"; + private static final String VALID_TO_UTC = "validToUTC"; private static final String MAIN_PACKAGE = "nodomain.freeyourgadget.gadgetbridge"; private static final String MODEL_PACKAGE = MAIN_PACKAGE + ".model"; private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate"; private static final String OVERRIDE = "@Override"; - public static final String SAMPLE_RAW_INTENSITY = "rawIntensity"; - public static final String SAMPLE_STEPS = "steps"; - public static final String SAMPLE_RAW_KIND = "rawKind"; - public static final String TIMESTAMP_FROM = "timestampFrom"; - public static final String TIMESTAMP_TO = "timestampTo"; + private static final String SAMPLE_RAW_INTENSITY = "rawIntensity"; + private static final String SAMPLE_STEPS = "steps"; + private static final String SAMPLE_RAW_KIND = "rawKind"; + private static final String SAMPLE_HEART_RATE = "heartRate"; + private static final String TIMESTAMP_FROM = "timestampFrom"; + private static final String TIMESTAMP_TO = "timestampTo"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(13, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(14, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -172,7 +173,7 @@ public class GBDaoGenerator { } private static void addHeartRateProperties(Entity activitySample) { - activitySample.addIntProperty("heartRate").notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_HEART_RATE).notNull().codeBeforeGetterAndSetter(OVERRIDE); } private static Entity addPebbleHealthActivitySample(Schema schema, Entity user, Entity device) { @@ -181,6 +182,7 @@ public class GBDaoGenerator { activitySample.addByteArrayProperty("rawPebbleHealthData").codeBeforeGetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); return activitySample; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java new file mode 100644 index 00000000..57222047 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java @@ -0,0 +1,26 @@ +package nodomain.freeyourgadget.gadgetbridge.database.schema; + +import android.database.sqlite.SQLiteDatabase; + +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySampleDao; + +/* + * adds heart rate column to health table + */ + +public class GadgetbridgeUpdate_14 implements DBUpdateScript { + @Override + public void upgradeSchema(SQLiteDatabase db) { + if (!DBHelper.existsColumn(PebbleHealthActivitySampleDao.TABLENAME, PebbleHealthActivitySampleDao.Properties.HeartRate.columnName, db)) { + String ADD_COLUMN_HEART_RATE = "ALTER TABLE " + PebbleHealthActivitySampleDao.TABLENAME + " ADD COLUMN " + + PebbleHealthActivitySampleDao.Properties.HeartRate.columnName + " INTEGER NOT NULL;"; + db.execSQL(ADD_COLUMN_HEART_RATE); + } + } + + @Override + public void downgradeSchema(SQLiteDatabase db) { + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java index fe0e6ea5..40c6df15 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java @@ -53,7 +53,7 @@ public class SchemaMigration { private DBUpdateScript getUpdateScript(SQLiteDatabase db, int version) { try { - Class updateClass = getClass().getClassLoader().loadClass(getClass().getPackage().getName() + ".schema." + classNamePrefix + version); + Class updateClass = getClass().getClassLoader().loadClass(getClass().getPackage().getName() + "." + classNamePrefix + version); return (DBUpdateScript) updateClass.newInstance(); } catch (ClassNotFoundException e) { return null; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index 86409d51..5f5af54b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -89,7 +89,8 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { deviceId, userId, stepsRecord.getRawData(), stepsRecord.intensity, - stepsRecord.steps + stepsRecord.steps, + stepsRecord.heart_rate ); samples[j].setProvider(sampleProvider); } @@ -108,6 +109,8 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { int orientation; int intensity; int light_intensity; + int heart_rate; + byte[] rawData; StepsRecord(int timestamp, short version, byte[] rawData) { @@ -123,6 +126,13 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { this.orientation = record.get() & 0xff; this.intensity = record.getShort() & 0xffff; this.light_intensity = record.get() & 0xff; + if (version >= 7) { + // skip 7 bytes + record.getInt(); + record.getShort(); + record.get(); + this.heart_rate = record.get(); + } } byte[] getRawData() { From d7256d172e15b43736f8847a05f4bdc284cbbd84 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 15 Nov 2016 23:23:21 +0100 Subject: [PATCH 027/147] I hate java --- .../service/devices/pebble/DatalogSessionHealthSteps.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index 5f5af54b..4bcc6ae5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -131,7 +131,7 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { record.getInt(); record.getShort(); record.get(); - this.heart_rate = record.get(); + this.heart_rate = record.get() & 0xff; } } From 485cda52a8fc7278443ba46dcb5baff502fb3a81 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 15 Nov 2016 23:41:35 +0100 Subject: [PATCH 028/147] Pebble 2: actually report HRM feature to Charts Activity --- .../gadgetbridge/devices/pebble/PebbleCoordinator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index 48cbbb88..25dd856b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -22,6 +22,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMorpheuzSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class PebbleCoordinator extends AbstractDeviceCoordinator { @@ -105,7 +106,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { @Override public boolean supportsHeartRateMeasurement(GBDevice device) { - return false; + return PebbleUtils.hasHRM(device.getModel()); } @Override From d41848014b3de40befaa8d476da809f559983034 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 16 Nov 2016 11:37:36 +0100 Subject: [PATCH 029/147] fix database schema migration from 13 to 14 --- .../gadgetbridge/database/schema/GadgetbridgeUpdate_14.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java index 57222047..f7c64ebe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java @@ -15,7 +15,7 @@ public class GadgetbridgeUpdate_14 implements DBUpdateScript { public void upgradeSchema(SQLiteDatabase db) { if (!DBHelper.existsColumn(PebbleHealthActivitySampleDao.TABLENAME, PebbleHealthActivitySampleDao.Properties.HeartRate.columnName, db)) { String ADD_COLUMN_HEART_RATE = "ALTER TABLE " + PebbleHealthActivitySampleDao.TABLENAME + " ADD COLUMN " - + PebbleHealthActivitySampleDao.Properties.HeartRate.columnName + " INTEGER NOT NULL;"; + + PebbleHealthActivitySampleDao.Properties.HeartRate.columnName + " INTEGER NOT NULL DEFAULT 0;"; db.execSQL(ADD_COLUMN_HEART_RATE); } } From 42901a295d49137506a3479d5c85c3cca7772f53 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 18 Nov 2016 12:33:12 +0100 Subject: [PATCH 030/147] Pebble: pretend the clay-settings key is always present (but empty per default) in the localStorage --- .../main/assets/app_config/js/gadgetbridge_boilerplate.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index 24de66f5..035fd08c 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -7,8 +7,12 @@ if (window.Storage){ }).bind(Storage.prototype.setItem); Storage.prototype.getItem = (function(key) { - //GBjs.gbLog("I am about to return " + prefix + key); - return this.call(localStorage,prefix + key); +// console.log("I am about to return " + prefix + key); + var def = null; + if(key == 'clay-settings') { + def = '{}'; + } + return this.call(localStorage,prefix + key) || def; }).bind(Storage.prototype.getItem); } From 96a16245dfb86acfc951182bac661cd613622b34 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 18 Nov 2016 21:31:55 +0100 Subject: [PATCH 031/147] Update to MPAndroidChart 3.0.1 --- app/build.gradle | 2 +- .../activities/charts/AbstractChartFragment.java | 10 ---------- .../activities/charts/TimestampValueFormatter.java | 5 ----- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a3031eb9..75a6248b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,7 +68,7 @@ dependencies { compile 'com.android.support:design:23.3.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.0' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.1' compile 'com.github.pfichtner:durationformatter:0.1.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.2' compile 'net.e175.klaus:solarpositioning:0.0.9' diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index e314023d..aafb0680 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -770,11 +770,6 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { String dateString = annotationDateFormat.format(date); return dateString; } - - @Override - public int getDecimalDigits() { - return 0; - } } protected static class PreformattedXIndexLabelFormatter implements IAxisValueFormatter { @@ -792,11 +787,6 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } return xLabels.get(index); } - - @Override - public int getDecimalDigits() { - return 0; - } } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java index 0f4c592c..437330cb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java @@ -32,9 +32,4 @@ public class TimestampValueFormatter implements IAxisValueFormatter { String dateString = dateFormat.format(date); return dateString; } - - @Override - public int getDecimalDigits() { - return 0; - } } From 66e3de91689ffc925340d4280215477eff36dbee Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 18 Nov 2016 22:38:39 +0100 Subject: [PATCH 032/147] Pebble 2: improve connection problems (at least for me) --- .../service/devices/pebble/ble/PebbleGATTClient.java | 6 +++--- .../service/devices/pebble/ble/PebbleLESupport.java | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index 942409f4..fa29a666 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -94,7 +94,7 @@ class PebbleGATTClient extends BluetoothGattCallback { } LOG.info("onCharacteristicWrite() " + characteristic.getUuid()); if (characteristic.getUuid().equals(PAIRING_TRIGGER_CHARACTERISTIC) || characteristic.getUuid().equals(CONNECTIVITY_CHARACTERISTIC)) { - mBtDevice.createBond(); // did not work when last tried + //mBtDevice.createBond(); // did not work when last tried if (oldPebble) { subscribeToConnectivity(gatt); @@ -139,7 +139,7 @@ class PebbleGATTClient extends BluetoothGattCallback { if (doPairing) { BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(PAIRING_TRIGGER_CHARACTERISTIC); if ((characteristic.getProperties() & PROPERTY_WRITE) != 0) { - characteristic.setValue(new byte[]{0, 1}); // bits 0=1 1=no slave sec 2=kk 3=samsung kk + characteristic.setValue(new byte[]{1}); gatt.writeCharacteristic(characteristic); } else { @@ -173,7 +173,7 @@ class PebbleGATTClient extends BluetoothGattCallback { if (mBluetoothGatt != null) { this.close(); } - mBtDevice.createBond(); + //mBtDevice.createBond(); mBluetoothGatt = btDevice.connectGatt(mContext, false, this); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 1d600019..c8faee1e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattServerCallback; import android.bluetooth.BluetoothManager; import android.content.Context; @@ -13,7 +12,7 @@ import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; -public class PebbleLESupport extends BluetoothGattServerCallback { +public class PebbleLESupport { private static final Logger LOG = LoggerFactory.getLogger(PebbleLESupport.class); private PipeReader mPipeReader; private PebbleGATTServer mPebbleGATTServer; From 51fa31aa6639a0403c01146165d69a1187d8c490 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 18 Nov 2016 23:30:41 +0100 Subject: [PATCH 033/147] Fix BLE type conversions problems --- .../service/btle/BLETypeConversions.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index ad36def4..727e6c14 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -82,11 +82,11 @@ public class BLETypeConversions { int year = toUint16(value[0], value[1]); GregorianCalendar timestamp = new GregorianCalendar( year, - value[2], - value[3], - value[4], - value[5], - value[6] + (value[2] & 0xff) - 1, + value[3] & 0xff, + value[4] & 0xff, + value[5] & 0xff, + value[6] & 0xff ); if (honorDeviceTimeOffset) { @@ -103,7 +103,7 @@ public class BLETypeConversions { } public static int toUint16(byte... bytes) { - return bytes[0] | (bytes[1] << 8); + return (bytes[0] & 0xff) | ((bytes[1] & 0xff) << 8); } public static byte[] fromUint16(int value) { @@ -156,7 +156,7 @@ public class BLETypeConversions { /** * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.dst_offset.xml - * @param Calendar + * @param now * @return the DST offset for the given time; 0 if none; 255 if unknown */ public static byte mapDstOffset(Calendar now) { From 4dc085de5721cdbd7eae77b1cc2e9383fc3a78cf Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 18 Nov 2016 23:36:27 +0100 Subject: [PATCH 034/147] Pebble 2: React to MTU notifications, fixes LE for older pebbles also It seems that setting the MTU on older pebbles does not work, so just use what we can use. Maybe old pebbles need setMTU() which only works on Android 5+, we could use that conditionally... --- .../devices/pebble/ble/PebbleGATTClient.java | 16 ++++++++++++---- .../devices/pebble/ble/PebbleLESupport.java | 10 +++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index fa29a666..d8fa0a33 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -15,6 +15,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static android.bluetooth.BluetoothGattCharacteristic.FORMAT_UINT16; import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE; @@ -32,15 +33,17 @@ class PebbleGATTClient extends BluetoothGattCallback { private final String mBtDeviceAddress; private final BluetoothDevice mBtDevice; private final Context mContext; + private final PebbleLESupport mPebbleLESupport; private boolean oldPebble = false; private boolean doPairing = true; private boolean removeBond = false; private BluetoothGatt mBluetoothGatt; - PebbleGATTClient(Context context, BluetoothDevice btDevice) { + PebbleGATTClient(PebbleLESupport pebbleLESupport, Context context, BluetoothDevice btDevice) { mContext = context; mBtDevice = btDevice; + mPebbleLESupport = pebbleLESupport; mBtDeviceAddress = btDevice.getAddress(); } @@ -54,7 +57,13 @@ class PebbleGATTClient extends BluetoothGattCallback { LOG.info("onCharacteristicChanged() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); return; } - LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); + if (characteristic.getUuid().equals(MTU_CHARACTERISTIC)) { + int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0); + mPebbleLESupport.setMTU(newMTU); + LOG.info("Pebble requested MTU = " + newMTU); + } else { + LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); + } } public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { @@ -141,8 +150,7 @@ class PebbleGATTClient extends BluetoothGattCallback { if ((characteristic.getProperties() & PROPERTY_WRITE) != 0) { characteristic.setValue(new byte[]{1}); gatt.writeCharacteristic(characteristic); - } - else { + } else { LOG.info("This seems to be some <4.0 FW Pebble, reading pairing trigger"); gatt.readCharacteristic(characteristic); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index c8faee1e..55c0008b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -19,6 +19,7 @@ public class PebbleLESupport { private PebbleGATTClient mPebbleGATTClient; private PipedInputStream mPipedInputStream; private PipedOutputStream mPipedOutputStream; + private int mMTU = 20; public PebbleLESupport(Context context, final String btDeviceAddress, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) { @@ -37,7 +38,7 @@ public class PebbleLESupport { mPebbleGATTServer = new PebbleGATTServer(this, context, btDevice); mPebbleGATTServer.initialize(); - mPebbleGATTClient = new PebbleGATTClient(context, btDevice); + mPebbleGATTClient = new PebbleGATTClient(this, context, btDevice); mPebbleGATTClient.initialize(); } @@ -83,13 +84,16 @@ public class PebbleLESupport { } } + void setMTU(int mtu) { + mMTU = mtu; + } + private class PipeReader extends Thread { int mmSequence = 0; private boolean mQuit = false; @Override public void run() { - int MTU = 339 - 3; byte[] buf = new byte[8192]; int bytesRead; while (!mQuit) { @@ -113,7 +117,7 @@ public class PebbleLESupport { int payloadToSend = bytesRead + 4; int srcPos = 0; while (payloadToSend > 0) { - int chunkSize = (payloadToSend < (MTU - 1)) ? payloadToSend : MTU - 1; + int chunkSize = (payloadToSend < (mMTU - 4)) ? payloadToSend : mMTU - 4; byte[] outBuf = new byte[chunkSize + 1]; outBuf[0] = (byte) ((mmSequence++ << 3) & 0xff); System.arraycopy(buf, srcPos, outBuf, 1, chunkSize); From ddfab1cdae7341387fc8a2efde89a6ed12a04305 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 18 Nov 2016 23:45:05 +0100 Subject: [PATCH 035/147] Pebble 2: use requestMTU() on Android 5+ instead of characteristics write. This actually fixes MTU problems with older Pebbles (Time, OG) --- .../devices/pebble/ble/PebbleGATTClient.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index d8fa0a33..8121e0a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Method; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.util.GB; import static android.bluetooth.BluetoothGattCharacteristic.FORMAT_UINT16; @@ -211,10 +212,14 @@ class PebbleGATTClient extends BluetoothGattCallback { private void setMTU(BluetoothGatt gatt) { LOG.info("setting MTU"); - BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC); - BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); - descriptor.setValue(new byte[]{0x0b, 0x01}); // unknown - gatt.writeCharacteristic(characteristic); + if (GBApplication.isRunningLollipopOrLater()) { + gatt.requestMtu(339); + } else { + BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC); + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); + descriptor.setValue(new byte[]{0x0b, 0x01}); // unknown + gatt.writeCharacteristic(characteristic); + } } public void close() { From 029cc02a299adc3cc88456eea2491faab52b7111 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 19 Nov 2016 22:13:00 +0100 Subject: [PATCH 036/147] Pebble: Add Pebble specific pairing activity mainly useful for Pebble 2 since it properly pairs the Pebble after connecting with BLE Fixes #433 --- app/src/main/AndroidManifest.xml | 3 + .../devices/miband/MiBandPairingActivity.java | 10 +- .../devices/pebble/PebbleCoordinator.java | 2 +- .../devices/pebble/PebblePairingActivity.java | 177 ++++++++++++++++++ .../res/layout/activity_mi_band_pairing.xml | 2 +- .../res/layout/activity_pebble_pairing.xml | 29 +++ app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 10 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-hu/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 10 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values/strings.xml | 22 ++- 19 files changed, 249 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java create mode 100644 app/src/main/res/layout/activity_pebble_pairing.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee643206..d97e5112 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -256,6 +256,9 @@ + getPairingActivity() { - return null; + return PebblePairingActivity.class; } public Class getPrimaryActivity() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java new file mode 100644 index 00000000..f6fe4f60 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java @@ -0,0 +1,177 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.pebble; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; +import android.widget.TextView; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; +import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity; +import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class PebblePairingActivity extends GBActivity { + private static final Logger LOG = LoggerFactory.getLogger(PebblePairingActivity.class); + private TextView message; + private boolean isPairing; + private boolean isLEPebble; + private String macAddress; + + private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) { + GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE); + LOG.debug("pairing activity: device changed: " + device); + if (macAddress.equals(device.getAddress())) { + if (device.isInitialized()) { + pairingFinished(true); + } else if (device.isConnecting() || device.isInitializing()) { + LOG.info("still connecting/initializing device..."); + } + } + } + } + }; + + private final BroadcastReceiver mBondingReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + LOG.info("Bond state changed: " + device + ", state: " + device.getBondState() + ", expected address: " + macAddress); + if (macAddress != null && macAddress.equals(device.getAddress())) { + int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + if (bondState == BluetoothDevice.BOND_BONDED) { + LOG.info("Bonded with " + device.getAddress()); + if (!isLEPebble) { + performConnect(device); + } + } else if (bondState == BluetoothDevice.BOND_BONDING) { + LOG.info("Bonding in progress with " + device.getAddress()); + } else if (bondState == BluetoothDevice.BOND_NONE) { + LOG.info("Not bonded with " + device.getAddress() + ", attempting to connect anyway."); + } else { + LOG.warn("Unknown bond state for device " + device.getAddress() + ": " + bondState); + pairingFinished(false); + } + } + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_pebble_pairing); + + message = (TextView) findViewById(R.id.pebble_pair_message); + Intent intent = getIntent(); + macAddress = intent.getStringExtra(DeviceCoordinator.EXTRA_DEVICE_MAC_ADDRESS); + if (macAddress == null) { + Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show(); + startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); + finish(); + return; + } + + startPairing(); + } + + @Override + protected void onDestroy() { + try { + // just to be sure, remove the receivers -- might actually be already unregistered + LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver); + unregisterReceiver(mBondingReceiver); + } catch (IllegalArgumentException ex) { + // already unregistered, ignore + } + if (isPairing) { + stopPairing(); + } + super.onDestroy(); + } + + private void startPairing() { + isPairing = true; + message.setText(getString(R.string.pairing, macAddress)); + + IntentFilter filter = new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED); + LocalBroadcastManager.getInstance(this).registerReceiver(mPairingReceiver, filter); + filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + registerReceiver(mBondingReceiver, filter); + + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); + if (device != null) { + performPair(device); + } else { + GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR); + } + } + + private void pairingFinished(boolean pairedSuccessfully) { + LOG.debug("pairingFinished: " + pairedSuccessfully); + if (!isPairing) { + // already gone? + return; + } + + isPairing = false; + LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver); + unregisterReceiver(mBondingReceiver); + + if (pairedSuccessfully) { + Intent intent = new Intent(this, ControlCenter.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } + finish(); + } + + private void stopPairing() { + // TODO + isPairing = false; + } + + protected void performPair(BluetoothDevice device) { + int bondState = device.getBondState(); + if (bondState == BluetoothDevice.BOND_BONDED) { + GB.toast(getString(R.string.pairing_already_bonded, device.getName(), device.getAddress()), Toast.LENGTH_SHORT, GB.INFO); + return; + } + + if (bondState == BluetoothDevice.BOND_BONDING) { + GB.toast(this, getString(R.string.pairing_in_progress, device.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO); + return; + } + + GB.toast(this, getString(R.string.pairing_creating_bond_with, device.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO); + GBApplication.deviceService().disconnect(); // just to make sure... + if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE) { + isLEPebble = true; + performConnect(device); + } else { + isLEPebble = false; + device.createBond(); + } + } + + private void performConnect(BluetoothDevice device) { + GBDevice gbDevice = new GBDevice(device.getAddress(), device.getName(), DeviceType.PEBBLE); + GBApplication.deviceService().connect(gbDevice); + } +} diff --git a/app/src/main/res/layout/activity_mi_band_pairing.xml b/app/src/main/res/layout/activity_mi_band_pairing.xml index b875dd34..9df8b8c7 100644 --- a/app/src/main/res/layout/activity_mi_band_pairing.xml +++ b/app/src/main/res/layout/activity_mi_band_pairing.xml @@ -6,7 +6,7 @@ android:paddingBottom="@dimen/activity_vertical_margin" tools:context="nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPairingActivity"> - diff --git a/app/src/main/res/layout/activity_pebble_pairing.xml b/app/src/main/res/layout/activity_pebble_pairing.xml new file mode 100644 index 00000000..ec306926 --- /dev/null +++ b/app/src/main/res/layout/activity_pebble_pairing.xml @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 68d92581..0f46a0a7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -126,7 +126,7 @@ Gerät paaren Verwende den Android Bluetooth Paaren-Dialog um Dein Gerät zu paaren. Paare Dein Mi Band - Paarung mit %s… + Paarung mit %s… Kein MAC Adresse bekommen, kann nicht paaren. Gerätespezifische Einstellungen Mi Band Einstellungen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index b6e242f3..88b38e74 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -126,11 +126,11 @@ Emparejar dispositivo Usar el menú de emparejar Bluetooth de Android para emparejar el dispositivo. Emparejar tu MiBand - Emparejando con %s... - Creando emparejamiento con %1$s (%2$s) - No se ha podido emparejar con %1$s (%2$s) - Emparejamiento en progreso: %1$s (%2$s) - Emparejado con %1$s (%2$s), conectando… + Emparejando con %s... + Creando emparejamiento con %1$s (%2$s) + No se ha podido emparejar con %1$s (%2$s) + Emparejamiento en progreso: %1$s (%2$s) + Emparejado con %1$s (%2$s), conectando… Ninguna dirección MAC aprobada, no se puede emparejar. Ajustes específicos del dispositivo Ajustes de MiBand diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 133e23f5..dcf5aae5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -123,7 +123,7 @@ Coupler l\'appareil Utiliser le couplement Bluetooth d\'Android pour coupler l\'appareil Coupler votre Mi Band - Coupler avec %s... + Coupler avec %s... Aucune adresse mac fournie, ne peut être couplé Paramètres spécifiques à l\'appareil Paramètres Mi Band diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index c0d6cfaa..22caf542 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -123,7 +123,7 @@ Eszköz párosítása Használja az Android Bluetooth párosítás ablakot eszköz párosításához. Párosítsd a Mi Band-ed - Párosítás: %s… + Párosítás: %s… Nincs találat a MAC címre, nem lehet párosítani. Eszközspecifikus beállítások MI Band beállítások diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 5c0b85b4..fe603a9c 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -126,7 +126,7 @@ Accoppia dispositivo Utilizza la funzione del sistema operativo per accoppiare il dispositivo. Accoppia la Mi Band - Accoppiamento con %s… + Accoppiamento con %s… Indirizzo MAC mancante, impossibile completare l\'accoppiamento. Impostazioni specifiche dispositivo Impostazioni Mi Band diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 49a80800..39599641 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -129,11 +129,11 @@ デバイスのペアリング デバイスをペアリングするには、AndroidのBluetoothペアリングのダイアログを使用します。 お使いのMi Bandをペアリング - %sとペアリング… - %1$s (%2$s) と結合を作成中 - %1$s (%2$s) とペアにすることができません - 結合の進行中: %1$s (%2$s) - 既に %1$s (%2$s) と結合しています。接続中… + %sとペアリング… + %1$s (%2$s) と結合を作成中 + %1$s (%2$s) とペアにすることができません + 結合の進行中: %1$s (%2$s) + 既に %1$s (%2$s) と結合しています。接続中… MACアドレスが渡されませんでした。ペアリングできません。 デバイス固有の設定 Mi Bandの設定 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index ae5569dc..4c993ae8 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -96,7 +96,7 @@ 기기 페어링 기기를 페어링하려면 안드로이드 블루투스 페어링 설정을 사용하세요 Mi Band 페어링 - %s에 페어링… + %s에 페어링… MAC 주소가 통과되지 않았습니다. 페어링 할 수 없습니다. 기기 특정 설정 Mi Band 설정 diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 82c019b6..885356aa 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -83,7 +83,7 @@ sparuj urządzenie Użyj parowania bluetooth na androidzie by sparować urządzenie. Sparuj swoje Mi Band - Parowanie z %s… + Parowanie z %s… Żaden mac nie przeszedł, nie można sparować. Ustawienia danego urządzenia Ustawienia Mi Band diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f08d9e03..2041edd1 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -99,7 +99,7 @@ Сопряжение устройств Для сопряжения устройств используйте диалог Android. Сопряжение вашего Mi Band - Сопряжение с %s… + Сопряжение с %s… MAC-адрес не был передан, сопряжение не удалось. Настройки устройства Настройки Mi Band diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 58421a74..d67627cb 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -80,7 +80,7 @@ Cihaz ile eşleştir Android Bluetooth Eşleme penceresini kullanarak cihaz ile eşleştiriniz. Mi Band ile eşleştir - %s ile eşleşiyor… + %s ile eşleşiyor… MAC adresi gelmedi, eşleşemiyor. Cihaz için özel ayarlar Mi Band Ayarları diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index db7942b3..3779ce4a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -105,7 +105,7 @@ Створення пари з пристроєм Для створення пари із пристроєм використовуйте діалог Android. Створення пари із вашим Mi—Band - Створення пари із %s… + Створення пари із %s… MAC-адресу не було передано, не вдалося створити пару. Параметри пристрою Параметри Mi—Band diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index cb9e6a06..5c7f9cb1 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -67,7 +67,7 @@ %1$s (%2$s) Ghép đôi thiết bị Ghép đôi Mi Band - Đang ghép đôi với %s… + Đang ghép đôi với %s… không thể kiểm tra địa chỉ mac, không thể ghép đôi. Cài đặt cụ thể cho thiết bị Cài đặt Mi Band diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 330d0ad7..2b4d62be 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,11 +155,11 @@ Pair Device Use the Android Bluetooth Pairing dialog to pair the device. Pair your Mi Band - Pairing with %s… - "Creating bond with %1$s (%2$s)" - "Unable to pair with %1$s (%2$s)" - Bonding in progress: %1$s (%2$s) - "Already bonded with %1$s (%2$s), connecting…" + Pairing with %s… + "Creating bond with %1$s (%2$s)" + "Unable to pair with %1$s (%2$s)" + Bonding in progress: %1$s (%2$s) + "Already bonded with %1$s (%2$s), connecting…" No mac address passed, cannot pair. Device Specific Settings Mi Band Settings @@ -278,6 +278,10 @@ Alarms to reserve for upcoming events Use Heartrate Sensor to improve sleep detection Device time offset in hours (for detecting sleep of shift workers) + Mi2: Date Format + Time + + Activate display upon lift waiting for reconnect @@ -347,8 +351,8 @@ Vibration - Mi2: Date Format - Time - - Activate display upon lift + + + Pebble Pairing + A pairing dialog is expected to pop up on your Android device. If that does not happen, look in the notification drawer and accept the pairing request. After that accept the pairing request on your Pebble From c95587c9159dfd9f36fefdc0f8d51215a940ba67 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 19 Nov 2016 22:45:42 +0100 Subject: [PATCH 037/147] remove _le suffix where not appropriate --- app/src/main/res/layout/activity_pebble_pairing.xml | 4 ++-- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/activity_pebble_pairing.xml b/app/src/main/res/layout/activity_pebble_pairing.xml index ec306926..d0793d61 100644 --- a/app/src/main/res/layout/activity_pebble_pairing.xml +++ b/app/src/main/res/layout/activity_pebble_pairing.xml @@ -21,8 +21,8 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2b4d62be..cd30af06 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -354,5 +354,5 @@ Pebble Pairing - A pairing dialog is expected to pop up on your Android device. If that does not happen, look in the notification drawer and accept the pairing request. After that accept the pairing request on your Pebble + A pairing dialog is expected to pop up on your Android device. If that does not happen, look in the notification drawer and accept the pairing request. After that accept the pairing request on your Pebble From 3b250a45688c1c868c6950ea832dddb17bdae2fe Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 19 Nov 2016 23:49:18 +0100 Subject: [PATCH 038/147] Pebble 2: initial hacky reconnect support Works sometimes, at least less crashes (#432) --- .../service/devices/pebble/PebbleIoThread.java | 4 ++-- .../service/devices/pebble/ble/PebbleGATTClient.java | 4 +++- .../service/devices/pebble/ble/PebbleGATTServer.java | 3 +++ .../service/devices/pebble/ble/PebbleLESupport.java | 7 ++++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 32238019..27fa3587 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -368,8 +368,8 @@ class PebbleIoThread extends GBDeviceIoThread { } catch (InterruptedException e) { e.printStackTrace(); } - } catch (IOException e) { - if (e.getMessage() != null && e.getMessage().contains("socket closed")) { //FIXME: this does not feel right + } catch (IOException | ArrayIndexOutOfBoundsException e) { + if (e.getMessage() != null && (e instanceof ArrayIndexOutOfBoundsException || e.getMessage().contains("socket closed"))) { //FIXME: this does not feel right LOG.info(e.getMessage()); mIsConnected = false; int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index 8121e0a6..82a8aedb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -93,7 +93,9 @@ class PebbleGATTClient extends BluetoothGattCallback { if (newState == BluetoothGatt.STATE_CONNECTED) { LOG.info("calling discoverServices()"); gatt.discoverServices(); - + } + else if (newState == BluetoothGatt.STATE_DISCONNECTED){ + mPebbleLESupport.close(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java index 81edbc60..08b58f61 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -127,6 +127,9 @@ class PebbleGATTServer extends BluetoothGattServerCallback { public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState); + if (newState == BluetoothGattServer.STATE_DISCONNECTED) { + mPebbleLESupport.close(); + } } public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 55c0008b..ff1dafcb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -50,7 +50,7 @@ public class PebbleLESupport { } } - public void close() { + synchronized public void close() { destroyPipedInputReader(); if (mPebbleGATTServer != null) { mPebbleGATTServer.close(); @@ -60,6 +60,11 @@ public class PebbleLESupport { mPebbleGATTClient.close(); mPebbleGATTClient = null; } + try { + mPipedInputStream.close(); + mPipedOutputStream.close(); + } catch (IOException ignore) { + } } void createPipedInputReader() { From 4f3c46f704fc53627e72de60227380146b9beab9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Nov 2016 00:12:41 +0100 Subject: [PATCH 039/147] Pebble 2: only add second service if first one was added sucessfully --- .../service/devices/pebble/ble/PebbleGATTServer.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java index 08b58f61..25b885db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattServer; @@ -49,10 +50,6 @@ class PebbleGATTServer extends BluetoothGattServerCallback { pebbleGATTService.addCharacteristic(writeCharacteristics); mBluetoothGattServer.addService(pebbleGATTService); - - final BluetoothGattService badbadService = new BluetoothGattService(SERVER_SERVICE_BADBAD, BluetoothGattService.SERVICE_TYPE_PRIMARY); - badbadService.addCharacteristic(new BluetoothGattCharacteristic(SERVER_SERVICE_BADBAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ)); - mBluetoothGattServer.addService(badbadService); return true; } @@ -148,6 +145,11 @@ class PebbleGATTServer extends BluetoothGattServerCallback { public void onServiceAdded(int status, BluetoothGattService service) { LOG.info("onServiceAdded() status = " + status + " service = " + service.getUuid()); + if (status == BluetoothGatt.GATT_SUCCESS && service.getUuid().equals(SERVER_SERVICE)) { + final BluetoothGattService badbadService = new BluetoothGattService(SERVER_SERVICE_BADBAD, BluetoothGattService.SERVICE_TYPE_PRIMARY); + badbadService.addCharacteristic(new BluetoothGattCharacteristic(SERVER_SERVICE_BADBAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ)); + mBluetoothGattServer.addService(badbadService); + } } public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) { From e8da301da3e9c72dbfbf4b734ac121181409f5ec Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Nov 2016 22:04:49 +0100 Subject: [PATCH 040/147] Pebble 2: fix a few crashes with disconnect/ reconnect --- .../devices/pebble/PebbleIoThread.java | 22 +++++++++++++------ .../devices/pebble/ble/PebbleLESupport.java | 14 ++++++------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 27fa3587..01b43168 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -157,6 +157,14 @@ class PebbleIoThread extends GBDeviceIoThread { mEnablePebblekit = prefs.getBoolean("pebble_enable_pebblekit", false); } + private int readWithException(InputStream inputStream, byte[] buffer, int byteOffset, int byteCount) throws IOException { + int ret = inputStream.read(buffer, byteOffset, byteCount); + if (ret == -1) { + throw new IOException("broken pipe"); + } + return ret; + } + private void sendAppMessageAck(int transactionId) { Intent intent = new Intent(); intent.setAction(PEBBLEKIT_ACTION_APP_RECEIVE_ACK); @@ -323,10 +331,10 @@ class PebbleIoThread extends GBDeviceIoThread { if (mIsTCP) { mInStream.skip(6); } - int bytes = mInStream.read(buffer, 0, 4); + int bytes = readWithException(mInStream, buffer, 0, 4); while (bytes < 4) { - bytes += mInStream.read(buffer, bytes, 4 - bytes); + bytes += readWithException(mInStream, buffer, bytes, 4 - bytes); } ByteBuffer buf = ByteBuffer.wrap(buffer); @@ -336,14 +344,14 @@ class PebbleIoThread extends GBDeviceIoThread { if (length < 0 || length > 8192) { LOG.info("invalid length " + length); while (mInStream.available() > 0) { - mInStream.read(buffer); // read all + readWithException(mInStream, buffer, 0, buffer.length); // read all } continue; } - bytes = mInStream.read(buffer, 4, length); + bytes = readWithException(mInStream, buffer, 4, length); while (bytes < length) { - bytes += mInStream.read(buffer, bytes + 4, length - bytes); + bytes += readWithException(mInStream, buffer, bytes + 4, length - bytes); } if (mIsTCP) { @@ -368,8 +376,8 @@ class PebbleIoThread extends GBDeviceIoThread { } catch (InterruptedException e) { e.printStackTrace(); } - } catch (IOException | ArrayIndexOutOfBoundsException e) { - if (e.getMessage() != null && (e instanceof ArrayIndexOutOfBoundsException || e.getMessage().contains("socket closed"))) { //FIXME: this does not feel right + } catch (IOException e) { + if (e.getMessage() != null && (e.getMessage().equals("broken pipe") || e.getMessage().contains("socket closed"))) { //FIXME: this does not feel right LOG.info(e.getMessage()); mIsConnected = false; int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index ff1dafcb..449b76d9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -60,14 +60,9 @@ public class PebbleLESupport { mPebbleGATTClient.close(); mPebbleGATTClient = null; } - try { - mPipedInputStream.close(); - mPipedOutputStream.close(); - } catch (IOException ignore) { - } } - void createPipedInputReader() { + synchronized void createPipedInputReader() { if (mPipeReader == null) { mPipeReader = new PipeReader(); } @@ -76,7 +71,7 @@ public class PebbleLESupport { } } - private void destroyPipedInputReader() { + synchronized private void destroyPipedInputReader() { if (mPipeReader != null) { mPipeReader.quit(); mPipeReader.interrupt(); @@ -137,6 +132,11 @@ public class PebbleLESupport { break; } } + try { + mPipedOutputStream.close(); + mPipedInputStream.close(); + } catch (IOException ignore) { + } } void quit() { From fce86482b9282e427cf32ddc060771447d5f390b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 21 Nov 2016 14:54:01 +0100 Subject: [PATCH 041/147] Pebble 2: try an alternative way of setting the MTU (might revert) --- .../devices/pebble/ble/PebbleGATTClient.java | 13 ++++--------- .../devices/pebble/ble/PebbleGATTServer.java | 7 ++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index 82a8aedb..37b9dcea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -60,7 +60,6 @@ class PebbleGATTClient extends BluetoothGattCallback { } if (characteristic.getUuid().equals(MTU_CHARACTERISTIC)) { int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0); - mPebbleLESupport.setMTU(newMTU); LOG.info("Pebble requested MTU = " + newMTU); } else { LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); @@ -214,14 +213,10 @@ class PebbleGATTClient extends BluetoothGattCallback { private void setMTU(BluetoothGatt gatt) { LOG.info("setting MTU"); - if (GBApplication.isRunningLollipopOrLater()) { - gatt.requestMtu(339); - } else { - BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC); - BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); - descriptor.setValue(new byte[]{0x0b, 0x01}); // unknown - gatt.writeCharacteristic(characteristic); - } + BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC); + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); + descriptor.setValue(new byte[]{0x0b, 0x01}); // unknown + gatt.writeCharacteristic(characteristic); } public void close() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java index 25b885db..c6c245b4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -59,7 +59,6 @@ class PebbleGATTServer extends BluetoothGattServerCallback { mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false); - try { Thread.sleep(100); // FIXME: bad bad, I mean BAAAD } catch (InterruptedException ignore) { @@ -152,6 +151,12 @@ class PebbleGATTServer extends BluetoothGattServerCallback { } } + @Override + public void onMtuChanged(BluetoothDevice device, int mtu) { + LOG.info("Pebble requested mtu for server: " + mtu); + mPebbleLESupport.setMTU(mtu); + } + public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) { //LOG.info("onNotificationSent() status = " + status + " to device " + mmBtDevice.getAddress()); } From 647b67cfcafc0716f59ada70ab59b29922932018 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 21 Nov 2016 15:16:30 +0100 Subject: [PATCH 042/147] Pebble 2: call both requestMTU and use the characteristic write --- .../service/devices/pebble/ble/PebbleGATTClient.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index 37b9dcea..a9a2acd0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -60,7 +60,7 @@ class PebbleGATTClient extends BluetoothGattCallback { } if (characteristic.getUuid().equals(MTU_CHARACTERISTIC)) { int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0); - LOG.info("Pebble requested MTU = " + newMTU); + LOG.info("Pebble requested MTU: " + newMTU); } else { LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); } @@ -92,8 +92,7 @@ class PebbleGATTClient extends BluetoothGattCallback { if (newState == BluetoothGatt.STATE_CONNECTED) { LOG.info("calling discoverServices()"); gatt.discoverServices(); - } - else if (newState == BluetoothGatt.STATE_DISCONNECTED){ + } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { mPebbleLESupport.close(); } } @@ -112,6 +111,10 @@ class PebbleGATTClient extends BluetoothGattCallback { } else { subscribeToConnectionParams(gatt); } + } else if (characteristic.getUuid().equals(MTU_CHARACTERISTIC)) { + if (GBApplication.isRunningLollipopOrLater()) { + gatt.requestMtu(339); + } } } From eb052cead3d8255f25e3236903c93e0f9ac93c17 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 21 Nov 2016 17:29:16 +0100 Subject: [PATCH 043/147] Pebble 2: Also honor the mtu when client gets a change reported Should help older LE Pebbles --- .../service/devices/pebble/ble/PebbleGATTClient.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index a9a2acd0..5d2c6038 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -61,6 +61,7 @@ class PebbleGATTClient extends BluetoothGattCallback { if (characteristic.getUuid().equals(MTU_CHARACTERISTIC)) { int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0); LOG.info("Pebble requested MTU: " + newMTU); + mPebbleLESupport.setMTU(newMTU); } else { LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); } From 8cb2030478258f808755cd47fdd42c4036c79618 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 21 Nov 2016 17:44:43 +0100 Subject: [PATCH 044/147] update changelogs (mi2 missing) --- CHANGELOG.md | 4 +++- app/src/main/res/xml/changelog_master.xml | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e25358..b04b53ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ###Changelog ####Version 0.14.0 (next) -* Pebble 2: Initial unstable pre-alpha support for P2/PT2 using BLE (won't be usable without weird workarounds for pairing) +* Pebble 2: Initial experimental support for P2/PT2 using BLE +* Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working) +* Pebble: Improve compatibility for watchface configuration ####Version 0.13.9 * Pebble: use the last known location for setting sunrise and sunset diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index f6220697..a9a79e4a 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,10 @@ + + Pebble 2: Initial experimental support for P2/PT2 using BLE + Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working) + Pebble: Improve compatibility for watchface configuration + Pebble: use the last known location for setting sunrise and sunset Pebble: fix Health disappering forever when deactivating through app manager (and get it back for affected users) From 9dd61031f05a4117aca1771fcaf8a337cd6c3621 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 21 Nov 2016 17:46:11 +0100 Subject: [PATCH 045/147] update Japanese from transifex. THANKS! --- app/src/main/res/values-ja/strings.xml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 39599641..67f701a2 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -25,6 +25,8 @@ Pebble Appstore で検索 アクティベート 非アクティベート + HRM をアクティベート + HRM を非アクティベート 設定 先頭に移動 @@ -247,6 +249,10 @@ 今後のイベントのために予約するアラーム 睡眠の検出を改善するために心拍センサーを使用する デバイスの時刻オフセット時間 (交代勤務の睡眠検知用) + Mi2: 日付形式 + 時間 + + 持ち上げ時に表示を有効にする 再接続の待機中 あなたについて 誕生年 @@ -309,8 +315,7 @@ Mi Band、Pebble Health、Morpheuz からデータをインポートすること 削除 バイブレーション - Mi2: 日付形式 - 時間 - - 持ち上げ時に表示を有効にする + + Pebbleペアリング + お使いのAndroidデバイスでペアリングのダイアログがポップアップすると思います。 起こらない場合は、通知ドロワーを調べて、ペアリング要求を受け入れます。 その後、Pebbleでペアリング要求を受け入れます From 4ed7906a9e989d0f7e76d1fb284fae537752bf6d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 21 Nov 2016 17:56:29 +0100 Subject: [PATCH 046/147] update README regarding Pebble 2 --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index df641a95..4fdda233 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,11 @@ need to create an account and transmit any of your data to the vendor's servers. ## Supported Devices * Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round +* Pebble 2 (experimental, PAIR WITHIN GADGETBRIDGE) * Mi Band, Mi Band 1A, Mi Band 1S * Mi Band 2 (notifications, alarms and heart rate measurement only) * Vibratissimo (experimental) -***THE PEBBLE 2 AND PEBBLE TIME 2 ARE CURRENTLY NOT SUPPORTED. ADDING SUPPORT IS HIGH-PRIORITY BUT WE CANNOT GIVE YOU AN ETA!*** - - ## Features (Pebble) * Incoming calls notification and display @@ -50,7 +48,7 @@ need to create an account and transmit any of your data to the vendor's servers. ## Getting Started (Pebble) -1. Pair your Pebble through the Android's Bluetooth Settings +1. Pair your Pebble through the Android's Bluetooth Settings or Gadgetbridge. Pebble 2 MUST be paried though Gadgetbridge (tap on the + in Control Center) 2. Start Gadgetbridge, tap on the device you want to connect to 3. To test, choose "Debug" from the menu and play around From dbe96582a795ee0e7a0025ee16ec260c5326cb54 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 22 Nov 2016 00:04:12 +0100 Subject: [PATCH 047/147] Changelog for 0.14.0 --- CHANGELOG.md | 4 ++++ app/src/main/res/xml/changelog_master.xml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b04b53ce..287ae7eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ * Pebble 2: Initial experimental support for P2/PT2 using BLE * Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working) * Pebble: Improve compatibility for watchface configuration +* Mi Band 2: support for heart rate measurement during sleep +* Mi Band 2: configuration option to activate the display on lift +* Mi Band 2: configuration option to display the time + date or just the time +* Mi Band 2: honor the wear location configuration option ####Version 0.13.9 * Pebble: use the last known location for setting sunrise and sunset diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index a9a79e4a..bd975b90 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -4,6 +4,10 @@ Pebble 2: Initial experimental support for P2/PT2 using BLE Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working) Pebble: Improve compatibility for watchface configuration + Mi Band 2: support for heart rate measurement during sleep + Mi Band 2: configuration option to activate the display on lift + Mi Band 2: configuration option to display the time + date or just the time + Mi Band 2: honor the wear location configuration option Pebble: use the last known location for setting sunrise and sunset From 82cd06f4c13f8c0e10fe8f56988abae1d8feca18 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 18 Nov 2016 21:14:04 +0100 Subject: [PATCH 048/147] Mi2: WIP: initial support for activity data (#323) --- .../freeyourgadget/gadgetbridge/Logging.java | 12 + .../devices/miband/MiBand2Coordinator.java | 4 +- .../devices/miband/MiBand2Service.java | 10 +- .../service/btle/BLETypeConversions.java | 36 +++ .../service/btle/actions/WriteAction.java | 9 + .../devices/miband/MiBand2Support.java | 86 ++++--- .../service/devices/miband/MiBandSupport.java | 1 + .../operations/AbstractMiBand1Operation.java | 17 ++ .../operations/AbstractMiBandOperation.java | 28 ++- .../operations/FetchActivityOperation.java | 7 +- .../operations/UpdateFirmwareOperation.java | 6 +- .../miband2/AbstractMiBand2Operation.java | 20 ++ .../operations/FetchActivityOperation.java | 233 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 14 files changed, 420 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/AbstractMiBand2Operation.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java index 75504d79..3d71b91f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java @@ -126,6 +126,18 @@ public abstract class Logging { return false; } + public static String formatBytes(byte[] bytes) { + if (bytes == null) { + return "(null)"; + } + StringBuilder builder = new StringBuilder(bytes.length * 5); + for (byte b : bytes) { + builder.append(String.format("0x%2x", b)); + builder.append(" "); + } + return builder.toString().trim(); + } + public static void logBytes(Logger logger, byte[] value) { if (value != null) { for (byte b : value) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 6cf308f8..84bc5d8a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -41,7 +41,7 @@ public class MiBand2Coordinator extends MiBandCoordinator { @Override public boolean supportsHeartRateMeasurement(GBDevice device) { - return false; // not yet + return true; } @Override @@ -51,7 +51,7 @@ public class MiBand2Coordinator extends MiBandCoordinator { @Override public boolean supportsActivityDataFetching() { - return false; // not yet + return true; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index c29451e9..51eead04 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -18,7 +18,7 @@ public class MiBand2Service { public static final UUID UUID_UNKNOWN_CHARACTERISTIC2 = UUID.fromString("00000002-0000-3512-2118-0009af100700"); public static final UUID UUID_UNKNOWN_CHARACTERISTIC3 = UUID.fromString("00000003-0000-3512-2118-0009af100700"); // Alarm related public static final UUID UUID_UNKNOWN_CHARACTERISTIC4 = UUID.fromString("00000004-0000-3512-2118-0009af100700"); - public static final UUID UUID_UNKNOWN_CHARACTERISTIC5 = UUID.fromString("00000005-0000-3512-2118-0009af100700"); + public static final UUID UUID_CHARACTERISTIC_ACTIVITY_DATA = UUID.fromString("00000005-0000-3512-2118-0009af100700"); public static final UUID UUID_UNKNOWN_CHARACTERISTIC6 = UUID.fromString("00000006-0000-3512-2118-0009af100700"); public static final UUID UUID_UNKNOWN_CHARACTERISTIC7 = UUID.fromString("00000007-0000-3512-2118-0009af100700"); public static final UUID UUID_UNKNOWN_CHARACTERISTIC8 = UUID.fromString("00000008-0000-3512-2118-0009af100700"); @@ -279,6 +279,9 @@ public class MiBand2Service { */ public static final byte AUTH_BYTE = 0x8; + // maybe not really activity data, but steps? + public static final byte COMMAND_FETCH_ACTIVITY_DATA = 0x02; + public static byte COMMAND_DATEFORMAT = 0x06; public static final byte[] DATEFORMAT_DATE_TIME = new byte[] { COMMAND_DATEFORMAT, 0x0a, 0x0, 0x03 }; @@ -286,10 +289,15 @@ public class MiBand2Service { public static final byte RESPONSE = 0x10; + public static final byte SUCCESS = 0x01; + public static final byte COMMAND_ACTIVITY_DATA_START_DATE = 0x01; + + public static final byte[] RESPONSE_FINISH_SUCCESS = new byte[] {RESPONSE, 2, 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_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 }; public static final byte[] WEAR_LOCATION_RIGHT_WRIST = new byte[] { 0x20, 0x00, 0x00, (byte) 0x82}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index 727e6c14..bc61b0f3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -52,6 +52,42 @@ public class BLETypeConversions { }; } + /** + * Similar to calendarToRawBytes, but only up to (and including) the MINUTES. + * @param timestamp + * @param honorDeviceTimeOffset + * @return + */ + public static byte[] shortCalendarToRawBytes(Calendar timestamp, boolean honorDeviceTimeOffset) { + + // The mi-band device currently records sleep + // only if it happens after 10pm and before 7am. + // The offset is used to trick the device to record sleep + // in non-standard hours. + // If you usually sleep, say, from 6am to 2pm, set the + // shift to -8, so at 6am the device thinks it's still 10pm + // of the day before. + if (honorDeviceTimeOffset) { + int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(); + if (offsetInHours != 0) { + timestamp.add(Calendar.HOUR_OF_DAY, offsetInHours); + } + } + + // MiBand2: + // year,year,month,dayofmonth,hour,minute,second,dayofweek,0,0,tz + + byte[] year = fromUint16(timestamp.get(Calendar.YEAR)); + return new byte[] { + year[0], + year[1], + fromUint8(timestamp.get(Calendar.MONTH) + 1), + fromUint8(timestamp.get(Calendar.DATE)), + fromUint8(timestamp.get(Calendar.HOUR_OF_DAY)), + fromUint8(timestamp.get(Calendar.MINUTE)) + }; + } + private static int getMiBand2TimeZone(int rawOffset) { int offsetMinutes = rawOffset / 1000 / 60; rawOffset = offsetMinutes < 0 ? -1 : 1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java index 5cecee06..b64c17bb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java @@ -4,6 +4,11 @@ import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.Logging; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; /** @@ -12,6 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; * {@link BluetoothGattCallback} */ public class WriteAction extends BtLEAction { + private static final Logger LOG = LoggerFactory.getLogger(WriteAction.class); private final byte[] value; @@ -32,6 +38,9 @@ public class WriteAction extends BtLEAction { } protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) { + if (LOG.isDebugEnabled()) { + LOG.debug("writing to characteristic: " + characteristic.getUuid() + ": " + Logging.formatBytes(value)); + } if (characteristic.setValue(value)) { return gatt.writeCharacteristic(characteristic); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index 10e6de68..c4714b89 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -26,6 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; @@ -57,8 +58,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateA import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile; -import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2NotificationStrategy; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.FetchActivityOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -176,13 +177,23 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // return this; // } - public MiBand2Support setCurrentTimeWithService(TransactionBuilder builder) { - GregorianCalendar now = BLETypeConversions.createCalendar(); - byte[] bytes = BLETypeConversions.calendarToRawBytes(now, true); - byte[] tail = new byte[] { 0, BLETypeConversions.mapTimeZone(now.getTimeZone()) }; // 0 = adjust reason bitflags? or DST offset?? , timezone + public byte[] getTimeBytes(Calendar calendar) { + byte[] bytes = BLETypeConversions.shortCalendarToRawBytes(calendar, true); + byte[] tail = new byte[] { 0, BLETypeConversions.mapTimeZone(calendar.getTimeZone()) }; // 0 = adjust reason bitflags? or DST offset?? , timezone // byte[] tail = new byte[] { 0x2 }; // reason byte[] all = BLETypeConversions.join(bytes, tail); - builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), all); + return all; + } + + public Calendar fromTimeBytes(byte[] bytes) { + GregorianCalendar timestamp = BLETypeConversions.rawBytesToCalendar(bytes, true); + return timestamp; + } + + public MiBand2Support setCurrentTimeWithService(TransactionBuilder builder) { + GregorianCalendar now = BLETypeConversions.createCalendar(); + byte[] bytes = getTimeBytes(now); + builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), bytes); // byte[] localtime = BLETypeConversions.calendarToLocalTimeBytes(now); // builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_LOCAL_TIME_INFORMATION), localtime); // builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), new byte[] {0x2, 0x00}); @@ -412,7 +423,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { builder.write(characteristic, MiBand2Service.WEAR_LOCATION_RIGHT_WRIST); break; } - builder.notify(characteristic, false); // TODO: this should actually be in some kind of finally-block in the queue + builder.notify(characteristic, false); // TODO: this should actually be in some kind of finally-block in the queue. It should also be sent asynchronously after the notifications have completely arrived and processed. } return this; } @@ -455,7 +466,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { LOG.info("Disabling heartrate sleep support..."); builder.write(characteristicHRControlPoint, MiBand2Service.COMMAND_DISABLE_HR_SLEEP_MEASUREMENT); } - builder.notify(characteristicHRControlPoint, false); // TODO: this should run in some kind of finally-block in the queue + builder.notify(characteristicHRControlPoint, false); // TODO: this should actually be in some kind of finally-block in the queue. It should also be sent asynchronously after the notifications have completely arrived and processed. } return this; } @@ -678,18 +689,18 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onEnableRealtimeHeartRateMeasurement(boolean enable) { - try { - TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); - if (enable) { - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); - } else { - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); - } - builder.queue(getQueue()); - } catch (IOException ex) { - LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); - } +// try { +// TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); +// if (enable) { +// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); +// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); +// } else { +// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); +// } +// builder.queue(getQueue()); +// } catch (IOException ex) { +// LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); +// } } @Override @@ -714,28 +725,27 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onFetchActivityData() { -// TODO: onFetchActivityData -// try { -// new FetchActivityOperation(this).perform(); -// } catch (IOException ex) { -// LOG.error("Unable to fetch MI activity data", ex); -// } + try { + new FetchActivityOperation(this).perform(); + } catch (IOException ex) { + LOG.error("Unable to fetch MI activity data", ex); + } } @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()); - } catch (IOException e) { - LOG.error("Unable to change realtime steps notification to: " + enable, e); - } +// 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()); +// } catch (IOException e) { +// LOG.error("Unable to change realtime steps notification to: " + enable, e); +// } } private byte[] getHighLatency() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index fafab86f..7df10ef7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -55,6 +55,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSuppo import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.Transaction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java new file mode 100644 index 00000000..00c1d7ba --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java @@ -0,0 +1,17 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; + +public abstract class AbstractMiBand1Operation extends AbstractMiBandOperation { + protected AbstractMiBand1Operation(MiBandSupport support) { + super(support); + } + + @Override + protected void enableOtherNotifications(TransactionBuilder builder, boolean enable) { + builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable) + .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java index 971a63d4..93fd8402 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java @@ -4,14 +4,16 @@ import android.widget.Toast; import java.io.IOException; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public abstract class AbstractMiBandOperation extends AbstractBTLEOperation { - protected AbstractMiBandOperation(MiBandSupport support) { +public abstract class AbstractMiBandOperation extends AbstractBTLEOperation { + protected AbstractMiBandOperation(T support) { super(support); } @@ -21,6 +23,7 @@ public abstract class AbstractMiBandOperation extends AbstractBTLEOperation { + protected AbstractMiBand2Operation(MiBand2Support support) { + super(support); + } + + @Override + protected void enableOtherNotifications(TransactionBuilder builder, boolean enable) { + // TODO: check which notifications we should disable and re-enable here +// builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable) +// .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java new file mode 100644 index 00000000..bb4b87bc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -0,0 +1,233 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.text.DateFormat; +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 nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.Logging; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +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.SetDeviceBusyAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +/** + * An operation that fetches activity data. For every fetch, a new operation must + * be created, i.e. an operation may not be reused for multiple fetches. + */ +public class FetchActivityOperation extends AbstractMiBand2Operation { + private static final Logger LOG = LoggerFactory.getLogger(FetchActivityOperation.class); + + private List samples = new ArrayList<>(60*24); // 1day per default + + private byte lastPacketCounter = -1; + private Calendar startTimestamp; + + public FetchActivityOperation(MiBand2Support support) { + super(support); + } + + @Override + protected void enableNeededNotifications(TransactionBuilder builder, boolean enable) { + if (!enable) { + // dynamically enabled, but always disabled on finish + builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable); + } + } + + @Override + protected void doPerform() throws IOException { + TransactionBuilder builder = performInitialized("fetching activity data"); + getSupport().setLowLatency(builder); + builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.busy_task_fetch_activity_data), getContext())); + BluetoothGattCharacteristic characteristicFetch = getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4); + builder.notify(characteristicFetch, true); + BluetoothGattCharacteristic characteristicActivityData = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_ACTIVITY_DATA); + + GregorianCalendar sinceWhen = getLastSuccessfulSynchronizedTime(); + builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE, 0x01 }, getSupport().getTimeBytes(sinceWhen))); + builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply + builder.notify(characteristicActivityData, true); + builder.write(characteristicFetch, new byte[] { MiBand2Service.COMMAND_FETCH_ACTIVITY_DATA }); + builder.queue(getQueue()); + } + + // TODO: use last synchronized sample for the timestamp! + // and what do we do if there was no sync? timestamp from first connectionn? + // or just now - 20d? + private GregorianCalendar getLastSuccessfulSynchronizedTime() { + GregorianCalendar calendar = BLETypeConversions.createCalendar(); + calendar.add(Calendar.DAY_OF_MONTH, -4); + return calendar; + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + UUID characteristicUUID = characteristic.getUuid(); + if (MiBand2Service.UUID_CHARACTERISTIC_ACTIVITY_DATA.equals(characteristicUUID)) { + handleActivityNotif(characteristic.getValue()); + return true; + } else if (MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) { + handleActivityMetadata(characteristic.getValue()); + return true; + } else { + return super.onCharacteristicChanged(gatt, characteristic); + } + } + + private void handleActivityFetchFinish() { + LOG.info("Fetching activity data has finished."); + saveSamples(); + operationFinished(); + unsetBusy(); + } + + private void saveSamples() { + if (samples.size() > 0) { + // save all the samples that we got + try (DBHandler handler = GBApplication.acquireDB()) { + DaoSession session = handler.getDaoSession(); + SampleProvider sampleProvider = new MiBandSampleProvider(getDevice(), session); + Device device = DBHelper.getDevice(getDevice(), session); + User user = DBHelper.getUser(session); + + GregorianCalendar timestamp = (GregorianCalendar) startTimestamp.clone(); + for (MiBandActivitySample sample : samples) { + sample.setDevice(device); + sample.setUser(user); + sample.setTimestamp((int) (timestamp.getTimeInMillis() / 1000)); + sample.setProvider(sampleProvider); + + if (LOG.isDebugEnabled()) { +// LOG.debug("sample: " + sample); + } + + timestamp.add(Calendar.MINUTE, 1); + } + sampleProvider.addGBActivitySamples(samples.toArray(new MiBandActivitySample[0])); + + } catch (Exception ex) { + GB.toast(getContext(), "Error saving activity samples", Toast.LENGTH_LONG, GB.ERROR); + } finally { + samples.clear(); + } + } + } + + /** + * Method to handle the incoming activity data. + * There are two kind of messages we currently know: + * - the first one is 11 bytes long and contains metadata (how many bytes to expect, when the data starts, etc.) + * - the second one is 20 bytes long and contains the actual activity data + *

+ * The first message type is parsed by this method, for every other length of the value param, bufferActivityData is called. + * + * @param value + */ + private void handleActivityNotif(byte[] value) { + if (!isOperationRunning()) { + LOG.error("ignoring activity data notification because operation is not running. Data length: " + value.length); + getSupport().logMessageContent(value); + return; + } + + if (value.length == 17) { + if ((byte) (lastPacketCounter + 1) == value[0] ) { + lastPacketCounter++; + bufferActivityData(value); + } else { + GB.toast("Error fetching activity data, invalid package counter: " + value[0], Toast.LENGTH_LONG, GB.ERROR); + handleActivityFetchFinish(); + return; + } + handleActivityMetadata(value); + } else { + GB.toast("Error fetching activity data, unexpected package length: " + value.length, Toast.LENGTH_LONG, GB.ERROR); + } + } + + /** + * Creates samples from the given 17-length array + * @param value + */ + private void bufferActivityData(byte[] value) { + int len = value.length; + + if (len % 4 != 1) { + throw new AssertionError("Unexpected activity array size: " + value); + } + + for (int i = 1; i < len; i++) { + if (i % 4 == 1) { + MiBandActivitySample sample = createSample(value[i], value[i + 1], value[i + 2], value[i + 3]); + samples.add(sample); + } + } + } + + private MiBandActivitySample createSample(byte category, byte intensity, byte steps, byte heartrate) { + MiBandActivitySample sample = new MiBandActivitySample(); + sample.setRawKind(category & 0xff); + sample.setRawIntensity(intensity & 0xff); + sample.setSteps(steps & 0xff); + sample.setHeartRate(heartrate & 0xff); + + return sample; + } + + private void handleActivityMetadata(byte[] value) { + if (value.length == 15) { + // first two bytes are whether our request was accepted + if (ArrayUtils.equals(MiBand2Service.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, value, 0, 2)) { + // the third byte (0x01 on success) = ? + // the 4th - 7th bytes probably somehow represent the number of bytes/packets to expect + + // last 8 bytes are the start date + Calendar startTimestamp = getSupport().fromTimeBytes(org.apache.commons.lang3.ArrayUtils.subarray(value, 7, value.length)); + setStartTimestamp(startTimestamp); + + GB.toast(getContext().getString(R.string.FetchActivityOperation_about_to_transfer_since, + DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), Toast.LENGTH_LONG, GB.INFO); + } else { + LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); + } + } else if (value.length == 3) { + if (Arrays.equals(MiBand2Service.RESPONSE_FINISH_SUCCESS, value)) { + handleActivityFetchFinish(); + } else { + LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); + } + } + } + + private void setStartTimestamp(Calendar startTimestamp) { + this.startTimestamp = startTimestamp; + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cd30af06..c4d60953 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -282,6 +282,7 @@ Time Activate display upon lift + About to transfer data since %1$s waiting for reconnect From 0c51f86afc470d88997adb924b3702e29fb43659 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 20 Nov 2016 23:33:46 +0100 Subject: [PATCH 049/147] Mi2: more work on activity data #323 --- .../gadgetbridge/devices/SampleProvider.java | 1 + .../miband/AbstractMiBandSampleProvider.java | 59 +++++++++++++++++ .../devices/miband/MiBand2SampleProvider.java | 64 +++++++++++++++++++ .../devices/miband/MiBandCoordinator.java | 2 +- .../devices/miband/MiBandSampleProvider.java | 50 ++------------- .../operations/FetchActivityOperation.java | 3 + 6 files changed, 134 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java index df91696e..54f809dd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -22,6 +22,7 @@ public interface SampleProvider { int PROVIDER_PEBBLE_GADGETBRIDGE = 2; // removed int PROVIDER_PEBBLE_MISFIT = 3; int PROVIDER_PEBBLE_HEALTH = 4; + int PROVIDER_MIBAND2 = 5; int PROVIDER_UNKNOWN = 100; // TODO: can also be removed diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java new file mode 100644 index 00000000..8bed5544 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java @@ -0,0 +1,59 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.miband; + +import android.support.annotation.NonNull; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +/** + * Base class for Mi1 and Mi2 sample providers. At the moment they both share the + * same activity sample class. + */ +public abstract class AbstractMiBandSampleProvider extends AbstractSampleProvider { + + // maybe this should be configurable 256 seems way off, though. + private final float movementDivisor = 180.0f; //256.0f; + + public AbstractMiBandSampleProvider(GBDevice device, DaoSession session) { + super(device, session); + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity / movementDivisor; + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getMiBandActivitySampleDao(); + } + + @NonNull + @Override + protected Property getTimestampSampleProperty() { + return MiBandActivitySampleDao.Properties.Timestamp; + } + + @NonNull + @Override + protected Property getDeviceIdentifierSampleProperty() { + return MiBandActivitySampleDao.Properties.DeviceId; + } + + @Override + protected Property getRawKindSampleProperty() { + return MiBandActivitySampleDao.Properties.RawKind; + } + + @Override + public MiBandActivitySample createActivitySample() { + return new MiBandActivitySample(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java new file mode 100644 index 00000000..9b7b2c66 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java @@ -0,0 +1,64 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.miband; + +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public class MiBand2SampleProvider extends AbstractMiBandSampleProvider { + // these are all bogus atm (come from Mi1) + public static final int TYPE_DEEP_SLEEP = 11; + public static final int TYPE_LIGHT_SLEEP = 5; + public static final int TYPE_ACTIVITY = -1; + public static final int TYPE_UNKNOWN = -1; + public static final int TYPE_NONWEAR = 3; + public static final int TYPE_CHARGING = 6; + + // observed the following values so far: + // 00 01 02 09 0a 0b 0c 10 11 + + public MiBand2SampleProvider(GBDevice device, DaoSession session) { + super(device, session); + } + + @Override + public int getID() { + return SampleProvider.PROVIDER_MIBAND2; + } + + @Override + public int normalizeType(int rawType) { + switch (rawType) { + case TYPE_DEEP_SLEEP: + return ActivityKind.TYPE_DEEP_SLEEP; + case TYPE_LIGHT_SLEEP: + return ActivityKind.TYPE_LIGHT_SLEEP; + case TYPE_ACTIVITY: + return ActivityKind.TYPE_ACTIVITY; + case TYPE_NONWEAR: + return ActivityKind.TYPE_NOT_WORN; + case TYPE_CHARGING: + return ActivityKind.TYPE_NOT_WORN; //I believe it's a safe assumption + default: +// case TYPE_UNKNOWN: // fall through + return ActivityKind.TYPE_UNKNOWN; + } + } + + @Override + public int toRawActivityKind(int activityKind) { + switch (activityKind) { + case ActivityKind.TYPE_ACTIVITY: + return TYPE_ACTIVITY; + case ActivityKind.TYPE_DEEP_SLEEP: + return TYPE_DEEP_SLEEP; + case ActivityKind.TYPE_LIGHT_SLEEP: + return TYPE_LIGHT_SLEEP; + case ActivityKind.TYPE_NOT_WORN: + return TYPE_NONWEAR; + case ActivityKind.TYPE_UNKNOWN: // fall through + default: + return TYPE_UNKNOWN; + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index 069c9ba1..eee28a3e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -80,7 +80,7 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { @Override public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { - return new MiBandSampleProvider(device, session); + return new MiBand2SampleProvider(device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java index 13f77224..8f2a95c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java @@ -1,16 +1,11 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; -import de.greenrobot.dao.AbstractDao; -import de.greenrobot.dao.Property; -import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class MiBandSampleProvider extends AbstractSampleProvider { +public class MiBandSampleProvider extends AbstractMiBandSampleProvider { public static final int TYPE_DEEP_SLEEP = 4; public static final int TYPE_LIGHT_SLEEP = 5; public static final int TYPE_ACTIVITY = -1; @@ -26,13 +21,15 @@ public class MiBandSampleProvider extends AbstractSampleProvider getSampleDao() { - return getSession().getMiBandActivitySampleDao(); - } - - @Override - protected Property getTimestampSampleProperty() { - return MiBandActivitySampleDao.Properties.Timestamp; - } - - @Override - protected Property getDeviceIdentifierSampleProperty() { - return MiBandActivitySampleDao.Properties.DeviceId; - } - - @Override - protected Property getRawKindSampleProperty() { - return MiBandActivitySampleDao.Properties.RawKind; - } - - @Override - public MiBandActivitySample createActivitySample() { - return new MiBandActivitySample(); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java index bb4b87bc..fa5facb7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -35,6 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; /** @@ -133,6 +134,8 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { } sampleProvider.addGBActivitySamples(samples.toArray(new MiBandActivitySample[0])); + LOG.info("Mi2 activity data: last sample timestamp: " + DateTimeUtils.formatDateTime(timestamp.getTime())); + } catch (Exception ex) { GB.toast(getContext(), "Error saving activity samples", Toast.LENGTH_LONG, GB.ERROR); } finally { From a8a7d8db3180f3afe26e934a3fc0aa06271027c0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 22 Nov 2016 00:03:23 +0100 Subject: [PATCH 050/147] Mi2: WIP synchronize only new data since last sync (#323) --- .../devices/AbstractSampleProvider.java | 18 +++++++ .../gadgetbridge/devices/SampleProvider.java | 8 ++++ .../devices/UnknownDeviceCoordinator.java | 7 +++ .../devices/miband/MiBand2SampleProvider.java | 48 ++++++++++--------- .../entities/AbstractActivitySample.java | 6 ++- .../operations/FetchActivityOperation.java | 24 ++++++++-- .../gadgetbridge/test/Tryout.java | 36 ++++++++++++++ 7 files changed, 119 insertions(+), 28 deletions(-) create mode 100644 app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index 4748ea47..cd375c52 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -74,6 +74,24 @@ public abstract class AbstractSampleProvider i getSampleDao().insertOrReplaceInTx(activitySamples); } + @Nullable + @Override + public T getLatestActivitySample() { + QueryBuilder qb = getSampleDao().queryBuilder(); + Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); + if (dbDevice == null) { + // no device, no sample + return null; + } + Property deviceProperty = getDeviceIdentifierSampleProperty(); + qb.where(deviceProperty.eq(dbDevice.getId())).orderDesc(getTimestampSampleProperty()).limit(1); + List samples = qb.build().list(); + if (samples.isEmpty()) { + return null; + } + return samples.get(0); + } + protected List getGBActivitySamples(int timestamp_from, int timestamp_to, int activityType) { if (getRawKindSampleProperty() == null && activityType != ActivityKind.TYPE_ALL) { // if we do not have a raw kind property we cannot query anything else then TYPE_ALL diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java index 54f809dd..fca1710e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.util.List; @@ -88,4 +89,11 @@ public interface SampleProvider { * @return the newly created "empty" sample */ T createActivitySample(); + + /** + * Returns the activity sample with the highest timestamp. or null if none + * @return the latest sample or null + */ + @Nullable + T getLatestActivitySample(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index 06fc3d4a..d7379c45 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.util.List; @@ -64,6 +65,12 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { return null; } + @Nullable + @Override + public AbstractActivitySample getLatestActivitySample() { + return null; + } + @Override public int getID() { return PROVIDER_UNKNOWN; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java index 9b7b2c66..d68f6d46 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java @@ -13,6 +13,9 @@ public class MiBand2SampleProvider extends AbstractMiBandSampleProvider { public static final int TYPE_UNKNOWN = -1; public static final int TYPE_NONWEAR = 3; public static final int TYPE_CHARGING = 6; + // appears to be a measurement problem resulting in type = 10 and intensity = 20, at least + // with fw 1.0.0.39 + public static final int TYPE_IGNORE = 10; // observed the following values so far: // 00 01 02 09 0a 0b 0c 10 11 @@ -29,16 +32,17 @@ public class MiBand2SampleProvider extends AbstractMiBandSampleProvider { @Override public int normalizeType(int rawType) { switch (rawType) { - case TYPE_DEEP_SLEEP: - return ActivityKind.TYPE_DEEP_SLEEP; - case TYPE_LIGHT_SLEEP: - return ActivityKind.TYPE_LIGHT_SLEEP; - case TYPE_ACTIVITY: - return ActivityKind.TYPE_ACTIVITY; - case TYPE_NONWEAR: - return ActivityKind.TYPE_NOT_WORN; - case TYPE_CHARGING: - return ActivityKind.TYPE_NOT_WORN; //I believe it's a safe assumption +// case TYPE_DEEP_SLEEP: +// return ActivityKind.TYPE_DEEP_SLEEP; +// case TYPE_LIGHT_SLEEP: +// return ActivityKind.TYPE_LIGHT_SLEEP; +// case TYPE_ACTIVITY: +// return ActivityKind.TYPE_ACTIVITY; +// case TYPE_NONWEAR: +// return ActivityKind.TYPE_NOT_WORN; +// case TYPE_CHARGING: +// return ActivityKind.TYPE_NOT_WORN; //I believe it's a safe assumption +// case TYPE_IGNORE: default: // case TYPE_UNKNOWN: // fall through return ActivityKind.TYPE_UNKNOWN; @@ -47,18 +51,18 @@ public class MiBand2SampleProvider extends AbstractMiBandSampleProvider { @Override public int toRawActivityKind(int activityKind) { - switch (activityKind) { - case ActivityKind.TYPE_ACTIVITY: - return TYPE_ACTIVITY; - case ActivityKind.TYPE_DEEP_SLEEP: - return TYPE_DEEP_SLEEP; - case ActivityKind.TYPE_LIGHT_SLEEP: - return TYPE_LIGHT_SLEEP; - case ActivityKind.TYPE_NOT_WORN: - return TYPE_NONWEAR; - case ActivityKind.TYPE_UNKNOWN: // fall through - default: +// switch (activityKind) { +// case ActivityKind.TYPE_ACTIVITY: +// return TYPE_ACTIVITY; +// case ActivityKind.TYPE_DEEP_SLEEP: +// return TYPE_DEEP_SLEEP; +// case ActivityKind.TYPE_LIGHT_SLEEP: +// return TYPE_LIGHT_SLEEP; +// case ActivityKind.TYPE_NOT_WORN: +// return TYPE_NONWEAR; +// case ActivityKind.TYPE_UNKNOWN: // fall through +// default: return TYPE_UNKNOWN; - } +// } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java index 08f09c11..03f9e442 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java @@ -74,12 +74,14 @@ public abstract class AbstractActivitySample implements ActivitySample { @Override public String toString() { + int kind = getProvider() != null ? getKind() : ActivitySample.NOT_MEASURED; + float intensity = getProvider() != null ? getIntensity() : ActivitySample.NOT_MEASURED; return getClass().getSimpleName() + "{" + "timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimeStamp(getTimestamp())) + - ", intensity=" + getIntensity() + + ", intensity=" + intensity + ", steps=" + getSteps() + ", heartrate=" + getHeartRate() + - ", type=" + getKind() + + ", type=" + kind + ", userId=" + getUserId() + ", deviceId=" + getDeviceId() + '}'; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java index fa5facb7..5d8c2dba 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -12,21 +12,26 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.UUID; +import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -79,12 +84,23 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { builder.queue(getQueue()); } - // TODO: use last synchronized sample for the timestamp! - // and what do we do if there was no sync? timestamp from first connectionn? - // or just now - 20d? private GregorianCalendar getLastSuccessfulSynchronizedTime() { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + SampleProvider sampleProvider = new MiBand2SampleProvider(getDevice(), session); + MiBandActivitySample sample = sampleProvider.getLatestActivitySample(); + if (sample != null) { + int timestamp = sample.getTimestamp(); + GregorianCalendar calendar = BLETypeConversions.createCalendar(); + calendar.setTimeInMillis(timestamp * 1000); + return calendar; + } + } catch (Exception ex) { + LOG.error("Error querying for latest activity sample, synchronizing the last 10 days", ex); + } + GregorianCalendar calendar = BLETypeConversions.createCalendar(); - calendar.add(Calendar.DAY_OF_MONTH, -4); + calendar.add(Calendar.DAY_OF_MONTH, -10); return calendar; } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java new file mode 100644 index 00000000..0f36f268 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java @@ -0,0 +1,36 @@ +package nodomain.freeyourgadget.gadgetbridge.test; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.GregorianCalendar; + +import nodomain.freeyourgadget.gadgetbridge.Logging; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; + +/** + * A simple class for trying out things, not actually testing something. + */ +public class Tryout extends TestBase { + private static final Logger LOG = LoggerFactory.getLogger(MiBand2Support.class); + + @Test + public void blah() { + int v = 1 << 7 | 1 << 2; + byte b = (byte) v; + LOG.info("v: " + v); + Logging.logBytes(LOG, new byte[] { b }); + } + + @Test + public void testCalendarBytes() { + GregorianCalendar calendar = MiBandDateConverter.createCalendar(); + byte[] bytes = MiBandDateConverter.calendarToRawBytes(calendar); + LOG.info("Calender: " + DateTimeUtils.formatDateTime(calendar.getTime())); + Logging.logBytes(LOG, bytes); + } + +} From 8d6888a13a1977bb9dae7f33190cffd4b92e4a79 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 22 Nov 2016 14:43:07 +0100 Subject: [PATCH 051/147] Remove "(next)" from 0.14.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 287ae7eb..985f1161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ###Changelog -####Version 0.14.0 (next) +####Version 0.14.0 * Pebble 2: Initial experimental support for P2/PT2 using BLE * Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working) * Pebble: Improve compatibility for watchface configuration From dfbaba4cb6902c19cc7570dd36e02edb96238485 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 22 Nov 2016 15:53:58 +0100 Subject: [PATCH 052/147] Make sure that the charts display the requested time range (instead of only the range with available samples) --- .../charts/AbstractChartFragment.java | 32 ++++++++++++++- .../charts/TrailingActivitySample.java | 39 +++++++++++++++++++ .../operations/FetchActivityOperation.java | 2 + 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index aafb0680..702243e9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -709,7 +709,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } protected List getSamples(DBHandler db, GBDevice device) { - List samples = getSamples(db, device, getTSStart(), getTSEnd()); + int tsStart = getTSStart(); + int tsEnd = getTSEnd(); + List samples = (List) getSamples(db, device, tsStart, tsEnd); + ensureStartAndEndSamples(samples, tsStart, tsEnd); // List samples2 = new ArrayList<>(); // int min = Math.min(samples.size(), 10); // int min = Math.min(samples.size(), 10); @@ -720,6 +723,33 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { return samples; } + protected void ensureStartAndEndSamples(List samples, int tsStart, int tsEnd) { + if (samples == null || samples.isEmpty()) { + return; + } + ActivitySample lastSample = samples.get(samples.size() - 1); + if (lastSample.getTimestamp() < tsEnd) { + samples.add(createTrailingActivitySample(lastSample, tsEnd)); + } + + ActivitySample firstSample = samples.get(0); + if (firstSample.getTimestamp() > tsStart) { + samples.add(createTrailingActivitySample(firstSample, tsStart)); + } + } + + private ActivitySample createTrailingActivitySample(ActivitySample referenceSample, int timestamp) { + TrailingActivitySample sample = new TrailingActivitySample(); + if (referenceSample instanceof AbstractActivitySample) { + AbstractActivitySample reference = (AbstractActivitySample) referenceSample; + sample.setUserId(reference.getUserId()); + sample.setDeviceId(reference.getDeviceId()); + sample.setProvider(reference.getProvider()); + } + sample.setTimestamp(timestamp); + return sample; + } + private int getTSEnd() { return toTimestamp(getEndDate()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java new file mode 100644 index 00000000..4c2fc77a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java @@ -0,0 +1,39 @@ +package nodomain.freeyourgadget.gadgetbridge.activities.charts; + +import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; + +public class TrailingActivitySample extends AbstractActivitySample { + private int timestamp; + private long userId; + private long deviceId; + + @Override + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + @Override + public void setUserId(long userId) { + this.userId = userId; + } + + @Override + public void setDeviceId(long deviceId) { + this.deviceId = deviceId; + } + + @Override + public long getDeviceId() { + return deviceId; + } + + @Override + public long getUserId() { + return userId; + } + + @Override + public int getTimestamp() { + return timestamp; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java index 5d8c2dba..26a46c84 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -236,12 +236,14 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), Toast.LENGTH_LONG, GB.INFO); } else { LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); + handleActivityFetchFinish(); } } else if (value.length == 3) { if (Arrays.equals(MiBand2Service.RESPONSE_FINISH_SUCCESS, value)) { handleActivityFetchFinish(); } else { LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); + handleActivityFetchFinish(); } } } From 67d89ce1b993dd8026630f6947c5cd89511491fb Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 22 Nov 2016 22:51:47 +0100 Subject: [PATCH 053/147] Create the correct start-sync timestamp #323 --- .../devices/miband2/operations/FetchActivityOperation.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java index 26a46c84..92449abd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -12,14 +12,11 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; -import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.UUID; -import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; @@ -31,7 +28,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -92,7 +88,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { if (sample != null) { int timestamp = sample.getTimestamp(); GregorianCalendar calendar = BLETypeConversions.createCalendar(); - calendar.setTimeInMillis(timestamp * 1000); + calendar.setTimeInMillis((long) timestamp * 1000); return calendar; } } catch (Exception ex) { From a5a5e66c627f1aba66f5f41e04e2f0a409e094d5 Mon Sep 17 00:00:00 2001 From: Gilles MOREL Date: Thu, 24 Nov 2016 17:06:43 +0100 Subject: [PATCH 054/147] French translation. Changed and added several French messages. --- app/src/main/res/values-fr/strings.xml | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index dcf5aae5..e956aca9 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -10,6 +10,8 @@ Trouver l\'appareil Prendre une capture d\'écran Déconnexion + Supprimer l\'appareil + Supprimer %1$s Déboguer Gestionnaire d\'application @@ -45,11 +47,11 @@ Thème Clair Sombre - Langage + Langue Notifications Répétitions Appels téléphoniques - Textos + SMS K9-Mail Messages Pebble Support des applications qui envoient des notification à Pebble par PebbleKit. @@ -60,7 +62,7 @@ toujours quand l\'écran est éteint jamais - Apps bloquées + Applications bloquées Modèles de messages Réponses Suffixe commun @@ -82,6 +84,8 @@ Obtenir l\'emplacement Latitude Longitude + Garder l\'emplacement à jour + Tenter d\'obtenir la localisation pendant la course à pied, utiliser la localisation enregistré en cas de problème. Activez la localisation réseau s\'il vous plaît Emplacement obtenu Protocole des notifications en vigueur @@ -145,7 +149,7 @@ Initialisation Récupération des données d\'activité De %1$s à %2$s - Port main gauche ou droite? + Côté de port du bracelet Profil de vibration Saccadé Court @@ -155,7 +159,10 @@ Sonnette Réveil Vibration - Notification Texto + Notification SMS + Clavardage (Chat) + Navigation + Réseaux sociaux Paramètres des vibrations Notification générique Notification Pebble @@ -211,7 +218,7 @@ Échec lors de l\'écriture du micrologiciel Pas Activité en direct - Nombre de pas aujourd\'hui, objectif: %1$s + Nombre de pas aujourd\'hui, objectif : %1$s Ne pas confirmer le transfert de données d\'activités Les données d\'activités ne seront pas effacées du bracelet si elles ne sont pas confirmées. Utile si GB est utilisé avec d\'autres applications. Les données d\'activités seront conservées sur le Mi Band après la synchronisation. Utile si GB est utilisé avec d\'autres applications. @@ -234,6 +241,11 @@ Alarmes à réserver pour événements futurs Utiliser le capteur cardiaque pour améliorer la précision du sommeil La compensation de temps en heure (pour détecter le sommeil de travailleurs en rotation, par exemple) + Mi band 2 : format de l\'heure + Heure seule + + Alumer quand le poignet se lève + About to transfer data since %1$s en attente de reconnexion A propos de vous Année de naissance @@ -244,7 +256,7 @@ authentification requise ZzZz Ajouter un widget - Préférer le mode heure pendant le sommeil + Durée préférée de sommeil en heures Une alarme a été enregistré pour %1$02d:%2$02d Modèle: %1$s Micrologiciel: %1$s From 5b804effa4733a478c0bef6f913bc4f8057974b2 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 24 Nov 2016 18:03:47 +0100 Subject: [PATCH 055/147] Pebble: use a proper event handling in the configuration JS This allows more advanced configuration pages to work properly. The problematic config pages emerged while fixing #431 --- CHANGELOG.md | 3 + .../app_config/js/gadgetbridge_boilerplate.js | 72 ++++++++++--------- app/src/main/res/xml/changelog_master.xml | 3 + 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 985f1161..b50b6709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version next +* Pebble: Further improve compatibility for watchface configuration + ####Version 0.14.0 * Pebble 2: Initial experimental support for P2/PT2 using BLE * Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working) diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index 035fd08c..0c51b1dd 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -66,36 +66,38 @@ function gbPebble() { this.configurationURL = null; this.configurationValues = null; var self = this; + self.events = {}; + //events processing: see http://stackoverflow.com/questions/10978311/implementing-events-in-my-own-object + self.addEventListener = function(name, handler) { + if (self.events.hasOwnProperty(name)) + self.events[name].push(handler); + else + self.events[name] = [handler]; + } - this.addEventListener = function(e, f) { - if(e == 'ready') { - self.ready = f; - } - if(e == 'showConfiguration') { - self.showConfiguration = f; - } - if(e == 'webviewclosed') { - self.parseconfig = f; - } - if(e == 'appmessage') { - self.appmessage = f; + self.removeEventListener = function(name, handler) { + if (!self.events.hasOwnProperty(name)) + return; + + var index = self.events[name].indexOf(handler); + if (index != -1) + self.events[name].splice(index, 1); + } + + self.evaluate = function(name, args) { + console.log(args); + if (!self.events.hasOwnProperty(name)) + return; + + if (!args || !args.length) + args = []; + + var evs = self.events[name], l = evs.length; + for (var i = 0; i < l; i++) { + evs[i].apply(null, args); } } - this.removeEventListener = function(e, f) { - if(e == 'ready') { - self.ready = null; - } - if(e == 'showConfiguration') { - self.showConfiguration = null; - } - if(e == 'webviewclosed') { - self.parseconfig = null; - } - if(e == 'appmessage') { - self.appmessage = null; - } - } this.actuallyOpenURL = function() { showStep("step1compat"); window.open(self.configurationURL.toString(), "config"); @@ -165,8 +167,6 @@ function gbPebble() { GBjs.gbLog("app wanted to show: " + title + " body: "+ body); } - this.ready = function() { - } this.showConfiguration = function() { console.error("This watchapp doesn't support configuration"); @@ -179,8 +179,8 @@ function gbPebble() { if (str.split(needle)[1] !== undefined) { var t = new Object(); - t.response = unescape(str.split(needle)[1]); - self.parseconfig(t); + t.response = decodeURIComponent(str.split(needle)[1]); + self.evaluate('webviewclosed',[t]); showStep("step2"); } else { console.error("No valid configuration found in the entered string."); @@ -198,11 +198,13 @@ if (jsConfigFile != null) { loadScript(jsConfigFile, function() { if (getURLVariable('config') == 'true') { showStep("step2"); - var json_string = unescape(getURLVariable('json')); + var json_string = decodeURIComponent(getURLVariable('json')); var t = new Object(); t.response = json_string; - if (json_string != '') - Pebble.parseconfig(t); + if (json_string != '') { + Pebble.evaluate('webviewclosed',[t]); + } + } else { if (storedPreset === undefined) { var presetElements = document.getElementsByClassName("load_presets"); @@ -210,8 +212,8 @@ if (jsConfigFile != null) { presetElements[i].style.display = 'none'; } } - Pebble.ready(); - Pebble.showConfiguration(); + Pebble.evaluate('ready'); + Pebble.evaluate('showConfiguration'); } }); } diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index bd975b90..c742ed41 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + Pebble: Further improve compatibility for watchface configuration + Pebble 2: Initial experimental support for P2/PT2 using BLE Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working) From 381323011ef9b98bbef3da4e104fd4b0efed3629 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 24 Nov 2016 18:52:58 +0100 Subject: [PATCH 056/147] Pebble 2: work around FW installation problems Wait a 0.5s after each complete Pebble packet and do not longer wait at all after a completed GATT package chunk (was 0.1s) Big outgoing messages will be now much faster such as firmware installations but smaller take more time than before. (This is not the proper fix but I could update the firmware this way, failed 10 times before after a few percent) --- .../service/devices/pebble/ble/PebbleGATTServer.java | 5 ----- .../service/devices/pebble/ble/PebbleLESupport.java | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java index c6c245b4..b762f87f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -57,12 +57,7 @@ class PebbleGATTServer extends BluetoothGattServerCallback { LOG.info("send data to pebble " + GB.hexdump(data, 0, -1)); writeCharacteristics.setValue(data.clone()); - mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false); - try { - Thread.sleep(100); // FIXME: bad bad, I mean BAAAD - } catch (InterruptedException ignore) { - } } synchronized private void sendAckToPebble(int serial) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 449b76d9..2346dab3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -126,6 +126,11 @@ public class PebbleLESupport { payloadToSend -= chunkSize; } + try { + Thread.sleep(500); // FIXME ugly wait 0.5s after each pebble package send to the pebble (we do not wait for the GATT chunks) + } catch (InterruptedException ignore) { + } + } catch (IOException e) { LOG.warn("IO exception"); mQuit = true; From 6520b46238e7f63254b467e30d2bb5f1c19ad0c8 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 24 Nov 2016 19:01:15 +0100 Subject: [PATCH 057/147] bump version, update changelogs --- CHANGELOG.md | 4 +++- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b50b6709..701824bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ###Changelog -####Version next +####Version 0.14.1 +* Mi Band 2: Initial experimental support for activity data +* Pebble 2: Work around firmware installation issues (tested with upgrading 4.2 to 4.3) * Pebble: Further improve compatibility for watchface configuration ####Version 0.14.0 diff --git a/app/build.gradle b/app/build.gradle index 75a6248b..e8fa2193 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.14.0" - versionCode 72 + versionName "0.14.1" + versionCode 73 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index c742ed41..e0c75295 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,6 +1,8 @@ - + + Mi Band 2: Initial experimental support for activity data + Pebble 2: Work around firmware installation issues (tested with upgrading 4.2 to 4.3) Pebble: Further improve compatibility for watchface configuration From cc0fbff297b467ac21cd2c16fe5e8dcd893d1b73 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Nov 2016 20:12:56 +0100 Subject: [PATCH 058/147] Set the sample provider in getLatestActivitySample() --- .../gadgetbridge/devices/AbstractSampleProvider.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index cd375c52..781548f1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -89,7 +89,9 @@ public abstract class AbstractSampleProvider i if (samples.isEmpty()) { return null; } - return samples.get(0); + T sample = samples.get(0); + sample.setProvider(this); + return sample; } protected List getGBActivitySamples(int timestamp_from, int timestamp_to, int activityType) { From 60cb67c3c8f0b5cb91ac26b8f5ffb1a29cd194c7 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Nov 2016 20:20:24 +0100 Subject: [PATCH 059/147] Some cleanup --- .../activities/charts/AbstractChartFragment.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 702243e9..3363e79d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -322,10 +322,6 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { return provider.getAllActivitySamples(tsFrom, tsTo); } - private int getTSLast24Hours(int tsTo) { - return (tsTo) - (24 * 60 * 60); // -24 hours - } - protected List getActivitySamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) { SampleProvider provider = getProvider(db, device); return provider.getActivitySamples(tsFrom, tsTo); @@ -337,18 +333,6 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { return provider.getSleepSamples(tsFrom, tsTo); } - protected List getTestSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2015, Calendar.JUNE, 10, 6, 40); - // ignore provided date ranges - tsTo = (int) ((cal.getTimeInMillis() / 1000)); - tsFrom = tsTo - (24 * 60 * 60); - - SampleProvider provider = getProvider(db, device); - return provider.getAllActivitySamples(tsFrom, tsTo); - } - protected void configureChartDefaults(Chart chart) { chart.getXAxis().setValueFormatter(new TimestampValueFormatter()); chart.getDescription().setText(""); From 9bebf1d32f2b0449d282d9fcd86dca1a94784ccf Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Nov 2016 21:11:28 +0100 Subject: [PATCH 060/147] When memory is really low, free up some memory #436 (although we probably can't save much) --- .../gadgetbridge/GBApplication.java | 28 +++++++++++++++++++ .../gadgetbridge/database/DBHelper.java | 10 +++++++ 2 files changed, 38 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 984f03ec..c3e7d447 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -22,6 +22,7 @@ import android.util.TypedValue; import java.io.File; import java.io.IOException; import java.util.HashSet; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -32,9 +33,11 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; @@ -131,6 +134,31 @@ public class GBApplication extends Application { } } + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + if (level >= TRIM_MEMORY_BACKGROUND) { + if (!hasBusyDevice()) { + DBHelper.clearSession(); + } + } + } + + /** + * Returns true if at least a single device is busy, e.g synchronizing activity data + * or something similar. + * Note: busy is not the same as connected or initialized! + */ + private boolean hasBusyDevice() { + List devices = getDeviceManager().getDevices(); + for (GBDevice device: devices) { + if (device.isBusy()) { + return true; + } + } + return true; + } + public static void setupLogging(boolean enabled) { logging.setupLogging(enabled); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 0794131d..3ec0ec6c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -26,6 +26,7 @@ import de.greenrobot.dao.query.Query; import de.greenrobot.dao.query.QueryBuilder; import de.greenrobot.dao.query.WhereCondition; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; @@ -685,4 +686,13 @@ public class DBHelper { } return cursor.getInt(columnIndex); } + + public static void clearSession() { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + session.clear(); + } catch (Exception e) { + LOG.warn("Unable to acquire database to clear the session", e); + } + } } From 3fdfb7d17221254a803741b0d48bc8b91e58291f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Nov 2016 21:58:32 +0100 Subject: [PATCH 061/147] Mi2: Support for setting the fitness goal (steps) --- .../devices/miband/MiBand2Service.java | 4 ++++ .../miband/MiBandPreferencesActivity.java | 14 ++++++++++++ .../devices/miband/MiBand2Support.java | 22 ++++++++++++------- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index 51eead04..931b91c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -282,6 +282,10 @@ public class MiBand2Service { // maybe not really activity data, but steps? public static final byte COMMAND_FETCH_ACTIVITY_DATA = 0x02; + public static final byte[] COMMAND_SET_FITNESS_GOAL_START = new byte[] { 0x10, 0x0, 0x0 }; + public static final byte[] COMMAND_SET_FITNESS_GOAL_END = new byte[] { 0, 0 }; + + public static byte COMMAND_DATEFORMAT = 0x06; public static final byte[] DATEFORMAT_DATE_TIME = new byte[] { COMMAND_DATEFORMAT, 0x0a, 0x0, 0x03 }; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java index af1d07a8..00b77c06 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java @@ -74,6 +74,20 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { return true; } }); + + final Preference fitnessGoal = findPreference(PREF_MIBAND_FITNESS_GOAL); + fitnessGoal.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MIBAND_FITNESS_GOAL); + } + }); + return true; + } + }); } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index c4714b89..963a34b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -10,10 +10,12 @@ import android.net.Uri; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; +import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -388,15 +390,15 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { private MiBand2Support setFitnessGoal(TransactionBuilder transaction) { LOG.info("Attempting to set Fitness Goal..."); - BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC8); if (characteristic != null) { int fitnessGoal = MiBandCoordinator.getFitnessGoal(getDevice().getAddress()); - transaction.write(characteristic, new byte[]{ - MiBandService.COMMAND_SET_FITNESS_GOAL, - 0, - (byte) (fitnessGoal & 0xff), - (byte) ((fitnessGoal >>> 8) & 0xff) - }); + byte[] bytes = ArrayUtils.addAll( + MiBand2Service.COMMAND_SET_FITNESS_GOAL_START, + BLETypeConversions.fromUint16(fitnessGoal)); + bytes = ArrayUtils.addAll(bytes, + MiBand2Service.COMMAND_SET_FITNESS_GOAL_END); + transaction.write(characteristic, bytes); } else { LOG.info("Unable to set Fitness Goal"); } @@ -419,7 +421,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { case 0: // left hand builder.write(characteristic, MiBand2Service.WEAR_LOCATION_LEFT_WRIST); break; - case 1: // write hand + case 1: // right hand builder.write(characteristic, MiBand2Service.WEAR_LOCATION_RIGHT_WRIST); break; } @@ -1181,6 +1183,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { case MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT: setActivateDisplayOnLiftWrist(builder); break; + case MiBandConst.PREF_MIBAND_FITNESS_GOAL: + setFitnessGoal(builder); + break; } builder.queue(getQueue()); } catch (IOException e) { @@ -1222,6 +1227,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { enableFurtherNotifications(builder, true); setDateDisplay(builder); setWearLocation(builder); + setFitnessGoal(builder); setActivateDisplayOnLiftWrist(builder); setHeartrateSleepSupport(builder); From 02e6ce02b243e121460f03cb97e2baf9122a75f6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Nov 2016 22:03:02 +0100 Subject: [PATCH 062/147] Add to 0.14.1 changelog --- CHANGELOG.md | 2 ++ app/src/main/res/xml/changelog_master.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 701824bc..8db743e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,10 @@ ####Version 0.14.1 * Mi Band 2: Initial experimental support for activity data +* Mi Band 2: Send the fitness goal (steps) to the band * Pebble 2: Work around firmware installation issues (tested with upgrading 4.2 to 4.3) * Pebble: Further improve compatibility for watchface configuration +* Charts: display the total time range, not just the range with available data ####Version 0.14.0 * Pebble 2: Initial experimental support for P2/PT2 using BLE diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index e0c75295..ab207d9e 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,8 +2,10 @@ Mi Band 2: Initial experimental support for activity data + Mi Band 2: Send the fitness goal (steps) to the band Pebble 2: Work around firmware installation issues (tested with upgrading 4.2 to 4.3) Pebble: Further improve compatibility for watchface configuration + Charts: display the total time range, not just the range with available data Pebble 2: Initial experimental support for P2/PT2 using BLE From 9d083e2330dbaa34ad7d73e1a8cdbf7ba6729429 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 24 Nov 2016 22:09:27 +0100 Subject: [PATCH 063/147] Pebble: add Kickstart Watchface to app manager on FW 4.X I know it only exist on 4.3+, but I am lazy --- .../activities/appmanager/AbstractAppManagerFragment.java | 5 +++++ .../appmanager/AppManagerFragmentInstalledWatchfaces.java | 1 + 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index 50b86ced..3c0de5ae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -195,6 +195,11 @@ public abstract class AbstractAppManagerFragment extends Fragment { continue; } } + if (PebbleUtils.getFwMajor(mGBDevice.getFirmwareVersion()) >= 4) { + if (baseName.equals("3af858c3-16cb-4561-91e7-f1ad2df8725f")) { + cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), "Kickstart (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); + } + } } if (uuids == null) { cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), baseName, "N/A", "", GBDeviceApp.Type.UNKNOWN)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java index a0df8d89..b41fad04 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java @@ -12,6 +12,7 @@ public class AppManagerFragmentInstalledWatchfaces extends AbstractAppManagerFra protected List getSystemAppsInCategory() { List systemWatchfaces = new ArrayList<>(); systemWatchfaces.add(new GBDeviceApp(UUID.fromString("8f3c8686-31a1-4f5f-91f5-01600c9bdc59"), "Tic Toc (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); + systemWatchfaces.add(new GBDeviceApp(UUID.fromString("3af858c3-16cb-4561-91e7-f1ad2df8725f"), "Kickstart (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); return systemWatchfaces; } From df4293108ab2b625fe9d79d243f3ae5b0f8f9874 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 24 Nov 2016 22:11:59 +0100 Subject: [PATCH 064/147] update changelog (again) --- CHANGELOG.md | 1 + app/src/main/res/xml/changelog_master.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8db743e4..dc3878f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Mi Band 2: Send the fitness goal (steps) to the band * Pebble 2: Work around firmware installation issues (tested with upgrading 4.2 to 4.3) * Pebble: Further improve compatibility for watchface configuration +* Pebble: add Kickstart watch face to app manager on FW 4.x * Charts: display the total time range, not just the range with available data ####Version 0.14.0 diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index ab207d9e..28d67e49 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -5,6 +5,7 @@ Mi Band 2: Send the fitness goal (steps) to the band Pebble 2: Work around firmware installation issues (tested with upgrading 4.2 to 4.3) Pebble: Further improve compatibility for watchface configuration + Pebble: add Kickstart watch face to app manager on FW 4.x Charts: display the total time range, not just the range with available data From 7da328d5dbdb325d81bbc3e07d2db87bc4d9de2e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Nov 2016 22:15:06 +0100 Subject: [PATCH 065/147] Fix an invalid leftover check for array length Also removed a method invocation that did not belong there. --- .../devices/miband2/operations/FetchActivityOperation.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java index 92449abd..4979d70f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -173,7 +173,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { return; } - if (value.length == 17) { + if ((value.length % 4) == 1) { if ((byte) (lastPacketCounter + 1) == value[0] ) { lastPacketCounter++; bufferActivityData(value); @@ -182,7 +182,6 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { handleActivityFetchFinish(); return; } - handleActivityMetadata(value); } else { GB.toast("Error fetching activity data, unexpected package length: " + value.length, Toast.LENGTH_LONG, GB.ERROR); } From 84caf22479602899aa85231fa2f14b934cef1345 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 24 Nov 2016 22:26:54 +0100 Subject: [PATCH 066/147] fix weekly charts to start y axis at 0 --- .../gadgetbridge/activities/charts/WeekStepsChartFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java index e20c310d..3d08ed5e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java @@ -192,6 +192,7 @@ public class WeekStepsChartFragment extends AbstractChartFragment { y.setTextColor(CHART_TEXT_COLOR); y.setDrawZeroLine(true); y.setSpaceBottom(0); + y.setAxisMinimum(0); y.setEnabled(true); From 79eb4f32df963b46d9022cbd4a405a4a624f52a6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 24 Nov 2016 22:44:18 +0100 Subject: [PATCH 067/147] update Japanese and Spanish from transifex (thanks!) --- app/src/main/res/values-es/strings.xml | 8 ++++++++ app/src/main/res/values-ja/strings.xml | 1 + 2 files changed, 9 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 88b38e74..f79966ec 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -25,6 +25,8 @@ Buscar en la Appstore de Pebble Activar Desactivar + Activar Monitor de Ritmo Cardíaco + Desactivar Monitor de Ritmo Cardíaco Configurar Mover a la parte de arriba @@ -85,6 +87,7 @@ Buscar localización Latitud Longitud + Mantener la localización actualizada Por favor, activa la localización por red localización encontrada Forzar protocolo de notificación @@ -244,6 +247,9 @@ Reserva de alarmas para próximos eventos Usar sensor de pulsaciones para mejorar la detección del sueño Compensación de la hora del dispositivo en horas (para detectar el sueño de trabajadores a turnos) + Mi2: Formato de fecha + Tiempo + Activar pantalla al levantar esperando reconexión Sobre ti Año de nacimiento @@ -299,4 +305,6 @@ Borrar Vibración + + Emparejando con Pebble diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 67f701a2..4b19c327 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -253,6 +253,7 @@ 時間 持ち上げ時に表示を有効にする + %1$s からデータを転送することについて 再接続の待機中 あなたについて 誕生年 From 2d4645f6cc17e908103d4e91ce6c515382fb6e26 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Nov 2016 23:34:33 +0100 Subject: [PATCH 068/147] Fix only the first day label being displayed in Week Steps charts --- .../activities/charts/AbstractChartFragment.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 3363e79d..b1beeb77 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -346,7 +346,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { // enable touch gestures chart.setTouchEnabled(true); - chart.getXAxis().setGranularity(60*5); +// commented out: this has weird bugs/sideeffects at least on WeekStepsCharts +// where only the first Day-label is drawn, because AxisRenderer.computeAxisValues(float,float) +// appears to have an overflow when calculating 'n' (number of entries) +// chart.getXAxis().setGranularity(60*5); setupLegend(chart); } From a5263141d7bbe7f17c073f88662f0ebe7f84d819 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 25 Nov 2016 14:28:04 +0100 Subject: [PATCH 069/147] Pebble 2: Ignore all GATT communication with all other that the current device Fixes a bad bug where disconnecting from another BLE device caused the Pebble2 to disconnect --- .../devices/pebble/ble/PebbleGATTClient.java | 26 +++++++++---------- .../devices/pebble/ble/PebbleGATTServer.java | 20 ++++++++++++++ .../devices/pebble/ble/PebbleLESupport.java | 14 +++++++--- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index 5d2c6038..3019f385 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -31,7 +31,6 @@ class PebbleGATTClient extends BluetoothGattCallback { private static final UUID CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.fromString("00000005-328E-0FBB-C642-1AA6699BDADA"); private static final UUID CHARACTERISTIC_CONFIGURATION_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); - private final String mBtDeviceAddress; private final BluetoothDevice mBtDevice; private final Context mContext; private final PebbleLESupport mPebbleLESupport; @@ -45,7 +44,6 @@ class PebbleGATTClient extends BluetoothGattCallback { mContext = context; mBtDevice = btDevice; mPebbleLESupport = pebbleLESupport; - mBtDeviceAddress = btDevice.getAddress(); } boolean initialize() { @@ -54,10 +52,10 @@ class PebbleGATTClient extends BluetoothGattCallback { } public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { - LOG.info("onCharacteristicChanged() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + if (!mPebbleLESupport.isExpectedDevice(gatt.getDevice())) { return; } + if (characteristic.getUuid().equals(MTU_CHARACTERISTIC)) { int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0); LOG.info("Pebble requested MTU: " + newMTU); @@ -68,10 +66,10 @@ class PebbleGATTClient extends BluetoothGattCallback { } public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { - LOG.info("onCharacteristicRead() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + if (!mPebbleLESupport.isExpectedDevice(gatt.getDevice())) { return; } + LOG.info("onCharacteristicRead() status = " + status); if (status == BluetoothGatt.GATT_SUCCESS) { LOG.info("onCharacteristicRead()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); @@ -85,10 +83,10 @@ class PebbleGATTClient extends BluetoothGattCallback { } public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { - LOG.info("onConnectionStateChange() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + if (!mPebbleLESupport.isExpectedDevice(gatt.getDevice())) { return; } + LOG.info("onConnectionStateChange() status = " + status + " newState = " + newState); if (newState == BluetoothGatt.STATE_CONNECTED) { LOG.info("calling discoverServices()"); @@ -99,10 +97,10 @@ class PebbleGATTClient extends BluetoothGattCallback { } public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { - LOG.info("onCharacteristcsWrite unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + if (!mPebbleLESupport.isExpectedDevice(gatt.getDevice())) { return; } + LOG.info("onCharacteristicWrite() " + characteristic.getUuid()); if (characteristic.getUuid().equals(PAIRING_TRIGGER_CHARACTERISTIC) || characteristic.getUuid().equals(CONNECTIVITY_CHARACTERISTIC)) { //mBtDevice.createBond(); // did not work when last tried @@ -120,10 +118,10 @@ class PebbleGATTClient extends BluetoothGattCallback { } public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor bluetoothGattDescriptor, int status) { - if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { - LOG.info("onDescriptorWrite() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + if (!mPebbleLESupport.isExpectedDevice(gatt.getDevice())) { return; } + LOG.info("onDescriptorWrite() status=" + status); UUID CHARACTERISTICUUID = bluetoothGattDescriptor.getCharacteristic().getUuid(); @@ -138,10 +136,10 @@ class PebbleGATTClient extends BluetoothGattCallback { } public void onServicesDiscovered(BluetoothGatt gatt, int status) { - if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) { - LOG.info("onServicesDiscovered() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress); + if (!mPebbleLESupport.isExpectedDevice(gatt.getDevice())) { return; } + LOG.info("onServicesDiscovered() status = " + status); if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattCharacteristic connectionPararmharacteristic = gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTION_PARAMETERS_CHARACTERISTIC); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java index b762f87f..b4167d41 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -74,6 +74,10 @@ class PebbleGATTServer extends BluetoothGattServerCallback { } public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { + if (!mPebbleLESupport.isExpectedDevice(device)) { + return; + } + if (!characteristic.getUuid().equals(READ_CHARACTERISTICS)) { LOG.warn("unexpected read request"); return; @@ -88,6 +92,10 @@ class PebbleGATTServer extends BluetoothGattServerCallback { public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { + if (!mPebbleLESupport.isExpectedDevice(device)) { + return; + } + if (!characteristic.getUuid().equals(WRITE_CHARACTERISTICS)) { LOG.warn("unexpected write request"); return; @@ -117,6 +125,10 @@ class PebbleGATTServer extends BluetoothGattServerCallback { } public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { + if (!mPebbleLESupport.isExpectedDevice(device)) { + return; + } + LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState); if (newState == BluetoothGattServer.STATE_DISCONNECTED) { mPebbleLESupport.close(); @@ -126,6 +138,10 @@ class PebbleGATTServer extends BluetoothGattServerCallback { public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { + if (!mPebbleLESupport.isExpectedDevice(device)) { + return; + } + if (!descriptor.getCharacteristic().getUuid().equals(WRITE_CHARACTERISTICS)) { LOG.warn("unexpected write request"); return; @@ -148,6 +164,10 @@ class PebbleGATTServer extends BluetoothGattServerCallback { @Override public void onMtuChanged(BluetoothDevice device, int mtu) { + if (!mPebbleLESupport.isExpectedDevice(device)) { + return; + } + LOG.info("Pebble requested mtu for server: " + mtu); mPebbleLESupport.setMTU(mtu); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 2346dab3..7aacff68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -14,6 +14,7 @@ import java.io.PipedOutputStream; public class PebbleLESupport { private static final Logger LOG = LoggerFactory.getLogger(PebbleLESupport.class); + private final BluetoothDevice mBtDevice; private PipeReader mPipeReader; private PebbleGATTServer mPebbleGATTServer; private PebbleGATTClient mPebbleGATTClient; @@ -34,11 +35,11 @@ public class PebbleLESupport { BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter adapter = manager.getAdapter(); - BluetoothDevice btDevice = adapter.getRemoteDevice(btDeviceAddress); - mPebbleGATTServer = new PebbleGATTServer(this, context, btDevice); + mBtDevice = adapter.getRemoteDevice(btDeviceAddress); + mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice); mPebbleGATTServer.initialize(); - mPebbleGATTClient = new PebbleGATTClient(this, context, btDevice); + mPebbleGATTClient = new PebbleGATTClient(this, context, mBtDevice); mPebbleGATTClient.initialize(); } @@ -149,5 +150,12 @@ public class PebbleLESupport { } } + boolean isExpectedDevice(BluetoothDevice device) { + if (!device.getAddress().equals(mBtDevice.getAddress())) { + LOG.info("unhandled device: " + device.getAddress() + " , ignoring, will only talk to " + mBtDevice.getAddress()); + return false; + } + return true; + } } From 6106dda2a3bffb0e3ca8850bf8bda3abbad4cb26 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 25 Nov 2016 14:31:56 +0100 Subject: [PATCH 070/147] bump version, update changelog --- CHANGELOG.md | 3 +++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc3878f4..2382d1ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version 0.14.2 +* Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices + ####Version 0.14.1 * Mi Band 2: Initial experimental support for activity data * Mi Band 2: Send the fitness goal (steps) to the band diff --git a/app/build.gradle b/app/build.gradle index e8fa2193..7d3500a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.14.1" - versionCode 73 + versionName "0.14.2" + versionCode 74 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 28d67e49..50ad1d7e 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices + Mi Band 2: Initial experimental support for activity data Mi Band 2: Send the fitness goal (steps) to the band From 352fc1a030f06de0d770e9dff79dc9d5ad457e02 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 25 Nov 2016 14:53:12 +0100 Subject: [PATCH 071/147] fix wrong return value --- .../nodomain/freeyourgadget/gadgetbridge/GBApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index c3e7d447..294ca36e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -156,7 +156,7 @@ public class GBApplication extends Application { return true; } } - return true; + return false; } public static void setupLogging(boolean enabled) { From b2e86ca06137f8cf6070e076671d320ec8aa84c0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 25 Nov 2016 14:55:30 +0100 Subject: [PATCH 072/147] fix xml in changelog --- app/src/main/res/xml/changelog_master.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 50ad1d7e..b7c3d3a4 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,7 +1,7 @@ - Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices + Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices Mi Band 2: Initial experimental support for activity data From c84003c1c083c0391f4089f5c411f376e97b2b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gilles=20=C3=89milien=20MOREL?= Date: Fri, 25 Nov 2016 19:37:21 +0100 Subject: [PATCH 073/147] Allumer instead of Alumer --- app/src/main/res/values-fr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e956aca9..f634a340 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -244,7 +244,7 @@ Mi band 2 : format de l\'heure Heure seule - Alumer quand le poignet se lève + Allumer quand le poignet se lève About to transfer data since %1$s en attente de reconnexion A propos de vous From b9ff2cd46848383bc4c1e9fabf8d53412046b156 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 27 Nov 2016 01:09:20 +0100 Subject: [PATCH 074/147] A few improvements to discovery - display the right icon for found device candidates - scan for specific LE services --- .../activities/DiscoveryActivity.java | 18 ++++++++++-- .../devices/AbstractDeviceCoordinator.java | 14 +++++++++ .../devices/DeviceCoordinator.java | 29 +++++++++++++++++-- .../devices/UnknownDeviceCoordinator.java | 4 +-- .../devices/miband/MiBand2Coordinator.java | 27 ++++++++++++++--- .../devices/miband/MiBandCoordinator.java | 29 +++++++++++++++---- .../devices/pebble/PebbleCoordinator.java | 8 +++-- .../vibratissimo/VibratissimoCoordinator.java | 7 +++-- .../gadgetbridge/impl/GBDeviceCandidate.java | 7 ++++- .../gadgetbridge/model/DeviceType.java | 4 +++ .../gadgetbridge/util/DeviceHelper.java | 14 ++++----- 11 files changed, 134 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index 6dd5cbc9..bb07ad63 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -7,6 +7,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.BroadcastReceiver; @@ -32,6 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; @@ -39,9 +41,11 @@ import nodomain.freeyourgadget.gadgetbridge.adapter.DeviceCandidateAdapter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES; import static android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY; import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY; @@ -257,7 +261,9 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC } GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi); - if (DeviceHelper.getInstance().isSupported(candidate)) { + DeviceType deviceType = DeviceHelper.getInstance().getSupportedType(candidate); + if (deviceType.isSupported()) { + candidate.setDeviceType(deviceType); int index = deviceCandidates.indexOf(candidate); if (index >= 0) { deviceCandidates.set(index, candidate); // replace @@ -410,7 +416,15 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC LOG.info("Start New BTLE Discovery"); handler.removeMessages(0, stopRunnable); handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION); - adapter.getBluetoothLeScanner().startScan(null, getScanSettings(), getScanCallback()); + adapter.getBluetoothLeScanner().startScan(getScanFilters(), getScanSettings(), getScanCallback()); + } + + private List getScanFilters() { + List allFilters = new ArrayList<>(); + for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) { + allFilters.addAll(coordinator.createBLEScanFilters()); + } + return allFilters; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index c403e6ec..46a65e09 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -2,11 +2,15 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; @@ -21,12 +25,22 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviceCoordinator.class); + @Override + public final boolean supports(GBDeviceCandidate candidate) { + return getSupportedType(candidate).isSupported(); + } @Override public boolean supports(GBDevice device) { return getDeviceType().equals(device.getType()); } + @NonNull + @Override + public Collection createBLEScanFilters() { + return Collections.emptyList(); + } + @Override public GBDevice createDevice(GBDeviceCandidate candidate) { return new GBDevice(candidate.getDevice().getAddress(), candidate.getName(), getDeviceType()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index c2eace40..43f0e8d4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -1,8 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices; +import android.annotation.TargetApi; import android.app.Activity; +import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; +import android.os.Build; +import android.support.annotation.NonNull; + +import java.util.Collection; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; @@ -24,7 +30,18 @@ public interface DeviceCoordinator { String EXTRA_DEVICE_MAC_ADDRESS = "nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate.EXTRA_MAC_ADDRESS"; /** - * Checks whether this candidate handles the given candidate. + * Checks whether this coordinator handles the given candidate. + * Returns the supported device type for the given candidate or + * DeviceType.UNKNOWN + * + * @param candidate + * @return the supported device type for the given candidate. + */ + @NonNull + DeviceType getSupportedType(GBDeviceCandidate candidate); + + /** + * Checks whether this coordinator handles the given candidate. * * @param candidate * @return true if this coordinator handles the given candidate. @@ -39,6 +56,15 @@ public interface DeviceCoordinator { */ boolean supports(GBDevice device); + /** + * Returns a list of scan filters that shall be used to discover devices supported + * by this coordinator. + * @return the list of scan filters, may be empty + */ + @NonNull + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + Collection createBLEScanFilters(); + GBDevice createDevice(GBDeviceCandidate candidate); /** @@ -154,5 +180,4 @@ public interface DeviceCoordinator { * @return */ Class getAppsManagementActivity(); - } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index d7379c45..6a50ba0f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -82,8 +82,8 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { } @Override - public boolean supports(GBDeviceCandidate candidate) { - return false; + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + return DeviceType.UNKNOWN; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 84bc5d8a..5ce4eddf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -1,12 +1,20 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; +import android.annotation.TargetApi; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; @@ -23,19 +31,30 @@ public class MiBand2Coordinator extends MiBandCoordinator { return DeviceType.MIBAND2; } + @NonNull @Override - public boolean supports(GBDeviceCandidate candidate) { - // and a heuristic + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid mi2Service = new ParcelUuid(MiBandService.UUID_SERVICE_MIBAND2_SERVICE); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mi2Service).build(); + return Collections.singletonList(filter); + } + + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + // and a heuristic for now try { BluetoothDevice device = candidate.getDevice(); if (isHealthWearable(device)) { String name = device.getName(); - return name != null && name.equalsIgnoreCase(MiBandConst.MI_BAND2_NAME); + if (name != null && name.equalsIgnoreCase(MiBandConst.MI_BAND2_NAME)) { + return DeviceType.MIBAND2; + } } } catch (Exception ex) { LOG.error("unable to check device support", ex); } - return false; + return DeviceType.UNKNOWN; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index eee28a3e..c44a3321 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -1,13 +1,21 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; +import android.annotation.TargetApi; import android.app.Activity; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; @@ -32,28 +40,39 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { public MiBandCoordinator() { } + @NonNull @Override - public boolean supports(GBDeviceCandidate candidate) { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid mi1Service = new ParcelUuid(MiBandService.UUID_SERVICE_MIBAND_SERVICE); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mi1Service).build(); + return Collections.singletonList(filter); + } + + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { String macAddress = candidate.getMacAddress().toUpperCase(); if (macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1_1A) || macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1S)) { - return true; + return DeviceType.MIBAND; } if (candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND_SERVICE) && !candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND2_SERVICE)) { - return true; + return DeviceType.MIBAND; } // and a heuristic try { BluetoothDevice device = candidate.getDevice(); if (isHealthWearable(device)) { String name = device.getName(); - return name != null && name.toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase()); + if (name != null && name.toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase())) { + return DeviceType.MIBAND; + } } } catch (Exception ex) { LOG.error("unable to check device support", ex); } - return false; + return DeviceType.UNKNOWN; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index 34152eae..5953113d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -30,9 +30,12 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { } @Override - public boolean supports(GBDeviceCandidate candidate) { + public DeviceType getSupportedType(GBDeviceCandidate candidate) { String name = candidate.getDevice().getName(); - return name != null && name.startsWith("Pebble"); + if (name != null && name.startsWith("Pebble")) { + return DeviceType.PEBBLE; + } + return DeviceType.UNKNOWN; } @Override @@ -45,6 +48,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { return PebblePairingActivity.class; } + @Override public Class getPrimaryActivity() { return AppManagerActivity.class; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java index d36b0c40..8f71f234 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java @@ -20,9 +20,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; public class VibratissimoCoordinator extends AbstractDeviceCoordinator { @Override - public boolean supports(GBDeviceCandidate candidate) { + public DeviceType getSupportedType(GBDeviceCandidate candidate) { String name = candidate.getDevice().getName(); - return name != null && name.startsWith("Vibratissimo"); + if (name != null && name.startsWith("Vibratissimo")) { + return DeviceType.VIBRATISSIMO; + } + return DeviceType.UNKNOWN; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java index 7820bc2a..bb9d7fae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -4,6 +4,7 @@ import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +39,7 @@ public class GBDeviceCandidate implements Parcelable { rssi = (short) in.readInt(); deviceType = DeviceType.valueOf(in.readString()); - if (device == null || deviceType == null) { + if (device == null) { throw new IllegalStateException("Unable to read state from Parcel"); } } @@ -54,6 +55,10 @@ public class GBDeviceCandidate implements Parcelable { return device; } + public void setDeviceType(DeviceType type) { + deviceType = type; + } + public DeviceType getDeviceType() { return deviceType; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 43223637..2a1c859e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -24,6 +24,10 @@ public enum DeviceType { return key; } + public boolean isSupported() { + return this != UNKNOWN; + } + public static DeviceType fromKey(int key) { for (DeviceType type : values()) { if (type.key == key) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index a14aff80..19078571 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -8,7 +8,6 @@ import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; @@ -47,16 +46,17 @@ public class DeviceHelper { // lazily created private List coordinators; - public boolean isSupported(GBDeviceCandidate candidate) { + public DeviceType getSupportedType(GBDeviceCandidate candidate) { for (DeviceCoordinator coordinator : getAllCoordinators()) { - if (coordinator.supports(candidate)) { - return true; + DeviceType deviceType = coordinator.getSupportedType(candidate); + if (deviceType.isSupported()) { + return deviceType; } } - return false; + return DeviceType.UNKNOWN; } - public boolean isSupported(GBDevice device) { + public boolean getSupportedType(GBDevice device) { for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(device)) { return true; @@ -174,7 +174,7 @@ public class DeviceHelper { List activeDevices = DBHelper.getActiveDevices(lockHandler.getDaoSession()); for (Device dbDevice : activeDevices) { GBDevice gbDevice = toGBDevice(dbDevice); - if (gbDevice != null && DeviceHelper.getInstance().isSupported(gbDevice)) { + if (gbDevice != null && DeviceHelper.getInstance().getSupportedType(gbDevice)) { result.add(gbDevice); } } From 2f7eb9ef23565c91c617b80541bfac4a18ff62b6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 27 Nov 2016 02:41:52 +0100 Subject: [PATCH 075/147] Some more improvements to discovery - pass service uuids to GBDeviceCandaidate so that DeviceCoordinators can detect devices by their services. Note: they should not rely on service uuids being available --- .../activities/DiscoveryActivity.java | 40 +++++++++++++-- .../devices/miband/MiBand2Coordinator.java | 5 ++ .../devices/miband/MiBandCoordinator.java | 1 + .../gadgetbridge/impl/GBDeviceCandidate.java | 50 ++++++++++++++++--- .../gadgetbridge/util/AndroidUtils.java | 15 ++++++ .../gadgetbridge/util/DeviceHelper.java | 2 +- 6 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index bb07ad63..579f8234 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -8,6 +8,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.BroadcastReceiver; @@ -42,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -95,6 +97,14 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC handleDeviceFound(device, rssi); break; } + case BluetoothDevice.ACTION_UUID: { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN); + Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID); + ParcelUuid[] uuids2 = AndroidUtils.toParcelUUids(uuids); + handleDeviceFound(device, rssi, uuids2); + break; + } case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device != null && device.getAddress().equals(bondingAddress)) { @@ -131,10 +141,18 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); try { + ScanRecord scanRecord = result.getScanRecord(); + ParcelUuid[] uuids = null; + if (scanRecord != null) { + //logMessageContent(scanRecord.getBytes()); + List serviceUuids = scanRecord.getServiceUuids(); + if (serviceUuids != null) { + uuids = serviceUuids.toArray(new ParcelUuid[0]); + } + } LOG.warn(result.getDevice().getName() + ": " + - ((result.getScanRecord() != null) ? result.getScanRecord().getBytes().length : -1)); - //logMessageContent(result.getScanRecord().getBytes()); - handleDeviceFound(result.getDevice(), (short) result.getRssi()); + ((scanRecord != null) ? scanRecord.getBytes().length : -1)); + handleDeviceFound(result.getDevice(), (short) result.getRssi(), uuids); } catch (NullPointerException e) { LOG.warn("Error handling scan result", e); } @@ -199,6 +217,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC IntentFilter bluetoothIntents = new IntentFilter(); bluetoothIntents.addAction(BluetoothDevice.ACTION_FOUND); + bluetoothIntents.addAction(BluetoothDevice.ACTION_UUID); bluetoothIntents.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); @@ -247,9 +266,20 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC } private void handleDeviceFound(BluetoothDevice device, short rssi) { + ParcelUuid[] uuids = device.getUuids(); + if (uuids == null) { + if (device.fetchUuidsWithSdp()) { + return; + } + } + + handleDeviceFound(device, rssi, uuids); + } + + + private void handleDeviceFound(BluetoothDevice device, short rssi, ParcelUuid[] uuids) { LOG.debug("found device: " + device.getName() + ", " + device.getAddress()); if (LOG.isDebugEnabled()) { - ParcelUuid[] uuids = device.getUuids(); if (uuids != null && uuids.length > 0) { for (ParcelUuid uuid : uuids) { LOG.debug(" supports uuid: " + uuid.toString()); @@ -260,7 +290,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC return; // ignore already bonded devices } - GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi); + GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi, uuids); DeviceType deviceType = DeviceHelper.getInstance().getSupportedType(candidate); if (deviceType.isSupported()) { candidate.setDeviceType(deviceType); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 5ce4eddf..12c945e0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -40,8 +40,13 @@ public class MiBand2Coordinator extends MiBandCoordinator { return Collections.singletonList(filter); } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { + if (candidate.supportsService(MiBand2Service.UUID_SERVICE_MIBAND2_SERVICE)) { + return DeviceType.MIBAND2; + } + // and a heuristic for now try { BluetoothDevice device = candidate.getDevice(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index c44a3321..3597706c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -49,6 +49,7 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return Collections.singletonList(filter); } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { String macAddress = candidate.getMacAddress().toUpperCase(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java index bb9d7fae..4fc25f62 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -5,17 +5,22 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; /** * A device candidate is a Bluetooth device that is not yet managed by @@ -27,21 +32,25 @@ public class GBDeviceCandidate implements Parcelable { private final BluetoothDevice device; private final short rssi; + private final ParcelUuid[] serviceUuds; private DeviceType deviceType = DeviceType.UNKNOWN; - public GBDeviceCandidate(BluetoothDevice device, short rssi) { + public GBDeviceCandidate(BluetoothDevice device, short rssi, ParcelUuid[] serviceUuds) { this.device = device; this.rssi = rssi; + this.serviceUuds = mergeServiceUuids(serviceUuds, device.getUuids()); } private GBDeviceCandidate(Parcel in) { device = in.readParcelable(getClass().getClassLoader()); - rssi = (short) in.readInt(); - deviceType = DeviceType.valueOf(in.readString()); - if (device == null) { throw new IllegalStateException("Unable to read state from Parcel"); } + rssi = (short) in.readInt(); + deviceType = DeviceType.valueOf(in.readString()); + + ParcelUuid[] uuids = AndroidUtils.toParcelUUids(in.readParcelableArray(getClass().getClassLoader())); + serviceUuds = mergeServiceUuids(uuids, device.getUuids()); } @Override @@ -49,8 +58,21 @@ public class GBDeviceCandidate implements Parcelable { dest.writeParcelable(device, 0); dest.writeInt(rssi); dest.writeString(deviceType.name()); + dest.writeArray(serviceUuds); } + public static final Creator CREATOR = new Creator() { + @Override + public GBDeviceCandidate createFromParcel(Parcel in) { + return new GBDeviceCandidate(in); + } + + @Override + public GBDeviceCandidate[] newArray(int size) { + return new GBDeviceCandidate[size]; + } + }; + public BluetoothDevice getDevice() { return device; } @@ -67,9 +89,25 @@ public class GBDeviceCandidate implements Parcelable { return device != null ? device.getAddress() : GBApplication.getContext().getString(R.string._unknown_); } + private ParcelUuid[] mergeServiceUuids(ParcelUuid[] serviceUuds, ParcelUuid[] deviceUuids) { + Set uuids = new HashSet<>(); + if (serviceUuds != null) { + uuids.addAll(Arrays.asList(serviceUuds)); + } + if (deviceUuids != null) { + uuids.addAll(Arrays.asList(deviceUuids)); + } + return uuids.toArray(new ParcelUuid[0]); + } + + @NonNull + public ParcelUuid[] getServiceUuids() { + return serviceUuds; + } + public boolean supportsService(UUID aService) { - ParcelUuid[] uuids = device.getUuids(); - if (uuids == null) { + ParcelUuid[] uuids = getServiceUuids(); + if (uuids.length == 0) { LOG.warn("no cached services available for " + this); return false; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java new file mode 100644 index 00000000..f08691e2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -0,0 +1,15 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +import android.os.ParcelUuid; +import android.os.Parcelable; + +public class AndroidUtils { + public static ParcelUuid[] toParcelUUids(Parcelable[] uuids) { + if (uuids == null) { + return null; + } + ParcelUuid[] uuids2 = new ParcelUuid[uuids.length]; + System.arraycopy(uuids, 0, uuids2, 0, uuids.length); + return uuids2; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 19078571..df4a76f8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -120,7 +120,7 @@ public class DeviceHelper { } public GBDevice toSupportedDevice(BluetoothDevice device) { - GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN); + GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN, device.getUuids()); for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(candidate)) { From 34ad088b883bc8969ca5607fb112b6bd6d03804a Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Nov 2016 09:49:28 +0100 Subject: [PATCH 076/147] Pebble: Experimental support for BLE on all models via dev option in Pebble Settings HOWTO: 1) Pair you normal Pebble (not necessary if already done), make sure it was connected once 2) Unpair your LE pebble if already paired 3) Switch on "Always prefer BLE" in Pebble Settings 4) Tap on the + in Control Center to add a new device 5) Pair your Pebble-LE XXXX or Pebble Time LE XXXX inside Gadgetbridge's Device Discovery actibity Now Gadgetbridge will connect to your LE Pebble when tapping on Pebble XXXX if "Always Prefer BLE" option is enabled. You can easily switch back to classic LE by turning that option off again --- .../gadgetbridge/daogen/GBDaoGenerator.java | 7 +- .../devices/pebble/PebblePairingActivity.java | 111 ++++++++++++++---- .../gadgetbridge/impl/GBDevice.java | 20 ++++ .../service/DeviceCommunicationService.java | 2 +- .../devices/pebble/PebbleIoThread.java | 24 ++-- .../devices/pebble/ble/PebbleLESupport.java | 9 +- .../service/serial/GBDeviceIoThread.java | 2 +- .../gadgetbridge/util/DeviceHelper.java | 6 +- app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/preferences.xml | 5 + 10 files changed, 144 insertions(+), 44 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 3a197772..63e5d713 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -40,13 +40,18 @@ public class GBDaoGenerator { private static final String TIMESTAMP_TO = "timestampTo"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(14, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(15, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); Entity deviceAttributes = addDeviceAttributes(schema); Entity device = addDevice(schema, deviceAttributes); + + // yeah deep shit, has to be here (after device) for db upgrade and column order + // because addDevice adds a property to deviceAttributes also.... + deviceAttributes.addStringProperty("volatileIdentifier"); + Entity tag = addTag(schema); Entity userDefinedActivityOverlay = addActivityDescription(schema, tag, user); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java index f6fe4f60..c221ef9f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java @@ -14,14 +14,22 @@ import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; + +import de.greenrobot.dao.query.Query; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity; import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.DeviceDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class PebblePairingActivity extends GBActivity { @@ -30,6 +38,7 @@ public class PebblePairingActivity extends GBActivity { private boolean isPairing; private boolean isLEPebble; private String macAddress; + private BluetoothDevice mBtDevice; private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() { @Override @@ -59,7 +68,7 @@ public class PebblePairingActivity extends GBActivity { if (bondState == BluetoothDevice.BOND_BONDED) { LOG.info("Bonded with " + device.getAddress()); if (!isLEPebble) { - performConnect(device); + performConnect(null); } } else if (bondState == BluetoothDevice.BOND_BONDING) { LOG.info("Bonding in progress with " + device.getAddress()); @@ -84,12 +93,34 @@ public class PebblePairingActivity extends GBActivity { macAddress = intent.getStringExtra(DeviceCoordinator.EXTRA_DEVICE_MAC_ADDRESS); if (macAddress == null) { Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show(); - startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); - finish(); + returnToPairingActivity(); return; } - startPairing(); + mBtDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); + if (mBtDevice == null) { + GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR); + returnToPairingActivity(); + return; + } + + isLEPebble = mBtDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE; + + GBDevice gbDevice = null; + if (isLEPebble) { + if (mBtDevice.getName().startsWith("Pebble-LE ") || mBtDevice.getName().startsWith("Pebble Time LE ")) { + if (!GBApplication.getPrefs().getBoolean("pebble_force_le", false)) { + GB.toast(this, "Please switch on \"Always prefer BLE\" option in Pebble settings before pairing you Pebble LE", Toast.LENGTH_LONG, GB.ERROR); + returnToPairingActivity(); + return; + } + gbDevice = getMatchingParentDeviceFromDB(mBtDevice); + if (gbDevice == null) { + return; + } + } + } + startPairing(gbDevice); } @Override @@ -104,10 +135,11 @@ public class PebblePairingActivity extends GBActivity { if (isPairing) { stopPairing(); } + super.onDestroy(); } - private void startPairing() { + private void startPairing(GBDevice gbDevice) { isPairing = true; message.setText(getString(R.string.pairing, macAddress)); @@ -116,12 +148,7 @@ public class PebblePairingActivity extends GBActivity { filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED); registerReceiver(mBondingReceiver, filter); - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); - if (device != null) { - performPair(device); - } else { - GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR); - } + performPair(gbDevice); } private void pairingFinished(boolean pairedSuccessfully) { @@ -147,31 +174,69 @@ public class PebblePairingActivity extends GBActivity { isPairing = false; } - protected void performPair(BluetoothDevice device) { - int bondState = device.getBondState(); + protected void performPair(GBDevice gbDevice) { + int bondState = mBtDevice.getBondState(); if (bondState == BluetoothDevice.BOND_BONDED) { - GB.toast(getString(R.string.pairing_already_bonded, device.getName(), device.getAddress()), Toast.LENGTH_SHORT, GB.INFO); + GB.toast(getString(R.string.pairing_already_bonded, mBtDevice.getName(), mBtDevice.getAddress()), Toast.LENGTH_SHORT, GB.INFO); return; } if (bondState == BluetoothDevice.BOND_BONDING) { - GB.toast(this, getString(R.string.pairing_in_progress, device.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO); + GB.toast(this, getString(R.string.pairing_in_progress, mBtDevice.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO); return; } - GB.toast(this, getString(R.string.pairing_creating_bond_with, device.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO); + GB.toast(this, getString(R.string.pairing_creating_bond_with, mBtDevice.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO); GBApplication.deviceService().disconnect(); // just to make sure... - if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE) { - isLEPebble = true; - performConnect(device); + + if (isLEPebble) { + performConnect(gbDevice); } else { - isLEPebble = false; - device.createBond(); + mBtDevice.createBond(); } } - private void performConnect(BluetoothDevice device) { - GBDevice gbDevice = new GBDevice(device.getAddress(), device.getName(), DeviceType.PEBBLE); + private void performConnect(GBDevice gbDevice) { + if (gbDevice == null) { + gbDevice = new GBDevice(mBtDevice.getAddress(), mBtDevice.getName(), DeviceType.PEBBLE); + } GBApplication.deviceService().connect(gbDevice); } + + private void returnToPairingActivity() { + startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); + finish(); + } + + private GBDevice getMatchingParentDeviceFromDB(BluetoothDevice btDevice) { + String expectedSuffix = btDevice.getName(); + expectedSuffix = expectedSuffix.replace("Pebble-LE ", ""); + expectedSuffix = expectedSuffix.replace("Pebble Time LE ", ""); + expectedSuffix = expectedSuffix.substring(0, 2) + ":" + expectedSuffix.substring(2); + LOG.info("will try to find a Pebble with BT address suffix " + expectedSuffix); + GBDevice gbDevice = null; + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + DeviceDao deviceDao = session.getDeviceDao(); + Query query = deviceDao.queryBuilder().where(DeviceDao.Properties.Type.eq(1), DeviceDao.Properties.Identifier.like("%" + expectedSuffix)).build(); + List devices = query.list(); + if (devices.size() == 0) { + GB.toast("Please pair your non-LE Pebble before pairing the LE one", Toast.LENGTH_SHORT, GB.INFO); + returnToPairingActivity(); + return null; + } else if (devices.size() > 1) { + GB.toast("Can not match this Pebble LE to a unique device", Toast.LENGTH_SHORT, GB.INFO); + returnToPairingActivity(); + return null; + } + DeviceHelper deviceHelper = DeviceHelper.getInstance(); + gbDevice = deviceHelper.toGBDevice(devices.get(0)); + gbDevice.setVolatileAddress(btDevice.getAddress()); + } catch (Exception e) { + GB.toast("Error retrieving devices from database", Toast.LENGTH_SHORT, GB.ERROR); + returnToPairingActivity(); + return null; + } + return gbDevice; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 7fbee9bf..c32f2a2a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -45,8 +45,10 @@ public class GBDevice implements Parcelable { private static final String DEVINFO_FW_VER = "FW: "; private static final String DEVINFO_HR_VER = "HR: "; private static final String DEVINFO_ADDR = "ADDR: "; + private static final String DEVINFO_ADDR2 = "ADDR2: "; private String mName; private final String mAddress; + private String mVolatileAddress; private final DeviceType mDeviceType; private String mFirmwareVersion; private String mFirmwareVersion2; @@ -60,7 +62,12 @@ public class GBDevice implements Parcelable { private List mDeviceInfos; public GBDevice(String address, String name, DeviceType deviceType) { + this(address, null, name, deviceType); + } + + public GBDevice(String address, String address2, String name, DeviceType deviceType) { mAddress = address; + mVolatileAddress = address2; mName = (name != null) ? name : mAddress; mDeviceType = deviceType; validate(); @@ -69,6 +76,7 @@ public class GBDevice implements Parcelable { private GBDevice(Parcel in) { mName = in.readString(); mAddress = in.readString(); + mVolatileAddress = in.readString(); mDeviceType = DeviceType.values()[in.readInt()]; mFirmwareVersion = in.readString(); mFirmwareVersion2 = in.readString(); @@ -88,6 +96,7 @@ public class GBDevice implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mName); dest.writeString(mAddress); + dest.writeString(mVolatileAddress); dest.writeInt(mDeviceType.ordinal()); dest.writeString(mFirmwareVersion); dest.writeString(mFirmwareVersion2); @@ -123,6 +132,10 @@ public class GBDevice implements Parcelable { return mAddress; } + public String getVolatileAddress() { + return mVolatileAddress; + } + public String getFirmwareVersion() { return mFirmwareVersion; } @@ -142,6 +155,10 @@ public class GBDevice implements Parcelable { mFirmwareVersion2 = firmwareVersion2; } + public void setVolatileAddress(String volatileAddress) { + mVolatileAddress = volatileAddress; + } + /** * Returns the specific model/hardware revision of this device. * This information is not always available, typically only when the device is initialized @@ -416,6 +433,9 @@ public class GBDevice implements Parcelable { if (mAddress != null) { result.add(new GenericItem(DEVINFO_ADDR, mAddress)); } + if (mVolatileAddress != null) { + result.add(new GenericItem(DEVINFO_ADDR2, mVolatileAddress)); + } Collections.sort(result); return result; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index ef36a80b..7473ec79 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -174,8 +174,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere DaoSession session = dbHandler.getDaoSession(); if (DBHelper.findDevice(device, session) == null) { DBHelper dbHelper = new DBHelper(context); + DBHelper.getDevice(device, session); // implicitly creates it :P if (dbHelper.getOldActivityDatabaseHandler() != null) { - DBHelper.getDevice(device, session); // implicitly creates it :P Intent startIntent = new Intent(context, OnboardingActivity.class); startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startIntent.putExtra(GBDevice.EXTRA_DEVICE, device); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 01b43168..7f459a4d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -64,7 +64,7 @@ class PebbleIoThread extends GBDeviceIoThread { public static final String PEBBLEKIT_ACTION_APP_START = "com.getpebble.action.app.START"; public static final String PEBBLEKIT_ACTION_APP_STOP = "com.getpebble.action.app.STOP"; - final Prefs prefs = GBApplication.getPrefs(); + private final Prefs prefs = GBApplication.getPrefs(); private final PebbleProtocol mPebbleProtocol; private final PebbleSupport mPebbleSupport; @@ -174,27 +174,31 @@ class PebbleIoThread extends GBDeviceIoThread { } @Override - protected boolean connect(String btDeviceAddress) { + protected boolean connect() { + String deviceAddress = gbDevice.getAddress(); GBDevice.State originalState = gbDevice.getState(); gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.sendDeviceUpdateIntent(getContext()); try { // contains only one ":"? then it is addr:port - int firstColon = btDeviceAddress.indexOf(":"); - if (firstColon == btDeviceAddress.lastIndexOf(":")) { + int firstColon = deviceAddress.indexOf(":"); + if (firstColon == deviceAddress.lastIndexOf(":")) { mIsTCP = true; - InetAddress serverAddr = InetAddress.getByName(btDeviceAddress.substring(0, firstColon)); - mTCPSocket = new Socket(serverAddr, Integer.parseInt(btDeviceAddress.substring(firstColon + 1))); + InetAddress serverAddr = InetAddress.getByName(deviceAddress.substring(0, firstColon)); + mTCPSocket = new Socket(serverAddr, Integer.parseInt(deviceAddress.substring(firstColon + 1))); mInStream = mTCPSocket.getInputStream(); mOutStream = mTCPSocket.getOutputStream(); } else { mIsTCP = false; - BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(btDeviceAddress); + if (gbDevice.getVolatileAddress() != null && prefs.getBoolean("pebble_force_le", false)) { + deviceAddress = gbDevice.getVolatileAddress(); + } + BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(deviceAddress); if (btDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE) { LOG.info("Ok this seems to be a LE Pebble, try LE Support, trouble ahead!"); mInStream = new PipedInputStream(); mOutStream = new PipedOutputStream(); - mPebbleLESupport = new PebbleLESupport(this.getContext(),btDeviceAddress,(PipedInputStream)mInStream,(PipedOutputStream)mOutStream); // secret branch :P + mPebbleLESupport = new PebbleLESupport(this.getContext(), btDevice, (PipedInputStream) mInStream, (PipedOutputStream) mOutStream); } else { ParcelUuid uuids[] = btDevice.getUuids(); if (uuids == null) { @@ -232,7 +236,7 @@ class PebbleIoThread extends GBDeviceIoThread { @Override public void run() { - mIsConnected = connect(gbDevice.getAddress()); + mIsConnected = connect(); if (!mIsConnected) { if (GBApplication.getGBPrefs().getAutoReconnect()) { gbDevice.setState(GBDevice.State.WAITING_FOR_RECONNECT); @@ -388,7 +392,7 @@ class PebbleIoThread extends GBDeviceIoThread { int delaySeconds = 1; while (reconnectAttempts-- > 0 && !mQuit && !mIsConnected) { LOG.info("Trying to reconnect (attempts left " + reconnectAttempts + ")"); - mIsConnected = connect(gbDevice.getAddress()); + mIsConnected = connect(); if (!mIsConnected) { try { Thread.sleep(delaySeconds * 1000); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 7aacff68..9254cdcb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -1,8 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothManager; import android.content.Context; import org.slf4j.Logger; @@ -22,8 +20,8 @@ public class PebbleLESupport { private PipedOutputStream mPipedOutputStream; private int mMTU = 20; - public PebbleLESupport(Context context, final String btDeviceAddress, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) { - + public PebbleLESupport(Context context, final BluetoothDevice btDevice, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) { + mBtDevice = btDevice; mPipedInputStream = new PipedInputStream(); mPipedOutputStream = new PipedOutputStream(); try { @@ -33,9 +31,6 @@ public class PebbleLESupport { LOG.warn("could not connect input stream"); } - BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); - BluetoothAdapter adapter = manager.getAdapter(); - mBtDevice = adapter.getRemoteDevice(btDeviceAddress); mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice); mPebbleGATTServer.initialize(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java index 31be6f02..a61d6a58 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java @@ -21,7 +21,7 @@ public abstract class GBDeviceIoThread extends Thread { return gbDevice; } - protected boolean connect(String btDeviceAddress) { + protected boolean connect() { return false; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index df4a76f8..b16445a8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -192,7 +192,7 @@ public class DeviceHelper { * @param dbDevice * @return */ - private GBDevice toGBDevice(Device dbDevice) { + public GBDevice toGBDevice(Device dbDevice) { DeviceType deviceType = DeviceType.fromKey(dbDevice.getType()); GBDevice gbDevice = new GBDevice(dbDevice.getIdentifier(), dbDevice.getName(), deviceType); List deviceAttributesList = dbDevice.getDeviceAttributesList(); @@ -201,6 +201,7 @@ public class DeviceHelper { DeviceAttributes attrs = deviceAttributesList.get(0); gbDevice.setFirmwareVersion(attrs.getFirmwareVersion1()); gbDevice.setFirmwareVersion2(attrs.getFirmwareVersion2()); + gbDevice.setVolatileAddress(attrs.getVolatileIdentifier()); } return gbDevice; @@ -211,6 +212,9 @@ public class DeviceHelper { List result = new ArrayList<>(pairedDevices.size()); DeviceHelper deviceHelper = DeviceHelper.getInstance(); for (BluetoothDevice pairedDevice : pairedDevices) { + if (pairedDevice.getName() != null && (pairedDevice.getName().startsWith("Pebble-LE ") || pairedDevice.getName().startsWith("Pebble Time LE "))) { + continue; // ignore LE Pebble (this is part of the main device now (volatileAddress) + } GBDevice device = deviceHelper.toSupportedDevice(pairedDevice); if (device != null) { result.add(device); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4d60953..1c8e5e8d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -118,6 +118,8 @@ This option forces using the latest notification protocol depending on the firmware version. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING! Enable untested features Enable features that are untested. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING! + Always prefer BLE + Use experimental Pebble LE support for all Pebbles instead of BT classic, requires paring a "Pebble LE" after non LE had been connected once Reconnection Attempts not connected diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 483f6deb..6dd3371b 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -336,6 +336,11 @@ android:key="pebble_force_untested" android:summary="@string/pref_summary_pebble_forceuntested" android:title="@string/pref_title_pebble_forceuntested" /> + Date: Sun, 27 Nov 2016 10:10:50 +0100 Subject: [PATCH 077/147] Pebble: try harder to get LE address into the database, does not work --- .../nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 3ec0ec6c..94773367 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -455,6 +455,7 @@ public class DBHelper { attributes.setValidFromUTC(now.getTime()); attributes.setFirmwareVersion1(gbDevice.getFirmwareVersion()); attributes.setFirmwareVersion2(gbDevice.getFirmwareVersion2()); + attributes.setVolatileIdentifier(gbDevice.getVolatileAddress()); DeviceAttributesDao attributesDao = session.getDeviceAttributesDao(); attributesDao.insert(attributes); From 723ad53588d0f8fea5f8da92cab063c06f6e542e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Nov 2016 10:32:56 +0100 Subject: [PATCH 078/147] Call getDevice always when connected This fixes a longstanding bug where device attributes where ONLY updated when fetching activity data or when pairing a new device --- .../service/DeviceCommunicationService.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 7473ec79..24388615 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -169,12 +169,13 @@ public class DeviceCommunicationService extends Service implements SharedPrefere boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized()); setReceiversEnableState(enableReceivers); GB.updateNotification(mGBDevice.getName() + " " + mGBDevice.getStateString(), mGBDevice.isInitialized(), context); - if (device.isInitialized() && (device.getType() != DeviceType.VIBRATISSIMO)) { + + if (device.isInitialized()) { try (DBHandler dbHandler = GBApplication.acquireDB()) { DaoSession session = dbHandler.getDaoSession(); - if (DBHelper.findDevice(device, session) == null) { - DBHelper dbHelper = new DBHelper(context); - DBHelper.getDevice(device, session); // implicitly creates it :P + DBHelper dbHelper = new DBHelper(context); + DBHelper.getDevice(device, session); // implicitly creates it :P + if (DBHelper.findDevice(device, session) == null && (device.getType() != DeviceType.VIBRATISSIMO)) { if (dbHelper.getOldActivityDatabaseHandler() != null) { Intent startIntent = new Intent(context, OnboardingActivity.class); startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -182,7 +183,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere startActivity(startIntent); } } - } catch (Exception _ignore) { + } catch (Exception ignore) { } } } else { @@ -426,11 +427,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere break; case ACTION_SETMUSICSTATE: MusicStateSpec stateSpec = new MusicStateSpec(); - stateSpec.shuffle = intent.getByteExtra(EXTRA_MUSIC_SHUFFLE, (byte)0); - stateSpec.repeat = intent.getByteExtra(EXTRA_MUSIC_REPEAT, (byte)0); + stateSpec.shuffle = intent.getByteExtra(EXTRA_MUSIC_SHUFFLE, (byte) 0); + stateSpec.repeat = intent.getByteExtra(EXTRA_MUSIC_REPEAT, (byte) 0); stateSpec.position = intent.getIntExtra(EXTRA_MUSIC_POSITION, 0); stateSpec.playRate = intent.getIntExtra(EXTRA_MUSIC_RATE, 0); - stateSpec.state = intent.getByteExtra(EXTRA_MUSIC_STATE, (byte)0); + stateSpec.state = intent.getByteExtra(EXTRA_MUSIC_STATE, (byte) 0); mDeviceSupport.onSetMusicState(stateSpec); break; case ACTION_REQUEST_APPINFO: From 16b4bfd0e7481e735711a1f379be1ae5b3b2f9bd Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Nov 2016 10:38:16 +0100 Subject: [PATCH 079/147] Pebble LE: also return to control center if successfully initialized --- .../gadgetbridge/devices/pebble/PebblePairingActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java index c221ef9f..2cf3b545 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java @@ -46,7 +46,7 @@ public class PebblePairingActivity extends GBActivity { if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) { GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE); LOG.debug("pairing activity: device changed: " + device); - if (macAddress.equals(device.getAddress())) { + if (macAddress.equals(device.getAddress()) || macAddress.equals(device.getVolatileAddress())) { if (device.isInitialized()) { pairingFinished(true); } else if (device.isConnecting() || device.isInitializing()) { From fa8df9f552265e207b500b91cf9658f53d9c5090 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Nov 2016 10:52:43 +0100 Subject: [PATCH 080/147] add missing migration script --- .../schema/GadgetbridgeUpdate_15.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java new file mode 100644 index 00000000..dd2ae146 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java @@ -0,0 +1,27 @@ +package nodomain.freeyourgadget.gadgetbridge.database.schema; + +import android.database.sqlite.SQLiteDatabase; + +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; +import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributesDao; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySampleDao; + +/* + * adds heart rate column to health table + */ + +public class GadgetbridgeUpdate_15 implements DBUpdateScript { + @Override + public void upgradeSchema(SQLiteDatabase db) { + if (!DBHelper.existsColumn(DeviceAttributesDao.TABLENAME, DeviceAttributesDao.Properties.VolatileIdentifier.columnName, db)) { + String ADD_COLUMN_VOLATILE_IDENTIFIER = "ALTER TABLE " + DeviceAttributesDao.TABLENAME + " ADD COLUMN " + + DeviceAttributesDao.Properties.VolatileIdentifier.columnName + " TEXT;"; + db.execSQL(ADD_COLUMN_VOLATILE_IDENTIFIER); + } + } + + @Override + public void downgradeSchema(SQLiteDatabase db) { + } +} From 84327a5b866aee745d44b3c565b7a4735d2f4604 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 27 Nov 2016 11:31:03 +0100 Subject: [PATCH 081/147] Pebble: use also the device address as seed to store app configuration This is not needed as long as one GB instance is used to manage a single pebble device, if multiple devices are managed the stored watchapp configuration could be messed up. --- .../app_config/js/gadgetbridge_boilerplate.js | 3 +-- .../activities/ExternalPebbleJSActivity.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index 0c51b1dd..50bab12d 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -1,5 +1,5 @@ if (window.Storage){ - var prefix = GBjs.getAppUUID(); + var prefix = GBjs.getAppLocalstoragePrefix(); GBjs.gbLog("redefining local storage with prefix: " + prefix); Storage.prototype.setItem = (function(key, value) { @@ -85,7 +85,6 @@ function gbPebble() { } self.evaluate = function(name, args) { - console.log(args); if (!self.events.hasOwnProperty(name)) return; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index d26691b7..4904a181 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -24,7 +24,10 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.Scanner; import java.util.UUID; @@ -274,6 +277,25 @@ public class ExternalPebbleJSActivity extends GBActivity { return appUuid.toString(); } + @JavascriptInterface + public String getAppLocalstoragePrefix() { + String prefix = mGBDevice.getAddress() + appUuid.toString(); + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + byte[] bytes = prefix.getBytes("UTF-8"); + digest.update(bytes, 0, bytes.length); + bytes = digest.digest(); + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + sb.append(String.format("%02X", bytes[i])); + } + return sb.toString().toLowerCase(); + } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { + e.printStackTrace(); + return prefix; + } + } + @JavascriptInterface public String getWatchToken() { //specification says: A string that is is guaranteed to be identical for each Pebble device for the same app across different mobile devices. The token is unique to your app and cannot be used to track Pebble devices across applications. see https://developer.pebble.com/docs/js/Pebble/ From 109a032f1e13c4fec5e1b3bb2259f389e38da44c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Nov 2016 22:45:57 +0100 Subject: [PATCH 082/147] Pebble: fix Pebble LE address not being propery added to device attributes --- .../freeyourgadget/gadgetbridge/database/DBHelper.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 94773367..7d87e9d3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -345,6 +345,9 @@ public class DBHelper { if (!Objects.equals(attr.getFirmwareVersion2(), gbDevice.getFirmwareVersion2())) { return false; } + if (!Objects.equals(attr.getVolatileIdentifier(), gbDevice.getVolatileAddress())) { + return false; + } return true; } From 2677dad8732dbe8cb44397217d70d3fe299a539f Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Nov 2016 22:50:21 +0100 Subject: [PATCH 083/147] Fix a recent regression that caused the database migration dialog to never pop up --- .../service/DeviceCommunicationService.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 24388615..18bfb2b7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -173,9 +173,13 @@ public class DeviceCommunicationService extends Service implements SharedPrefere if (device.isInitialized()) { try (DBHandler dbHandler = GBApplication.acquireDB()) { DaoSession session = dbHandler.getDaoSession(); - DBHelper dbHelper = new DBHelper(context); - DBHelper.getDevice(device, session); // implicitly creates it :P - if (DBHelper.findDevice(device, session) == null && (device.getType() != DeviceType.VIBRATISSIMO)) { + boolean askForDBMigration = false; + if (DBHelper.findDevice(device, session) == null && device.getType() != DeviceType.VIBRATISSIMO) { + askForDBMigration = true; + } + DBHelper.getDevice(device, session); // implicitly creates the device in database if not present, and updates device attributes + if (askForDBMigration) { + DBHelper dbHelper = new DBHelper(context); if (dbHelper.getOldActivityDatabaseHandler() != null) { Intent startIntent = new Intent(context, OnboardingActivity.class); startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); From 011646b097166fead9df57d38465c769621e0a74 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 29 Nov 2016 22:22:54 +0100 Subject: [PATCH 084/147] Fix activity data on Mi Band 1 #440 --- .../gadgetbridge/devices/miband/MiBand2Coordinator.java | 8 ++++++++ .../gadgetbridge/devices/miband/MiBandCoordinator.java | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 12c945e0..5d40bfc1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -18,6 +18,9 @@ import java.util.Collections; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; @@ -78,6 +81,11 @@ public class MiBand2Coordinator extends MiBandCoordinator { return true; } + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return new MiBand2SampleProvider(device, session); + } + @Override public InstallHandler findInstallHandler(Uri uri, Context context) { return null; // not supported at the moment diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index 3597706c..abff3bc1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -100,7 +100,7 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { @Override public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { - return new MiBand2SampleProvider(device, session); + return new MiBandSampleProvider(device, session); } @Override From 1862b59dad5ca2dd0fd4f4ca49c3f170eb0a6adc Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 29 Nov 2016 22:36:05 +0100 Subject: [PATCH 085/147] bump version and add changelog --- CHANGELOG.md | 4 ++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2382d1ed..ed4fb82f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ###Changelog +####Version 0.14.3 +* Pebble: Experimental support for pairing and using all Pebble models via BLE +* Mi Band: Fix regressing causing wrong activity data + ####Version 0.14.2 * Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices diff --git a/app/build.gradle b/app/build.gradle index 7d3500a7..97a7b056 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.14.2" - versionCode 74 + versionName "0.14.3" + versionCode 75 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index b7c3d3a4..98f9cb75 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,9 @@ + + Pebble: Experimental support for pairing and using all Pebble models via BLE + Mi Band: Fix regressing causing wrong activity data + Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices From 49acde118d1a96208937c9ac6ee6a8cb9aa8551b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 29 Nov 2016 22:34:10 +0100 Subject: [PATCH 086/147] Do not log heartrate separately during miband 1s sync --- .../devices/miband/operations/FetchActivityOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index 7809f67c..ab4fd5d6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -331,7 +331,7 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { steps = activityStruct.activityDataHolder[i + 2]; if (hasExtendedActivityData) { heartrate = activityStruct.activityDataHolder[i + 3]; - LOG.debug("heartrate received: " + (heartrate & 0xff)); +// LOG.debug("heartrate received: " + (heartrate & 0xff)); } MiBandActivitySample sample = getSupport().createActivitySample(device, user, timestampInSeconds, provider); From 09ff95eb343bbe38e07b52cd3fa38d82037ead71 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 29 Nov 2016 23:22:36 +0100 Subject: [PATCH 087/147] Support for continuous hr readings (live activity) #323 --- .../profiles/heartrate/HeartRateProfile.java | 8 +- .../devices/miband/MiBand2Support.java | 140 +++++++++++++----- 2 files changed, 106 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java index f5495124..ec195fce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java @@ -44,12 +44,6 @@ public class HeartRateProfile extends Abstr } - // TODO: I didn't find anything in the spec to request heart rate readings, so probably this - // should be done in a device specific way. - public void requestHeartRateMeasurement(TransactionBuilder builder) { - writeToControlPoint(new byte[] { 0x15, 0x02, 0x01}, builder); - } - @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { if (GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { @@ -61,7 +55,7 @@ public class HeartRateProfile extends Abstr format = BluetoothGattCharacteristic.FORMAT_UINT8; } final int heartRate = characteristic.getIntValue(format, 1); - GB.toast(getContext(), "Heart rate: " + heartRate, Toast.LENGTH_LONG, GB.INFO); + LOG.info("Heart rate: " + heartRate, Toast.LENGTH_LONG, GB.INFO); } return false; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index 963a34b5..62016c4d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -15,7 +15,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -26,19 +25,28 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; +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; +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; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; @@ -109,6 +117,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + private RealtimeSamplesSupport realtimeSamplesSupport; public MiBand2Support() { super(LOG); @@ -124,7 +133,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { deviceInfoProfile = new DeviceInfoProfile<>(this); addSupportedProfile(deviceInfoProfile); - heartRateProfile = new HeartRateProfile(this); + heartRateProfile = new HeartRateProfile<>(this); addSupportedProfile(heartRateProfile); LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); @@ -173,12 +182,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return builder; } -// private MiBand2Support maybeAuth(TransactionBuilder builder) { -// builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0), new byte[] {0x20, 0x00}); -// builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0), new byte[] {0x03,0x00,(byte)0x8e,(byte)0xce,0x5a,0x09,(byte)0xb3,(byte)0xd8,0x55,0x57,0x10,0x2a,(byte)0xed,0x7d,0x6b,0x78,(byte)0xc5,(byte)0xd2}); -// return this; -// } - public byte[] getTimeBytes(Calendar calendar) { byte[] bytes = BLETypeConversions.shortCalendarToRawBytes(calendar, true); byte[] tail = new byte[] { 0, BLETypeConversions.mapTimeZone(calendar.getTimeZone()) }; // 0 = adjust reason bitflags? or DST offset?? , timezone @@ -678,11 +681,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void onHeartRateTest() { try { TransactionBuilder builder = performInitialized("HeartRateTest"); - heartRateProfile.requestHeartRateMeasurement(builder); -// profile.resetEnergyExpended(builder); -// 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(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.queue(getQueue()); } catch (IOException ex) { LOG.error("Unable to read HearRate with MI2", ex); @@ -691,18 +692,19 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onEnableRealtimeHeartRateMeasurement(boolean enable) { -// try { -// TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); -// if (enable) { -// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); -// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); -// } else { -// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); -// } -// builder.queue(getQueue()); -// } catch (IOException ex) { -// LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); -// } + 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); + } else { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); + } + builder.queue(getQueue()); + enableRealtimeSamplesTimer(enable); + } catch (IOException ex) { + LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); + } } @Override @@ -745,6 +747,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // 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); // } @@ -922,7 +925,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void logHeartrate(byte[] value, int status) { if (status == BluetoothGatt.GATT_SUCCESS && value != null) { LOG.info("Got heartrate:"); - if (value.length == 2 && value[0] == 6) { + if (value.length == 2 && value[0] == 0) { int hrValue = (value[1] & 0xff); GB.toast(getContext(), "Heart Rate measured: " + hrValue, Toast.LENGTH_LONG, GB.INFO); } @@ -932,15 +935,17 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } private void handleHeartrate(byte[] value) { - if (value.length == 2 && value[0] == 6) { + if (value.length == 2 && value[0] == 0) { int hrValue = (value[1] & 0xff); if (LOG.isDebugEnabled()) { LOG.debug("heart rate: " + hrValue); } - Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT) - .putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, hrValue) - .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + RealtimeSamplesSupport realtimeSamplesSupport = getRealtimeSamplesSupport(); + realtimeSamplesSupport.setHeartrateBpm(hrValue); + if (!realtimeSamplesSupport.isRunning()) { + // single shot measurement, manually invoke storage and result publishing + realtimeSamplesSupport.triggerCurrentSample(); + } } } @@ -949,10 +954,75 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { if (LOG.isDebugEnabled()) { LOG.debug("realtime steps: " + steps); } - Intent intent = new Intent(DeviceService.ACTION_REALTIME_STEPS) - .putExtra(DeviceService.EXTRA_REALTIME_STEPS, steps) - .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + getRealtimeSamplesSupport().setSteps(steps); + } + + private void enableRealtimeSamplesTimer(boolean enable) { + if (enable) { + getRealtimeSamplesSupport().start(); + } else { + if (realtimeSamplesSupport != null) { + realtimeSamplesSupport.stop(); + } + } + } + + public MiBandActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { + MiBandActivitySample sample = new MiBandActivitySample(); + sample.setDevice(device); + sample.setUser(user); + sample.setTimestamp(timestampInSeconds); + sample.setProvider(provider); + + return sample; + } + + private RealtimeSamplesSupport getRealtimeSamplesSupport() { + if (realtimeSamplesSupport == null) { + realtimeSamplesSupport = new RealtimeSamplesSupport(1000, 1000) { + @Override + public void doCurrentSample() { + + try (DBHandler handler = GBApplication.acquireDB()) { + DaoSession session = handler.getDaoSession(); + + Device device = DBHelper.getDevice(getDevice(), session); + User user = DBHelper.getUser(session); + int ts = (int) (System.currentTimeMillis() / 1000); + MiBand2SampleProvider provider = new MiBand2SampleProvider(gbDevice, session); + MiBandActivitySample sample = createActivitySample(device, user, ts, provider); + sample.setHeartRate(getHeartrateBpm()); + sample.setSteps(getSteps()); + sample.setRawIntensity(ActivitySample.NOT_MEASURED); + sample.setRawKind(MiBand2SampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? + + // TODO: remove this once fully ported to REALTIME_SAMPLES + if (sample.getSteps() != ActivitySample.NOT_MEASURED) { + Intent intent = new Intent(DeviceService.ACTION_REALTIME_STEPS) + .putExtra(DeviceService.EXTRA_REALTIME_STEPS, sample.getSteps()) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + if (sample.getHeartRate() != ActivitySample.NOT_MEASURED) { + Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT) + .putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, sample.getHeartRate()) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + +// Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) +// .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample); +// LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + + LOG.debug("Storing realtime sample: " + sample); + provider.addGBActivitySample(sample); + } catch (Exception e) { + LOG.warn("Unable to acquire db for saving realtime samples", e); + } + } + }; + } + return realtimeSamplesSupport; } /** From 092012ab31a62cae3234780feb3c4df3e6d941b1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 29 Nov 2016 23:28:15 +0100 Subject: [PATCH 088/147] Changelog for 0.14.3 --- CHANGELOG.md | 3 ++- app/src/main/res/xml/changelog_master.xml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4fb82f..2804493c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ####Version 0.14.3 * Pebble: Experimental support for pairing and using all Pebble models via BLE -* Mi Band: Fix regressing causing wrong activity data +* Mi Band 1: Fix regression causing display of wrong activity data (#440) +* Mi Band 2: Support for continuous heart rate measurements in live activity view ####Version 0.14.2 * Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 98f9cb75..395662b6 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,7 +2,8 @@ Pebble: Experimental support for pairing and using all Pebble models via BLE - Mi Band: Fix regressing causing wrong activity data + Mi Band 1: Fix regression causing display of wrong activity data + Mi Band 2: Support for continuous heart rate measurements in live activity view Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices From 95ec1fb44c17c65ba1f5a03b8e97e9d532912a66 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 30 Nov 2016 19:51:22 +0100 Subject: [PATCH 089/147] Pebble LE/Pebble 2: Improve reconnect --- .../devices/pebble/ble/PebbleLESupport.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 9254cdcb..6c351bb1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -7,6 +7,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.io.InterruptedIOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; @@ -69,13 +70,17 @@ public class PebbleLESupport { synchronized private void destroyPipedInputReader() { if (mPipeReader != null) { - mPipeReader.quit(); mPipeReader.interrupt(); try { mPipeReader.join(); } catch (InterruptedException e) { LOG.error(e.getMessage()); } + try { + mPipedOutputStream.close(); + } catch (IOException ignore) { + } + mPipeReader = null; } } @@ -86,13 +91,12 @@ public class PebbleLESupport { private class PipeReader extends Thread { int mmSequence = 0; - private boolean mQuit = false; @Override public void run() { byte[] buf = new byte[8192]; int bytesRead; - while (!mQuit) { + while (true) { try { // this code is very similar to iothread, that is bad // because we are the ones who prepared the buffer, there should be no @@ -122,26 +126,22 @@ public class PebbleLESupport { payloadToSend -= chunkSize; } - try { - Thread.sleep(500); // FIXME ugly wait 0.5s after each pebble package send to the pebble (we do not wait for the GATT chunks) - } catch (InterruptedException ignore) { - } - - } catch (IOException e) { - LOG.warn("IO exception"); - mQuit = true; + Thread.sleep(500); // FIXME ugly wait 0.5s after each pebble package send to the pebble (we do not wait for the GATT chunks) + } catch (InterruptedIOException | InterruptedException e) { + Thread.currentThread().interrupt(); break; + } catch (IOException ignore) { } } - try { - mPipedOutputStream.close(); - mPipedInputStream.close(); - } catch (IOException ignore) { - } } - void quit() { - mQuit = true; + @Override + public void interrupt() { + super.interrupt(); + try { + mPipedInputStream.close(); + } catch (IOException ignore) { + } } } From b878fa5eda7b0fdc9496e74be2bd534be32da1e0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 30 Nov 2016 22:28:34 +0100 Subject: [PATCH 090/147] Pebble LE/Pebble 2: Fix reconnect not working when first attempt fails --- .../service/devices/pebble/ble/PebbleLESupport.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 6c351bb1..97aaaad5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -76,13 +76,12 @@ public class PebbleLESupport { } catch (InterruptedException e) { LOG.error(e.getMessage()); } - try { - mPipedOutputStream.close(); - } catch (IOException ignore) { - } - mPipeReader = null; } + try { + mPipedInputStream.close(); + } catch (IOException ignore) { + } } void setMTU(int mtu) { From 74c20f3a825910fcaee8f1103dd1e12ffe1d9547 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 30 Nov 2016 23:56:58 +0100 Subject: [PATCH 091/147] Pebble 2/LE: More fun with reconnect --- .../service/devices/pebble/PebbleIoThread.java | 2 +- .../service/devices/pebble/ble/PebbleLESupport.java | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 7f459a4d..fe8868a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -473,7 +473,7 @@ class PebbleIoThread extends GBDeviceIoThread { mOutStream.flush(); } } catch (IOException e) { - LOG.error("Error writing.", e); + LOG.error("Error writing.", e.getMessage()); } try { Thread.sleep(100); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 97aaaad5..84acd650 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -82,6 +82,10 @@ public class PebbleLESupport { mPipedInputStream.close(); } catch (IOException ignore) { } + try { + mPipedOutputStream.close(); + } catch (IOException ignore) { + } } void setMTU(int mtu) { @@ -126,18 +130,20 @@ public class PebbleLESupport { } Thread.sleep(500); // FIXME ugly wait 0.5s after each pebble package send to the pebble (we do not wait for the GATT chunks) - } catch (InterruptedIOException | InterruptedException e) { + } catch (IOException | InterruptedException e) { + LOG.info(e.getMessage()); Thread.currentThread().interrupt(); break; - } catch (IOException ignore) { } } + LOG.info("Pipereader thread shut down"); } @Override public void interrupt() { super.interrupt(); try { + LOG.info("closing piped inputstream"); mPipedInputStream.close(); } catch (IOException ignore) { } From 2993bb6b5ce4821d1783f053c6aa2731724402f1 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 1 Dec 2016 00:20:21 +0100 Subject: [PATCH 092/147] Pebble 2/LE: fix potential NPE --- .../service/devices/pebble/ble/PebbleGATTServer.java | 3 +++ .../service/devices/pebble/ble/PebbleLESupport.java | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java index b4167d41..9e560b85 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -40,6 +40,9 @@ class PebbleGATTServer extends BluetoothGattServerCallback { BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this); + if (mBluetoothGattServer == null) { + return false; + } BluetoothGattService pebbleGATTService = new BluetoothGattService(SERVER_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY); pebbleGATTService.addCharacteristic(new BluetoothGattCharacteristic(READ_CHARACTERISTICS, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 84acd650..3016dc5c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -33,10 +33,10 @@ public class PebbleLESupport { } mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice); - mPebbleGATTServer.initialize(); - - mPebbleGATTClient = new PebbleGATTClient(this, context, mBtDevice); - mPebbleGATTClient.initialize(); + if (mPebbleGATTServer.initialize()) { + mPebbleGATTClient = new PebbleGATTClient(this, context, mBtDevice); + mPebbleGATTClient.initialize(); + } } void writeToPipedOutputStream(byte[] value, int offset, int count) { From f0789cc147d7311e69542d7f8a2fe783f9ba34ca Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 1 Dec 2016 00:28:23 +0100 Subject: [PATCH 093/147] Pebble 2/LE: fix another bug when reconnecting --- .../devices/pebble/ble/PebbleLESupport.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 3016dc5c..0b9ac531 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -57,6 +57,14 @@ public class PebbleLESupport { mPebbleGATTClient.close(); mPebbleGATTClient = null; } + try { + mPipedInputStream.close(); + } catch (IOException ignore) { + } + try { + mPipedOutputStream.close(); + } catch (IOException ignore) { + } } synchronized void createPipedInputReader() { @@ -78,14 +86,6 @@ public class PebbleLESupport { } mPipeReader = null; } - try { - mPipedInputStream.close(); - } catch (IOException ignore) { - } - try { - mPipedOutputStream.close(); - } catch (IOException ignore) { - } } void setMTU(int mtu) { From 2846b9cc154d3abe5a2739b018a6cce464d3a75c Mon Sep 17 00:00:00 2001 From: Uwe Hermann Date: Thu, 1 Dec 2016 16:36:26 +0100 Subject: [PATCH 094/147] README.md: Random typo and consistency fixes. --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4fdda233..62bdcc0c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Gadgetbridge ============ -Gadgetbridge is an Android (4.4+) Application which will allow you to use your +Gadgetbridge is an Android (4.4+) application which will allow you to use your Pebble or Mi Band without the vendor's closed source application and without the need to create an account and transmit any of your data to the vendor's servers. @@ -29,7 +29,7 @@ need to create an account and transmit any of your data to the vendor's servers. * K-9 Mail notification support * Support for generic notifications (above filtered out) * Support for up to 16 predefined replies for SMS and Android Wear compatible notifications (experimental, tested with Signal) -* Dismiss individial notifications, mute or open corresponding app on phone from the action menu (generic notifications) +* Dismiss individual notifications, mute or open corresponding app on phone from the action menu (generic notifications) * Dismiss all notifications from the action menu (non-generic notifications) * Music playback info (artist, album, track) * Music control: play/pause, next track, previous track, volume up, volume down @@ -48,7 +48,7 @@ need to create an account and transmit any of your data to the vendor's servers. ## Getting Started (Pebble) -1. Pair your Pebble through the Android's Bluetooth Settings or Gadgetbridge. Pebble 2 MUST be paried though Gadgetbridge (tap on the + in Control Center) +1. Pair your Pebble through the Android's Bluetooth Settings or Gadgetbridge. Pebble 2 MUST be paired though Gadgetbridge (tap on the + in Control Center) 2. Start Gadgetbridge, tap on the device you want to connect to 3. To test, choose "Debug" from the menu and play around @@ -65,7 +65,7 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/ * Generic Android notifications * Synchronize the time to the Mi Band * Display firmware version and battery state -* Firmware Update +* Firmware update * Heart rate measurement on demand and during sleep * Synchronize activity data * Display sleep data (alpha) @@ -99,8 +99,8 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/ tap it quickly a few times in a row to confirm the pairing with the band. 1. Configure other notifications as desired -2. Go back to the "Gadgetbridge" Activity -3. Tap the Mi Band item to connect if you're not connected yet. +2. Go back to the "Gadgetbridge" activity +3. Tap the Mi Band item to connect if you're not connected yet 4. To test, chose "Debug" from the menu and play around Known Issues: @@ -127,10 +127,9 @@ Translations can be contributed via https://www.transifex.com/projects/p/gadgetb Feel free to open an issue on our issue tracker, but please: - do not use the issue tracker as a forum, do not ask for ETAs and read the issue conversation before posting -- use the search functionality to ensure that your questions wasn't already answered. Don't forget to check the **closed** issues as well! +- use the search functionality to ensure that your question wasn't already answered. Don't forget to check the **closed** issues as well! - remember that this is a community project, people are contributing in their free time because they like doing so: don't take the fun away! Be kind and constructive. - ## Having problems? 1. Open Gadgetbridge's settings and check the option to write log files From 82bc89f042e95fa3b8b97e2ef195f418e0014988 Mon Sep 17 00:00:00 2001 From: Uwe Hermann Date: Thu, 1 Dec 2016 17:20:10 +0100 Subject: [PATCH 095/147] CHANGELOG.md: Random typo and consistency fixes. --- CHANGELOG.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2804493c..49ff429a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ ####Version 0.13.9 * Pebble: use the last known location for setting sunrise and sunset -* Pebble: fix Health disappering forever when deactivating through app manager (and get it back for affected users) +* Pebble: fix Health disappearing forever when deactivating through app manager (and get it back for affected users) * Mi Band 2: More fixes for connection issues (#408) ####Version 0.13.8 @@ -59,7 +59,7 @@ * Possibly fix logging to file on certain devices (#406) * Mi Band 2: Possibly fix weird connection interdependency between Mi 1 and 2 (#323) * Mi Band 1S: Whitelist firmware 4.16.4.22 -* Mi Band: try application level pairing again, in ordert to support data sharing with Mi Fit (#250) +* Mi Band: try application level pairing again, in order to support data sharing with Mi Fit (#250) * Pebble: new icons and colours for certain apps * Debug-screen: added button to test "new functionality", currently live sensor data for Mi Band 1 @@ -90,7 +90,7 @@ ####Version 0.12.1 (release withdrawn) * Pebble: Fix activity data being associated with the wrong device and/or user in some cases causing them to invisible in charts -* Remove special handling for Conversations notfications since upstream dropped special pebble support +* Remove special handling for Conversations notifications since upstream dropped special pebble support ####Version 0.12.0 (release withdrawn) * NB: User action needed to migrate existing data! @@ -107,13 +107,13 @@ ####Version 0.11.1 * Various fixes (including crashes) for location settings * Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) -* Fix a rare crash when, due to bluetooth problems, when a device has no name +* Fix a rare crash when, due to Bluetooth problems, when a device has no name * Fix activity fetching getting stuck when double tapping (#333) * Mi Band: in the Device Discovery activity, do not display devices that are already paired * Mi Band: only allow automatic reconnection on disconnect when the device was previously fully connected -* Mi Band: fix a rare crash when reading data fails due to bluetooth problems +* Mi Band: fix a rare crash when reading data fails due to Bluetooth problems * Mi Band: log full activity sample to help deciphering activity kinds (#341) -* Mi Band 2: improved discovery mechanism to not rely on mac addresses (#323) +* Mi Band 2: improved discovery mechanism to not rely on MAC addresses (#323) * Charts: only display heart rate samples on devices that support that * Add more logging to detect problems with external directories (#343) @@ -126,13 +126,13 @@ ####Version 0.10.2 * Pebble: allow to manually paste configuration data for legacy configuration pages * Pebble: various improvements to the configuration page -* Pebble: Suppport FW 4.0-dp1 and Pebble2 emulator (needs recompilation of Gadgetbridge) +* Pebble: Support FW 4.0-dp1 and Pebble2 emulator (needs recompilation of Gadgetbridge) * Pebble: Fix a problem with key events when using the Pebble music player ####Version 0.10.1 * Pebble: set extended music info by dissecting notifications on Android 5.0+ -* Pebble: various other improvemnts to music playback -* Pebble: allow ignoring activity trackers indiviually (to keep the data on the pebble) +* Pebble: various other improvements to music playback +* Pebble: allow ignoring activity trackers individually (to keep the data on the pebble) * Mi Band: support for shifting the device time by N hours (for people who sleep at daytime) * Mi Band: initial and untested support for Mi Band 2 * Allow setting the application language @@ -141,12 +141,12 @@ * Pebble: option to send sunrise and sunset events to timeline * Pebble: fix problems with unknown app keys while configuring watchfaces * Mi Band: BLE connection fixes -* Fixes for enabling logging at whithout restarting Gadgetbridge +* Fixes for enabling logging at without restarting Gadgetbridge * Re-enable device paring activity on Android 6 (BLE scanning needs the location preference) * Display device address in device info ####Version 0.9.8 -* Pebble: fix more reconnnect issues +* Pebble: fix more reconnect issues * Pebble: fix deep sleep not being detected with Firmware 3.12 when using Pebble Health * Pebble: option in AppManager to delete files from cache * Pebble: enable pbw cache and watchface configuration for Firmware 2.x @@ -158,7 +158,7 @@ * Pebble: hopefully fix some reconnect issues * Mi Band: fix live activity monitoring running forever if back button pressed * Mi Band: allow low latency firmware updates, fixes update with some phones -* Mi Band: inital experimental and probably broken support for Amazfit +* Mi Band: initial experimental and probably broken support for Amazfit * Show aliases for BT Devices if they had been renamed in BT Settings * Do not show a hint about App Manager when a Mi Band is connected @@ -240,7 +240,7 @@ ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. * Pebble: Fix regression with broken active reconnect since 0.7.0 -* Pebble: Support activation and deactivation of Pebble Health. Activation uses the User details as seen above. Insigths are NOT activated. +* Pebble: Support activation and deactivation of Pebble Health. Activation uses the User details as seen above. Insights are NOT activated. Please be aware that deactivation does NOT delete the data stored on the watch (but it seems to stop the tracking), and we do not know how to switch to metric length units. ####Version 0.7.3 @@ -249,7 +249,7 @@ ####Version 0.7.2 * Pebble: Allow replying to generic notifications that contain a wearable reply action (tested with Signal) -* Pebble: Support seting up a common suffix for canned replies (defaults to " (canned reply)") +* Pebble: Support setting up a common suffix for canned replies (defaults to " (canned reply)") * Mi Band: Avoid NPEs when aborting an erroneous sync #205 * Mi Band: Fix discovery of Mi Band 1S * Add a confirmation dialog when performing a db import @@ -266,7 +266,7 @@ * Pebble: Allow installing apps compiled with SDK 2.x also on the basalt platform (Time, Time Steel) * Pebble: Fix decoding strings in appmessages from the pebble (fixes sending SMS from "Dialer for Pebble") * Pebble: Support incoming reconnections when device returns from "Airplane Mode" or "Stand-By Mode" -* Pebble: Fix crash when turning off bluetooth when connected on Android 6.0 +* Pebble: Fix crash when turning off Bluetooth when connected on Android 6.0 * Mi Band: reserve some alarm slots for alerting when upcoming events begin. NB: the band will vibrate at the start time of the event, android reminders are ignored * Mi Band: Display unique devices Names, not just "MI" * Some new and updated icons @@ -290,7 +290,7 @@ * Pebble: fix installation of pbw files on firmware 3.x when using content providers (eg. download manager) * Pebble: fix crash on firmware 3.x when pebble requests a pbw that is not in Gadgetbridge's cache + Treat Signal notifications as chat notifications -* Fix crash when contacts cannot be read on Android 6.0 (non-granted pemissions) +* Fix crash when contacts cannot be read on Android 6.0 (non-granted permissions) ####Version 0.6.7 * Pebble: Allow installation of 3.x apps on OG Pebble (FW will be released soon) @@ -324,7 +324,7 @@ * Try to prevent service being killed by disallowing backups ####Version 0.6.2 -* Mi Band: support firmare versione 1.0.10.14 (and onwards?) vibration +* Mi Band: support firmware version 1.0.10.14 (and onwards?) vibration * Mi Band: get device name from official BT SIG endpoint * Mi Band: initial support for displaying live activity data, screen stays on @@ -336,11 +336,11 @@ * Bugfix for app blacklist (some checkboxes where wrongly drawn as checked) ####Version 0.6.0 -* Pebble: WIP implementantion of PebbleKit Intents to make some 3rd party Android apps work with the Pebble (eg. Ventoo) +* Pebble: WIP implementation of PebbleKit Intents to make some 3rd party Android apps work with the Pebble (eg. Ventoo) * Pebble: Option to set reconnection attempts in settings (one attempt usually takes about 5 seconds) -* Support contolling all audio players that react to media buttons (can be chosen in settings) +* Support controlling all audio players that react to media buttons (can be chosen in settings) * Treat SMS as generic notification if set to "never" (can be blacklisted there also if desired) -* Treat Conversations messagess as chat messages, even if arrived via Pebble Intents (nice icon for Pebble FW 3.x) +* Treat Conversations messages as chat messages, even if arrived via Pebble Intents (nice icon for Pebble FW 3.x) * Allow opening firmware / app files from the download manager "app" (technically a content provider) * Mi Band: whitelisted a few firmware versions @@ -364,7 +364,7 @@ * Graphs are now using the same theme as the rest of the application * Graphs now show when the device was not worn by the user (for devices that send this information) * Remove unused settings option in charts view -* Build target is now Android SDK 23 (Marshmellow) +* Build target is now Android SDK 23 (Marshmallow) ####Version 0.5.1 * Pebble: support taking screenshot from Pebble Time @@ -377,7 +377,7 @@ * Pebble: use SMS/EMAIL icons for FW 3.x/Pebble Time * Pebble: do not throttle notifications * Support going forward/backwards in time in the activity charts -* Various small bugfixes to the App/Fw Installation Activity +* Various small bugfixes to the App/FW Installation Activity ####Version 0.4.6 * Mi Band: Fixed negative number of steps displayed (#91) @@ -392,7 +392,7 @@ ####Version 0.4.5 * Enhancement to activity graphs: new graph showing the number of steps done today and in the last week * New preference to set the desired fitness goal (number of steps to walk in one day) -* Mi Band: support for setting the fitness goal (the band will show the progress to the goal with the leds and vibrates when the goal is reached) +* Mi Band: support for setting the fitness goal (the band will show the progress to the goal with the LEDs and vibrates when the goal is reached) * Mi Band: send the wear location (left / right hand) to the device * Mi Band: support for flashing firmware from .fw files (upgrades and downgrades are possible) * Fixed crash when synchronizing activity data in the graphs activity and changing device orientation @@ -475,7 +475,7 @@ ####Version 0.2.0 * Experimental pbw installation support (watchfaces/apps) * New icons for device and app lists -* Fix for device list not refreshing when bluetooth gets turned on +* Fix for device list not refreshing when Bluetooth gets turned on * Filter out annoying low battery notifications * Fix for crash on some devices when creating a debug notification * Lots of internal changes preparing multi device support @@ -498,8 +498,8 @@ * Remove quit button from the service notification, put a quit item in the context menu instead ####Version 0.1.2 -* Added option to start Gadgetbridge and connect automatically when bluetooth is turned on -* stop service if bluetooth is turned off +* Added option to start Gadgetbridge and connect automatically when Bluetooth is turned on +* stop service if Bluetooth is turned off * try to reconnect if connection was lost ####Version 0.1.1 From 1352575f12ae33ec5ba34213c57cdf6d8b464fe6 Mon Sep 17 00:00:00 2001 From: Uwe Hermann Date: Thu, 1 Dec 2016 17:22:29 +0100 Subject: [PATCH 096/147] changelog_master.xml: Random typo and consistency fixes. --- app/src/main/res/xml/changelog_master.xml | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 395662b6..2a0c90f3 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -27,7 +27,7 @@ Pebble: use the last known location for setting sunrise and sunset - Pebble: fix Health disappering forever when deactivating through app manager (and get it back for affected users) + Pebble: fix Health disappearing forever when deactivating through app manager (and get it back for affected users) Mi Band 2: More fixes for connection issues (#408) @@ -59,7 +59,7 @@ Possibly fix logging to file on certain devices) Mi Band 2: Possibly fix weird connection interdependency between Mi 1 and 2 Mi Band 1S: Whitelist firmware 4.16.4.22 - Mi Band: try application level pairing again, in ordert to support data sharing with Mi Fit + Mi Band: try application level pairing again, in order to support data sharing with Mi Fit Pebble: new icons and colours for certain apps Debug-screen: added button to test "new functionality", currently live sensor data for Mi Band 1 @@ -90,7 +90,7 @@ Pebble: fix activity data being associated with the wrong device and/or user in some cases causing them to invisible in charts - Remove special handling for Conversations notfications since upstream dropped special pebble support + Remove special handling for Conversations notifications since upstream dropped special pebble support NB: User action needed to migrate existing data! @@ -107,13 +107,13 @@ Various fixes (including crashes) for location settings Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) - Fix a rare crash when, due to bluetooth problems, when a device has no name + Fix a rare crash when, due to Bluetooth problems, when a device has no name Fix activity fetching getting stuck when double tapping (#333) Mi Band: in the Device Discovery activity, do not display devices that are already paired Mi Band: only allow automatic reconnection on disconnect when the device was previously fully connected - Mi Band: fix a rare crash when reading data fails due to bluetooth problems + Mi Band: fix a rare crash when reading data fails due to Bluetooth problems Mi Band: log full activity sample to help deciphering activity kinds (#341) - Mi Band 2: improved discovery mechanism to not rely on mac addresses (#323) + Mi Band 2: improved discovery mechanism to not rely on MAC addresses (#323) Charts: only display heart rate samples on devices that support that Add more logging to detect problems with external directories (#343) @@ -126,13 +126,13 @@ Pebble: allow to manually paste configuration data for legacy configuration pages Pebble: various improvements to the configuration page - Pebble: Suppport FW 4.0-dp1 and Pebble2 emulator (needs recompilation of Gadgetbridge) + Pebble: Support FW 4.0-dp1 and Pebble2 emulator (needs recompilation of Gadgetbridge) Pebble: Fix a problem with key events when using the Pebble music player Pebble: set extended music info by dissecting notifications on Android 5.0+ - Pebble: various other improvemnts to music playback - Pebble: allow ignoring activity trackers indiviually (to keep the data on the pebble) + Pebble: various other improvements to music playback + Pebble: allow ignoring activity trackers individually (to keep the data on the pebble) Mi Band: support for shifting the device time by N hours (for people who sleep at daytime) Mi Band: initial and untested support for Mi Band 2 Allow setting the application language @@ -141,12 +141,12 @@ Pebble: option to send sunrise and sunset events to timeline Pebble: fix problems with unknown app keys while configuring watchfaces Mi Band: BLE connection fixes - Fixes for enabling logging at whithout restarting Gadgetbridge + Fixes for enabling logging at without restarting Gadgetbridge Re-enable device paring activity on Android 6 (BLE scanning needs the location preference) Display device address in device info - Pebble: fix more reconnnect issues + Pebble: fix more reconnect issues Pebble: fix deep sleep not being detected with Firmware 3.12 when using Pebble Health Pebble: option in AppManager to delete files from cache Pebble: enable pbw cache and watchface configuration for Firmware 2.x @@ -158,7 +158,7 @@ Pebble: hopefully fix some reconnect issues Mi Band: fix live activity monitoring running forever if back button pressed Mi Band: allow low latency firmware updates, fixes update with some phones - Mi Band: inital experimental and probably broken support for Amazfit + Mi Band: initial experimental and probably broken support for Amazfit Show aliases for BT Devices if they had been renamed in BT Settings Do not show a hint about App Manager when a Mi Band is connected From 6dd74d04ac841d1694f48f88e97cae5d31a61e0c Mon Sep 17 00:00:00 2001 From: Uwe Hermann Date: Thu, 1 Dec 2016 17:28:51 +0100 Subject: [PATCH 097/147] code/docs: Consistently use the "Gadgetbridge" spelling. "Gadgetbridge" appears to be the common spelling as per most docs, code comments, wiki, and so on. Thus avoid using "GadgetBridge". --- CHANGELOG.md | 2 +- .../freeyourgadget/gadgetbridge/devices/EventHandler.java | 2 +- .../freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java | 2 +- app/src/main/res/values-hu/strings.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ff429a..7db8a50d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -398,7 +398,7 @@ * Fixed crash when synchronizing activity data in the graphs activity and changing device orientation ####Version 0.4.4 -* Set GadgetBridge notification visibility to public, to show the connection status on the lockscreen +* Set Gadgetbridge notification visibility to public, to show the connection status on the lockscreen * Support for backup up and restoring of the activity database (via Debug activity) * Support for graceful upgrades and downgrades, keeping your activity database intact * Enhancement to activity graphs: new graphs for sleep data (only last night) accessible swiping right from the main graph diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 7c7002ee..2e652788 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -14,7 +14,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; /** - * Specifies all events that GadgetBridge intends to send to the gadget device. + * Specifies all events that Gadgetbridge intends to send to the gadget device. * Implementations can decide to ignore events that they do not support. * Implementations need to send/encode event to the connected device. */ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index 49038370..7639da3f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -48,7 +48,7 @@ public final class BtLEQueue { private final InternalGattCallback internalGattCallback; private boolean mAutoReconnect; - private Thread dispatchThread = new Thread("GadgetBridge GATT Dispatcher") { + private Thread dispatchThread = new Thread("Gadgetbridge GATT Dispatcher") { @Override public void run() { diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 22caf542..53475354 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -214,7 +214,7 @@ Lépések ma, cél: %1$s Hagyja az aktivitási adatokat a készüléken Az aktivitás adatok a band-en maradnak. Hasznos, ha a GB-t más alkalamzásokkal együtt használod. - Az adatokat a Mi Band-en fogja tárolni szinkronizálás után is. Hasznos, ha a GadgetBridge-et együtt használod más alkalmazásokkal. + Az adatokat a Mi Band-en fogja tárolni szinkronizálás után is. Hasznos, ha a Gadgetbridge-et együtt használod más alkalmazásokkal. Használja a low-latency módot a Firmware frissítésnél Ez segíthet, ha a firmware frissítés sikertelen. Lépésnapló From 0746aaa579f4b6f50d8103f7e639377a3076a330 Mon Sep 17 00:00:00 2001 From: Uwe Hermann Date: Thu, 1 Dec 2016 20:18:36 +0100 Subject: [PATCH 098/147] app: Random typo and consistency fixes. --- app/src/main/AndroidManifest.xml | 4 ++-- app/src/main/assets/app_config/configure.html | 4 ++-- .../gadgetbridge/activities/DiscoveryActivity.java | 8 ++++---- .../gadgetbridge/activities/ExternalPebbleJSActivity.java | 2 +- .../gadgetbridge/devices/DeviceCoordinator.java | 2 +- .../gadgetbridge/devices/miband/MiBand2Service.java | 2 +- .../gadgetbridge/devices/miband/VibrationProfile.java | 2 +- .../gadgetbridge/devices/pebble/PBWInstallHandler.java | 4 ++-- .../gadgetbridge/devices/pebble/PBWReader.java | 4 ++-- .../gadgetbridge/externalevents/K9Receiver.java | 2 +- .../freeyourgadget/gadgetbridge/impl/GBDevice.java | 2 +- .../gadgetbridge/service/DeviceCommunicationService.java | 2 +- .../gadgetbridge/service/btle/AbstractBTLEOperation.java | 4 ++-- .../gadgetbridge/service/btle/BtLEAction.java | 2 +- .../gadgetbridge/service/btle/BtLEQueue.java | 2 +- .../gadgetbridge/service/btle/actions/WriteAction.java | 2 +- .../btle/profiles/alertnotification/AlertCategory.java | 2 +- .../alertnotification/SupportedNewAlertCategory.java | 2 +- .../btle/profiles/heartrate/BodySensorLocation.java | 2 +- .../service/devices/pebble/AppMessageHandlerPebStyle.java | 4 ++-- .../service/devices/pebble/PebbleIoThread.java | 4 ++-- .../service/devices/pebble/PebbleProtocol.java | 4 ++-- .../service/devices/pebble/ble/PebbleGATTServer.java | 4 ++-- .../nodomain/freeyourgadget/gadgetbridge/util/GB.java | 4 ++-- .../gadgetbridge/service/devices/miband/FirmwareTest.java | 2 +- 25 files changed, 38 insertions(+), 38 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d97e5112..10d64c1f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -174,7 +174,7 @@ - + @@ -182,7 +182,7 @@ - + diff --git a/app/src/main/assets/app_config/configure.html b/app/src/main/assets/app_config/configure.html index 8ab733cd..6da22151 100644 --- a/app/src/main/assets/app_config/configure.html +++ b/app/src/main/assets/app_config/configure.html @@ -54,7 +54,7 @@

-

Url of the configuration:

+

URL of the configuration:

-

In case of "network error" after saving settings in the watchhapp, copy the "network error" +

In case of "network error" after saving settings in the watchapp, copy the "network error" URL and paste it here: