From 483c435aa500bda69f1b674cbae5da6034abdd92 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Dec 2015 19:44:33 +0100 Subject: [PATCH 001/244] Commit old weather experiment - might be worth continuing --- app/src/main/AndroidManifest.xml | 7 ++++ .../WeatherNotificationReceiver.java | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 73064a6d..7fc6c6cd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -196,6 +196,13 @@ + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java new file mode 100644 index 00000000..06145c14 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -0,0 +1,33 @@ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WeatherNotificationReceiver extends BroadcastReceiver { + + private static final Logger LOG = LoggerFactory.getLogger(WeatherNotificationReceiver.class); + private static final int VERSION = 2; + private final String TAG = this.getClass().getSimpleName(); + + @Override + public void onReceive(Context context, Intent intent) { + if (!intent.getAction().contains("WEATHER_UPDATE_2")) { + LOG.info("Wrong action"); + return; + } + int f = intent.getParcelableExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER"); + // int version = parcel.readInt(); + // if (version != VERSION) { + // LOG.info("Wrong version"); + // return; + // } + + //Bundle bundle = parcel.readBundle(this.getClass().getClassLoader()); + // String location = bundle.getString("weather_location"); + // LOG.info("got location: " + location); + } +} \ No newline at end of file From 7a1a6dbb2b34da1f4471fc2e23ddac6f06dee08c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 28 Dec 2015 11:33:22 +0100 Subject: [PATCH 002/244] WeatherNotification: Try to dissect the parcelable extra This is useless, since we do not get the extra at all (only weather skins) So... this is a dead end... --- .../WeatherNotificationReceiver.java | 81 ++++++++++++++++--- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index 06145c14..dc0caf62 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -3,15 +3,65 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class WeatherNotificationReceiver extends BroadcastReceiver { +public class WeatherNotificationReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(WeatherNotificationReceiver.class); - private static final int VERSION = 2; - private final String TAG = this.getClass().getSimpleName(); + + static class Weather implements Parcelable { + // getters and setters suck ;) + + public long time = 0; + public long queryTime = 0; + public int version = 0; + public String location = ""; + int currentTemp = 0; + + private Weather(Parcel in) { + int version = in.readInt(); + if (version != 2) { + LOG.info("wrong version" + version); + return; + } + Bundle bundle = in.readBundle(); + location = bundle.getString("weather_location"); + time = bundle.getLong("weather_time"); + queryTime = bundle.getLong("weather_query_time"); + int conditions = bundle.getInt("weather_conditions"); + if (conditions > 0) { + currentTemp = bundle.getInt("weather_current_temp"); + } + } + + public static final Creator CREATOR = new Creator() { + @Override + public Weather createFromParcel(Parcel in) { + return new Weather(in); + } + + @Override + public Weather[] newArray(int size) { + return new Weather[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + // we do not really want to use this at all + } + } + @Override public void onReceive(Context context, Intent intent) { @@ -19,15 +69,22 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { LOG.info("Wrong action"); return; } - int f = intent.getParcelableExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER"); - // int version = parcel.readInt(); - // if (version != VERSION) { - // LOG.info("Wrong version"); - // return; - // } + Bundle bundle = intent.getExtras(); - //Bundle bundle = parcel.readBundle(this.getClass().getClassLoader()); - // String location = bundle.getString("weather_location"); - // LOG.info("got location: " + location); + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + LOG.info(String.format("%s %s (%s)", key, + value.toString(), value.getClass().getName())); + } + + if (!intent.hasExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER")) { + LOG.info("no weather extra"); + return; + } + + Weather weather = intent.getParcelableExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER"); + if (weather != null) { + LOG.info("weather in " + weather.location + " is " + weather.currentTemp); + } } } \ No newline at end of file From aa1014f51cdb904cef7a4cfa2b9ddec25f43c1be Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Mon, 28 Dec 2015 17:46:16 +0100 Subject: [PATCH 003/244] Added dummy configuration file. This way we get data from the weather notification app. Problem is we probably ned to add it as library, in order to unmarshal the intent. (That's why I commented the offending code in the receiver) --- app/src/main/AndroidManifest.xml | 8 ++++++++ .../externalevents/WeatherNotificationConfig.java | 9 +++++++++ .../externalevents/WeatherNotificationReceiver.java | 5 +++-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7fc6c6cd..60d09124 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -203,6 +203,14 @@ + + + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java new file mode 100644 index 00000000..a2b325e3 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java @@ -0,0 +1,9 @@ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + +import android.app.Activity; + +public class WeatherNotificationConfig extends Activity { + + //TODO: we just need the user to enable us in the weather notification settings. There must be a better way + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index dc0caf62..029637c1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -1,5 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; +import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -71,7 +72,7 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { } Bundle bundle = intent.getExtras(); - for (String key : bundle.keySet()) { +/* for (String key : bundle.keySet()) { Object value = bundle.get(key); LOG.info(String.format("%s %s (%s)", key, value.toString(), value.getClass().getName())); @@ -85,6 +86,6 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { Weather weather = intent.getParcelableExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER"); if (weather != null) { LOG.info("weather in " + weather.location + " is " + weather.currentTemp); - } + }*/ } } \ No newline at end of file From af9ee90383025ebdce8006096c785dbadf600700 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 28 Dec 2015 20:55:59 +0100 Subject: [PATCH 004/244] actually get weather, seems to be the only way --- .../WeatherNotificationReceiver.java | 76 +++---------------- .../notification/ParcelableWeather2.java | 62 +++++++++++++++ 2 files changed, 72 insertions(+), 66 deletions(-) create mode 100644 app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index 029637c1..8ba021e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -1,68 +1,18 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.gelin.android.weather.notification.ParcelableWeather2; + public class WeatherNotificationReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(WeatherNotificationReceiver.class); - static class Weather implements Parcelable { - // getters and setters suck ;) - - public long time = 0; - public long queryTime = 0; - public int version = 0; - public String location = ""; - int currentTemp = 0; - - private Weather(Parcel in) { - int version = in.readInt(); - if (version != 2) { - LOG.info("wrong version" + version); - return; - } - Bundle bundle = in.readBundle(); - location = bundle.getString("weather_location"); - time = bundle.getLong("weather_time"); - queryTime = bundle.getLong("weather_query_time"); - int conditions = bundle.getInt("weather_conditions"); - if (conditions > 0) { - currentTemp = bundle.getInt("weather_current_temp"); - } - } - - public static final Creator CREATOR = new Creator() { - @Override - public Weather createFromParcel(Parcel in) { - return new Weather(in); - } - - @Override - public Weather[] newArray(int size) { - return new Weather[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - // we do not really want to use this at all - } - } - @Override public void onReceive(Context context, Intent intent) { @@ -70,22 +20,16 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { LOG.info("Wrong action"); return; } - Bundle bundle = intent.getExtras(); - -/* for (String key : bundle.keySet()) { - Object value = bundle.get(key); - LOG.info(String.format("%s %s (%s)", key, - value.toString(), value.getClass().getName())); + ParcelableWeather2 weather = null; + try { + weather = intent.getParcelableExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER"); + } catch (RuntimeException e) { + e.printStackTrace(); } - if (!intent.hasExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER")) { - LOG.info("no weather extra"); - return; - } - - Weather weather = intent.getParcelableExtra("ru.gelin.android.weather.notification.EXTRA_WEATHER"); if (weather != null) { - LOG.info("weather in " + weather.location + " is " + weather.currentTemp); - }*/ + LOG.info("weather in " + weather.location + " is " + (weather.currentTemp - 273) + "°C"); + } + } } \ No newline at end of file diff --git a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java new file mode 100644 index 00000000..b340898b --- /dev/null +++ b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java @@ -0,0 +1,62 @@ +package ru.gelin.android.weather.notification; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ParcelableWeather2 implements Parcelable { + private static final Logger LOG = LoggerFactory.getLogger(ParcelableWeather2.class); + + // getters and setters suck ;) + + public long time = 0; + public long queryTime = 0; + public int version = 0; + public String location = ""; + public int currentTemp = 0; + + private ParcelableWeather2(Parcel in) { + int version = in.readInt(); + if (version != 2) { + return; + } + Bundle bundle = in.readBundle(this.getClass().getClassLoader()); + location = bundle.getString("weather_location"); + time = bundle.getLong("weather_time"); + queryTime = bundle.getLong("weather_query_time"); + bundle.getString("weather_forecast_url"); + int conditions = bundle.getInt("weather_conditions"); + if (conditions > 0) { + Bundle conditionBundle = in.readBundle(this.getClass().getClassLoader()); + conditionBundle.getString("weather_condition_text"); + conditionBundle.getStringArray("weather_condition_types"); + currentTemp = conditionBundle.getInt("weather_current_temp"); + + } + } + + public static final Creator CREATOR = new Creator() { + @Override + public ParcelableWeather2 createFromParcel(Parcel in) { + return new ParcelableWeather2(in); + } + + @Override + public ParcelableWeather2[] newArray(int size) { + return new ParcelableWeather2[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + // we do not really want to use this at all + } +} From e533fdbaa6c955f70a0ae3d3357482b6c12d8d8e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 29 Dec 2015 22:10:38 +0100 Subject: [PATCH 005/244] Pebble: actually display current whether in WeatherNeat --- .../WeatherNotificationReceiver.java | 12 ++++++++++-- .../pebble/AppMessageHandlerWeatherNeat.java | 19 +++++++++++++------ .../notification/ParcelableWeather2.java | 15 +++++++++++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index 8ba021e5..4a598aef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -3,6 +3,8 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +30,14 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { } if (weather != null) { - LOG.info("weather in " + weather.location + " is " + (weather.currentTemp - 273) + "°C"); - } + LOG.info("weather in " + weather.location + " is " + weather.currentCondition + " (" + (weather.currentTemp - 273) + "°C)"); + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor edit = sharedPrefs.edit(); + edit.putString("weather_location", weather.location); + edit.putString("weather_current_condition", weather.currentCondition); + edit.putInt("weather_current_temp", weather.currentTemp); + edit.apply(); + } } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java index c32074d3..ac09bc7a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.util.Pair; import org.slf4j.Logger; @@ -9,6 +11,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; @@ -16,7 +19,7 @@ public class AppMessageHandlerWeatherNeat extends AppMessageHandler { public static final int KEY_REQUEST = 0; public static final int KEY_CITY = 1; - public static final int KEY_TEMPERATUR = 2; + public static final int KEY_TEMPERATURE = 2; public static final int KEY_CONDITION = 3; public static final int KEY_LIGHT_TIME = 5; @@ -28,10 +31,10 @@ public class AppMessageHandlerWeatherNeat extends AppMessageHandler { private byte[] encodeWeatherNeatMessage(String city, String temperature, String condition, int light_time) { ArrayList> pairs = new ArrayList<>(4); - pairs.add(new Pair<>(1, (Object) city)); - pairs.add(new Pair<>(2, (Object) temperature)); - pairs.add(new Pair<>(3, (Object) condition)); - pairs.add(new Pair<>(5, (Object) light_time)); // seconds for backlight on shake + pairs.add(new Pair<>(KEY_CITY, (Object) city)); + pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) temperature)); + pairs.add(new Pair<>(KEY_CONDITION, (Object) condition)); + pairs.add(new Pair<>(KEY_LIGHT_TIME, (Object) light_time)); // seconds for backlight on shake byte[] ackMessage = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); byte[] testMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); @@ -47,8 +50,12 @@ public class AppMessageHandlerWeatherNeat extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + String currentTemp = (sharedPrefs.getInt("weather_current_temp", 0) - 273) + "°C"; + String location = sharedPrefs.getString("weather_location", "unknown"); + String condition = sharedPrefs.getString("weather_current_condition", "unknown"); GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeWeatherNeatMessage("Berlin", "22 C", "cloudy", 0); + sendBytes.encodedBytes = encodeWeatherNeatMessage(location, currentTemp, condition, 3); return new GBDeviceEvent[]{sendBytes}; } } diff --git a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java index b340898b..ab04cace 100644 --- a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java +++ b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java @@ -17,24 +17,31 @@ public class ParcelableWeather2 implements Parcelable { public int version = 0; public String location = ""; public int currentTemp = 0; + public String currentCondition = ""; private ParcelableWeather2(Parcel in) { int version = in.readInt(); if (version != 2) { return; } - Bundle bundle = in.readBundle(this.getClass().getClassLoader()); + Bundle bundle = in.readBundle(); location = bundle.getString("weather_location"); time = bundle.getLong("weather_time"); queryTime = bundle.getLong("weather_query_time"); bundle.getString("weather_forecast_url"); int conditions = bundle.getInt("weather_conditions"); if (conditions > 0) { - Bundle conditionBundle = in.readBundle(this.getClass().getClassLoader()); - conditionBundle.getString("weather_condition_text"); + Bundle conditionBundle = in.readBundle(); + currentCondition = conditionBundle.getString("weather_condition_text"); conditionBundle.getStringArray("weather_condition_types"); currentTemp = conditionBundle.getInt("weather_current_temp"); - + } + // get the rest + while(--conditions > 0) { + Bundle conditionBundle = in.readBundle(); + conditionBundle.getString("weather_condition_text"); + conditionBundle.getStringArray("weather_condition_types"); + conditionBundle.getInt("weather_current_temp"); } } From abb7ed01899f6fe6366d20c164607d704fcab04a Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 3 Jan 2016 16:33:48 +0100 Subject: [PATCH 006/244] Manual mapping between openweathermap and yahoo weather conditions. --- .../gadgetbridge/model/Weather.java | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java new file mode 100644 index 00000000..b8be5cca --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java @@ -0,0 +1,218 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +public class Weather { + + public int mapToYahooCondition(int openWeatherMapCondition) { + // openweathermap.org conditions: + // http://openweathermap.org/weather-conditions + switch (openWeatherMapCondition) { +//Group 2xx: Thunderstorm + case 200: //thunderstorm with light rain: //11d + case 201: //thunderstorm with rain: //11d + case 202: //thunderstorm with heavy rain: //11d + case 210: //light thunderstorm:: //11d + case 211: //thunderstorm: //11d + case 230: //thunderstorm with light drizzle: //11d + case 231: //thunderstorm with drizzle: //11d + case 232: //thunderstorm with heavy drizzle: //11d + return 4; + case 212: //heavy thunderstorm: //11d + case 221: //ragged thunderstorm: //11d + return 3; +//Group 3xx: Drizzle + case 300: //light intensity drizzle: //09d + case 301: //drizzle: //09d + case 302: //heavy intensity drizzle: //09d + case 310: //light intensity drizzle rain: //09d + case 311: //drizzle rain: //09d + case 312: //heavy intensity drizzle rain: //09d + return 9; + case 313: //shower rain and drizzle: //09d + case 314: //heavy shower rain and drizzle: //09d + case 321: //shower drizzle: //09d + return 11; +//Group 5xx: Rain + case 500: //light rain: //10d + case 501: //moderate rain: //10d + case 502: //heavy intensity rain: //10d + case 503: //very heavy rain: //10d + case 504: //extreme rain: //10d + case 511: //freezing rain: //13d + return 10; + case 520: //light intensity shower rain: //09d + return 40; + case 521: //shower rain: //09d + case 522: //heavy intensity shower rain: //09d + case 531: //ragged shower rain: //09d + return 12; +//Group 6xx: Snow + case 600: //light snow: //[[file:13d.png]] + return 7; + case 601: //snow: //[[file:13d.png]] + return 16; + case 602: //heavy snow: //[[file:13d.png]] + return 15; + case 611: //sleet: //[[file:13d.png]] + case 612: //shower sleet: //[[file:13d.png]] + return 18; + case 615: //light rain and snow: //[[file:13d.png]] + case 616: //rain and snow: //[[file:13d.png]] + return 5; + case 620: //light shower snow: //[[file:13d.png]] + return 14; + case 621: //shower snow: //[[file:13d.png]] + return 46; + case 622: //heavy shower snow: //[[file:13d.png]] +//Group 7xx: Atmosphere + case 701: //mist: //[[file:50d.png]] + case 711: //smoke: //[[file:50d.png]] + return 22; + case 721: //haze: //[[file:50d.png]] + return 21; + case 731: //sandcase dust whirls: //[[file:50d.png]] + return 3200; + case 741: //fog: //[[file:50d.png]] + return 20; + case 751: //sand: //[[file:50d.png]] + case 761: //dust: //[[file:50d.png]] + return 19; + case 762: //volcanic ash: //[[file:50d.png]] + case 771: //squalls: //[[file:50d.png]] + return 3200; + case 781: //tornado: //[[file:50d.png]] + case 900: //tornado + return 0; +//Group 800: Clear + case 800: //clear sky: //[[file:01d.png]] [[file:01n.png]] + return 32; +//Group 80x: Clouds + case 801: //few clouds: //[[file:02d.png]] [[file:02n.png]] + case 802: //scattered clouds: //[[file:03d.png]] [[file:03d.png]] + return 34; + case 803: //broken clouds: //[[file:04d.png]] [[file:03d.png]] + case 804: //overcast clouds: //[[file:04d.png]] [[file:04d.png]] + return 44; +//Group 90x: Extreme + case 901: //tropical storm + return 1; + case 903: //cold + return 25; + case 904: //hot + return 36; + case 905: //windy + return 24; + case 906: //hail + return 17; +//Group 9xx: Additional + case 951: //calm + case 952: //light breeze + case 953: //gentle breeze + case 954: //moderate breeze + case 955: //fresh breeze + return 34; + case 956: //strong breeze + case 957: //high windcase near gale + return 24; + case 958: //gale + case 959: //severe gale + case 960: //storm + case 961: //violent storm + return 3200; + case 902: //hurricane + case 962: //hurricane + return 2; + default: + return 3200; + + } + } + + + public int mapToOpenWeatherMapCondition(int yahooCondition) { + switch (yahooCondition) { +//yahoo weather conditions: +//https://developer.yahoo.com/weather/documentation.html + case 0: //tornado + return 900; + case 1: //tropical storm + return 901; + case 2: //hurricane + return 962; + case 3: //severe thunderstorms + return 212; + case 4: //thunderstorms + return 211; + case 5: //mixed rain and snow + case 6: //mixed rain and sleet + return 616; + case 7: //mixed snow and sleet + return 600; + case 8: //freezing drizzle + case 9: //drizzle + return 301; + case 10: //freezing rain + return 511; + case 11: //showers + case 12: //showers + return 521; + case 13: //snow flurries + case 14: //light snow showers + return 620; + case 15: //blowing snow + case 41: //heavy snow + case 42: //scattered snow showers + case 43: //heavy snow + case 46: //snow showers + return 602; + case 16: //snow + return 601; + case 17: //hail + case 35: //mixed rain and hail + return 906; + case 18: //sleet + return 611; + case 19: //dust + return 761; + case 20: //foggy + return 741; + case 21: //haze + return 721; + case 22: //smoky + return 711; + case 23: //blustery + case 24: //windy + return 905; + case 25: //cold + return 903; + case 26: //cloudy + case 27: //mostly cloudy (night) + case 28: //mostly cloudy (day) + return 804; + case 29: //partly cloudy (night) + case 30: //partly cloudy (day) + return 801; + case 31: //clear (night) + case 32: //sunny + return 800; + case 33: //fair (night) + case 34: //fair (day) + return 801; + case 36: //hot + return 904; + case 37: //isolated thunderstorms + case 38: //scattered thunderstorms + case 39: //scattered thunderstorms + return 210; + case 40: //scattered showers + return 520; + case 44: //partly cloudy + return 801; + case 45: //thundershowers + case 47: //isolated thundershowers + return 621; + case 3200: //not available + default: + return -1; + } + } +} From c7c723134ea73d88e9dea9382fecd11303268455 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 3 Jan 2016 18:28:32 +0100 Subject: [PATCH 007/244] Add weather singleton (to store the whole data passed by weather notification). Add weather info for TimeStylePebble. Add further fields to the ParcelableWeather class. Add reverse mapping to ParcelableWeather to get back the original OpenWeatherMap API condition codes. --- .../WeatherNotificationReceiver.java | 2 + .../gadgetbridge/model/Weather.java | 8 ++ .../AppMessageHandlerTimeStylePebble.java | 39 +++--- .../notification/ParcelableWeather2.java | 129 +++++++++++++++++- 4 files changed, 159 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index 4a598aef..7a8e2aa7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -9,6 +9,7 @@ import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; import ru.gelin.android.weather.notification.ParcelableWeather2; @@ -30,6 +31,7 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { } if (weather != null) { + Weather.getInstance().setWeather2(weather); LOG.info("weather in " + weather.location + " is " + weather.currentCondition + " (" + (weather.currentTemp - 273) + "°C)"); SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java index b8be5cca..eedb9ac1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java @@ -1,6 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.model; +import ru.gelin.android.weather.notification.ParcelableWeather2; + public class Weather { + private ParcelableWeather2 weather2 = null; + public ParcelableWeather2 getWeather2() {return weather2;} + public void setWeather2(ParcelableWeather2 weather2) {this.weather2 = weather2;} + + private static final Weather weather = new Weather(); + public static Weather getInstance() {return weather;} public int mapToYahooCondition(int openWeatherMapCondition) { // openweathermap.org conditions: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 13bf8430..d0c4d89f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -12,6 +12,8 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import ru.gelin.android.weather.notification.ParcelableWeather2; public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { public static final int KEY_SETTING_SIDEBAR_LEFT = 9; @@ -59,43 +61,44 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { pairs.add(new Pair<>(KEY_SETTING_SHOW_LEADING_ZERO, (Object) 1)); pairs.add(new Pair<>(KEY_SETTING_LANGUAGE_ID, (Object) 2)); //2 = Deutsch pairs.add(new Pair<>(KEY_SETTING_USE_METRIC, (Object) 1)); + pairs.add(new Pair<>(KEY_SETTING_SHOW_BATTERY_PCT, (Object) 1)); pairs.add(new Pair<>(KEY_WIDGET_0_ID, (Object) 7)); //7 = current weather pairs.add(new Pair<>(KEY_WIDGET_1_ID, (Object) 2)); //2 = battery pairs.add(new Pair<>(KEY_WIDGET_2_ID, (Object) 4)); //4 = Date -/* - pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) 6)); - pairs.add(new Pair<>(KEY_CONDITION_CODE, (Object) 25)); - pairs.add(new Pair<>(KEY_FORECAST_CONDITION, (Object) 2)); - pairs.add(new Pair<>(KEY_FORECAST_TEMP_HIGH, (Object) 12)); - pairs.add(new Pair<>(KEY_FORECAST_TEMP_LOW, (Object) 0)); -*/ - byte[] ackMessage = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); byte[] testMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); - //byte[] weatherMessage=encodeTimeStylePebbleWeather(); - - ByteBuffer buf = ByteBuffer.allocate(ackMessage.length + testMessage.length ); + byte[] weatherMessage=encodeTimeStylePebbleWeather(); + ByteBuffer buf = ByteBuffer.allocate(ackMessage.length + testMessage.length + weatherMessage.length); // encode ack and put in front of push message (hack for acknowledging the last message) buf.put(ackMessage); buf.put(testMessage); + buf.put(weatherMessage); return buf.array(); } private byte[] encodeTimeStylePebbleWeather() { ArrayList> pairs = new ArrayList<>(); - pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) 6)); - pairs.add(new Pair<>(KEY_CONDITION_CODE, (Object) 1)); - pairs.add(new Pair<>(KEY_FORECAST_CONDITION, (Object) 2)); - pairs.add(new Pair<>(KEY_FORECAST_TEMP_HIGH, (Object) 12)); - pairs.add(new Pair<>(KEY_FORECAST_TEMP_LOW, (Object) 0)); + ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + + if (weather != null) { + //TODO: use the night icons when night + pairs.add(new Pair<>(KEY_USE_NIGHT_ICON, (Object) 0)); + pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) (weather.currentTemp - 273))); + pairs.add(new Pair<>(KEY_CONDITION_CODE, (Object) weather.currentConditionCode)); + pairs.add(new Pair<>(KEY_FORECAST_CONDITION, (Object) weather.forecastConditionCode)); + pairs.add(new Pair<>(KEY_FORECAST_TEMP_HIGH, (Object) (weather.highTemp - 273))); + pairs.add(new Pair<>(KEY_FORECAST_TEMP_LOW, (Object) (weather.lowTemp - 273))); + byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + return weatherMessage; + } + + return null; - byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); - return weatherMessage; } @Override diff --git a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java index ab04cace..d7b42674 100644 --- a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java +++ b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java @@ -19,6 +19,14 @@ public class ParcelableWeather2 implements Parcelable { public int currentTemp = 0; public String currentCondition = ""; + String[] currentConditionType = null; + public int currentConditionCode = 3200; + String[] forecastConditionType = null; + public int forecastConditionCode = 3200; + public int lowTemp = 0; + public int highTemp = 0; + + private ParcelableWeather2(Parcel in) { int version = in.readInt(); if (version != 2) { @@ -35,9 +43,20 @@ public class ParcelableWeather2 implements Parcelable { currentCondition = conditionBundle.getString("weather_condition_text"); conditionBundle.getStringArray("weather_condition_types"); currentTemp = conditionBundle.getInt("weather_current_temp"); + + currentConditionType = conditionBundle.getStringArray("weather_condition_types"); + currentConditionCode = weatherConditionTypesToOpenWeatherMapIds(currentConditionType[0]); + lowTemp = conditionBundle.getInt("weather_low_temp"); + highTemp = conditionBundle.getInt("weather_high_temp"); + //fetch immediate next forecast + if (--conditions > 0) { + Bundle forecastBundle = in.readBundle(); + forecastConditionType = forecastBundle.getStringArray("weather_condition_types"); + forecastConditionCode = weatherConditionTypesToOpenWeatherMapIds(currentConditionType[0]); + } } // get the rest - while(--conditions > 0) { + while (--conditions > 0) { Bundle conditionBundle = in.readBundle(); conditionBundle.getString("weather_condition_text"); conditionBundle.getStringArray("weather_condition_types"); @@ -66,4 +85,112 @@ public class ParcelableWeather2 implements Parcelable { public void writeToParcel(Parcel dest, int flags) { // we do not really want to use this at all } + + private int weatherConditionTypesToOpenWeatherMapIds(String weather_condition_type) { + switch (weather_condition_type) { + case "THUNDERSTORM_RAIN_LIGHT": + return 200; + case "THUNDERSTORM_RAIN": + return 201; + case "THUNDERSTORM_RAIN_HEAVY": + return 202; + case "THUNDERSTORM_LIGHT": + return 210; + case "THUNDERSTORM": + return 211; + case "THUNDERSTORM_HEAVY": + return 212; + case "THUNDERSTORM_RAGGED": + return 221; + case "THUNDERSTORM_DRIZZLE_LIGHT": + return 230; + case "THUNDERSTORM_DRIZZLE": + return 231; + case "THUNDERSTORM_DRIZZLE_HEAVY": + return 232; + + case "DRIZZLE_LIGHT": + return 300; + case "DRIZZLE": + return 301; + case "DRIZZLE_HEAVY": + return 302; + case "DRIZZLE_RAIN_LIGHT": + return 310; + case "DRIZZLE_RAIN": + return 311; + case "DRIZZLE_RAIN_HEAVY": + return 312; + case "DRIZZLE_SHOWER": + return 321; + + case "RAIN_LIGHT": + return 500; + case "RAIN": + return 501; + case "RAIN_HEAVY": + return 502; + case "RAIN_VERY_HEAVY": + return 503; + case "RAIN_EXTREME": + return 504; + case "RAIN_FREEZING": + return 511; + case "RAIN_SHOWER_LIGHT": + return 520; + case "RAIN_SHOWER": + return 521; + case "RAIN_SHOWER_HEAVY": + return 522; + + case "SNOW_LIGHT": + return 600; + case "SNOW": + return 601; + case "SNOW_HEAVY": + return 602; + case "SLEET": + return 611; + case "SNOW_SHOWER": + return 621; + + case "MIST": + return 701; + case "SMOKE": + return 711; + case "HAZE": + return 721; + case "SAND_WHIRLS": + return 731; + case "FOG": + return 741; + + case "CLOUDS_CLEAR": + return 800; + case "CLOUDS_FEW": + return 801; + case "CLOUDS_SCATTERED": + return 802; + case "CLOUDS_BROKEN": + return 803; + case "CLOUDS_OVERCAST": + return 804; + + case "TORNADO": + return 900; + case "TROPICAL_STORM": + return 901; + case "HURRICANE": + return 902; + case "COLD": + return 903; + case "HOT": + return 904; + case "WINDY": + return 905; + case "HAIL": + return 906; + } + return 3200; + } } From 092af9f38d04ba6c8d2e3644ebfd6953e824caf8 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Tue, 5 Jan 2016 16:03:59 +0100 Subject: [PATCH 008/244] fix forecast condition parsing --- .../gelin/android/weather/notification/ParcelableWeather2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java index d7b42674..8defe1e0 100644 --- a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java +++ b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java @@ -52,7 +52,7 @@ public class ParcelableWeather2 implements Parcelable { if (--conditions > 0) { Bundle forecastBundle = in.readBundle(); forecastConditionType = forecastBundle.getStringArray("weather_condition_types"); - forecastConditionCode = weatherConditionTypesToOpenWeatherMapIds(currentConditionType[0]); + forecastConditionCode = weatherConditionTypesToOpenWeatherMapIds(forecastConditionType[0]); } } // get the rest From d7f4769f5713758af259c1f1e27b1313ee35a4ae Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Tue, 5 Jan 2016 16:04:32 +0100 Subject: [PATCH 009/244] make the conversion methods static --- .../nodomain/freeyourgadget/gadgetbridge/model/Weather.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java index eedb9ac1..59981199 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java @@ -10,7 +10,7 @@ public class Weather { private static final Weather weather = new Weather(); public static Weather getInstance() {return weather;} - public int mapToYahooCondition(int openWeatherMapCondition) { + public static int mapToYahooCondition(int openWeatherMapCondition) { // openweathermap.org conditions: // http://openweathermap.org/weather-conditions switch (openWeatherMapCondition) { @@ -135,8 +135,7 @@ public class Weather { } } - - public int mapToOpenWeatherMapCondition(int yahooCondition) { + public static int mapToOpenWeatherMapCondition(int yahooCondition) { switch (yahooCondition) { //yahoo weather conditions: //https://developer.yahoo.com/weather/documentation.html From 3d389f31a329db56f124bbe42f09cc4142fdc566 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Tue, 5 Jan 2016 16:05:12 +0100 Subject: [PATCH 010/244] fix force close when weather hasn't been parsed yet, use the yahoo codes for this watchface, as required --- .../pebble/AppMessageHandlerTimeStylePebble.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index d0c4d89f..da4fc399 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -89,15 +89,13 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { //TODO: use the night icons when night pairs.add(new Pair<>(KEY_USE_NIGHT_ICON, (Object) 0)); pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) (weather.currentTemp - 273))); - pairs.add(new Pair<>(KEY_CONDITION_CODE, (Object) weather.currentConditionCode)); - pairs.add(new Pair<>(KEY_FORECAST_CONDITION, (Object) weather.forecastConditionCode)); + pairs.add(new Pair<>(KEY_CONDITION_CODE, (Object) Weather.mapToYahooCondition(weather.currentConditionCode))); + pairs.add(new Pair<>(KEY_FORECAST_CONDITION, (Object) Weather.mapToYahooCondition(weather.forecastConditionCode))); pairs.add(new Pair<>(KEY_FORECAST_TEMP_HIGH, (Object) (weather.highTemp - 273))); pairs.add(new Pair<>(KEY_FORECAST_TEMP_LOW, (Object) (weather.lowTemp - 273))); - byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); - return weatherMessage; } - - return null; + byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + return weatherMessage; } From 7ba156da62ec78bf797392b923102aa6ab917334 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 10 Jan 2016 20:12:52 +0100 Subject: [PATCH 011/244] Try to support MarioTime. Does not work :/ --- .../pebble/AppMessageHandlerMarioTime.java | 69 +++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 3 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java new file mode 100644 index 00000000..edfeac41 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -0,0 +1,69 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.util.Pair; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import ru.gelin.android.weather.notification.ParcelableWeather2; + +public class AppMessageHandlerMarioTime extends AppMessageHandler { + + public static final int KEY_WEATHER_ICON_ID = 10; + public static final int KEY_WEATHER_TEMPERATURE = 11; + public static final int KEY_WEATHER_REQUEST = 12; + + private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerMarioTime.class); + + public AppMessageHandlerMarioTime(UUID uuid, PebbleProtocol pebbleProtocol) { + super(uuid, pebbleProtocol); + } + + private byte[] encodeWeatherMessage(int temperature, int condition) { + ArrayList> pairs = new ArrayList<>(2); + pairs.add(new Pair<>(KEY_WEATHER_ICON_ID, (Object) (byte) condition)); + pairs.add(new Pair<>(KEY_WEATHER_TEMPERATURE, (Object) (byte) temperature)); + byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + + ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); + + // encode ack and put in front of push message (hack for acknowledging the last message) + buf.put(weatherMessage); + + return buf.array(); + } + + @Override + public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + boolean weatherRequested = false; + for (Pair pair : pairs) { + switch (pair.first) { + case KEY_WEATHER_REQUEST: + LOG.info("got weather request"); + weatherRequested = true; + break; + default: + LOG.info("unknown key " + pair.first); + } + } + if (!weatherRequested) { + return new GBDeviceEvent[]{null}; + } + ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeWeatherMessage(weather.currentConditionCode, weather.currentTemp - 273); + + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + + return new GBDeviceEvent[]{sendBytesAck, sendBytes}; + } +} 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 f0373381..05c9b86e 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 @@ -351,6 +351,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_PEBBLE_HEALTH = UUID.fromString("36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c"); private static final UUID UUID_PEBBLE_TIMESTYLE = UUID.fromString("4368ffa4-f0fb-4823-90be-f754b076bdaa"); private static final UUID UUID_PEBSTYLE = UUID.fromString("da05e84d-e2a2-4020-a2dc-9cdcf265fcdd"); + private static final UUID UUID_MARIOTIME = UUID.fromString("43caa750-2896-4f46-94dc-1adbd4bc1ff3"); private static final Map mAppMessageHandlers = new HashMap<>(); @@ -361,7 +362,7 @@ public class PebbleProtocol extends GBDeviceProtocol { mAppMessageHandlers.put(UUID_MISFIT, new AppMessageHandlerMisfit(UUID_MISFIT, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_PEBBLE_TIMESTYLE, new AppMessageHandlerTimeStylePebble(UUID_PEBBLE_TIMESTYLE, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_PEBSTYLE, new AppMessageHandlerPebStyle(UUID_PEBSTYLE, PebbleProtocol.this)); - + mAppMessageHandlers.put(UUID_MARIOTIME, new AppMessageHandlerMarioTime(UUID_MARIOTIME, PebbleProtocol.this)); } private static byte[] encodeSimpleMessage(short endpoint, byte command) { From 3d3643ece3531f9b551843767ac549ec929a9b56 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Fri, 22 Jan 2016 21:04:01 +0100 Subject: [PATCH 012/244] Add weather to PebStyle as well. --- .../pebble/AppMessageHandlerPebStyle.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java index c29460da..37468e75 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java @@ -12,6 +12,8 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import ru.gelin.android.weather.notification.ParcelableWeather2; public class AppMessageHandlerPebStyle extends AppMessageHandler { public static final int KEY_AMPM_TEXT = 21; @@ -57,7 +59,7 @@ public class AppMessageHandlerPebStyle extends AppMessageHandler { pairs.add(new Pair<>(KEY_SECOND_HAND, (Object) 0)); //1 enabled pairs.add(new Pair<>(KEY_BLUETOOTH_ALERT, (Object) 0)); //1 silent, 2 weak, up to 5 pairs.add(new Pair<>(KEY_TEMPERATURE_FORMAT, (Object) 1)); //0 fahrenheit - pairs.add(new Pair<>(KEY_LOCATION_SERVICE, (Object) 2)); //0 uto, 1 manual + //pairs.add(new Pair<>(KEY_LOCATION_SERVICE, (Object) 2)); //0 uto, 1 manual pairs.add(new Pair<>(KEY_SIDEBAR_LOCATION, (Object) 1)); //0 right pairs.add(new Pair<>(KEY_COLOR_SELECTION, (Object) 1)); //1 custom pairs.add(new Pair<>(KEY_MAIN_COLOR, (Object) PebbleColor.Black)); @@ -65,7 +67,7 @@ public class AppMessageHandlerPebStyle extends AppMessageHandler { pairs.add(new Pair<>(KEY_SIDEBAR_BG_COLOR, (Object) PebbleColor.MediumSpringGreen)); //DIGITAL settings - /* + /* pairs.add(new Pair<>(KEY_MAIN_CLOCK, (Object) 1)); //0 analog pairs.add(new Pair<>(KEY_SECONDARY_INFO_TYPE, (Object) 3)); //1 time, 2 location */ @@ -75,12 +77,13 @@ public class AppMessageHandlerPebStyle extends AppMessageHandler { //WEATHER - /* - //comment the same key in the general section above! - pairs.add(new Pair<>(KEY_LOCATION_SERVICE, (Object) 0)); //0 auto, 1 manual - pairs.add(new Pair<>(KEY_WEATHER_CODE, (Object) 3)); - pairs.add(new Pair<>(KEY_WEATHER_TEMP, (Object) 10)); - */ + ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + if (weather != null) { + //comment the same key in the general section above! + pairs.add(new Pair<>(KEY_LOCATION_SERVICE, (Object) 0)); //0 auto, 1 manual + pairs.add(new Pair<>(KEY_WEATHER_CODE, (Object) Weather.mapToYahooCondition(weather.currentConditionCode))); + pairs.add(new Pair<>(KEY_WEATHER_TEMP, (Object) (weather.currentTemp - 273))); + } byte[] testMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); From f9d2fddb7a4c92b3685de89f13576e6554ed7084 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 28 Jan 2016 11:12:28 +0100 Subject: [PATCH 013/244] Android studio 2 complains about this comparison otherwise --- .../nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java index 42bc1566..c69cc0de 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java @@ -23,7 +23,7 @@ public class LimitedQueue { public void remove(int id) { for (Iterator iter = list.iterator(); iter.hasNext(); ) { Pair pair = iter.next(); - if (pair.first == id) { + if (((int)pair.first) == id) { iter.remove(); } } From 088dfda5f49c504cabe5455d17f7e05ee4147790 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 9 Jun 2016 19:55:36 +0200 Subject: [PATCH 014/244] Pebble: implement app reordering in PebbleProtocol Not yet used. --- .../devices/pebble/PebbleProtocol.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) 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 d53de9b1..3d274cee 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 @@ -69,6 +69,7 @@ public class PebbleProtocol extends GBDeviceProtocol { static final short ENDPOINT_RUNKEEPER = 7000; static final short ENDPOINT_SCREENSHOT = 8000; static final short ENDPOINT_NOTIFICATIONACTION = 11440; // 3.x only, TODO: find a better name + static final short ENDPOINT_APPREORDER = (short) 0xabcd; // 3.x only static final short ENDPOINT_BLOBDB = (short) 45531; // 3.x only static final short ENDPOINT_PUTBYTES = (short) 48879; @@ -1282,6 +1283,22 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeSimpleMessage(ENDPOINT_SCREENSHOT, SCREENSHOT_TAKE); } + public byte[] encodeAppReoder(UUID[] uuids) { + int length = 2 + uuids.length * LENGTH_UUID; + ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + length); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putShort((short) length); + buf.putShort(ENDPOINT_APPREORDER); + buf.put((byte) 0x01); + buf.put((byte) uuids.length); + for (UUID uuid : uuids) { + buf.putLong(uuid.getMostSignificantBits()); + buf.putLong(uuid.getLeastSignificantBits()); + } + + return buf.array(); + } + /* pebble specific install methods */ public byte[] encodeUploadStart(byte type, int app_id, int size, String filename) { short length; @@ -1937,6 +1954,16 @@ public class PebbleProtocol extends GBDeviceProtocol { return sendBytes; } + private GBDeviceEvent decodeAppReorder(ByteBuffer buf) { + byte status = buf.get(); + if (status == 1) { + LOG.info("app reordering successful"); + } else { + LOG.info("app reordering returned status " + status); + } + return null; + } + @Override public GBDeviceEvent[] decodeResponse(byte[] responseData) { ByteBuffer buf = ByteBuffer.wrap(responseData); @@ -2187,6 +2214,8 @@ public class PebbleProtocol extends GBDeviceProtocol { case ENDPOINT_BLOBDB: devEvts = new GBDeviceEvent[]{decodeBlobDb(buf)}; break; + case ENDPOINT_APPREORDER: + devEvts = new GBDeviceEvent[]{decodeAppReorder(buf)}; default: break; } From b0fe4b151939039162a41c43e5a7dffa84126830 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 9 Jun 2016 23:39:00 +0200 Subject: [PATCH 015/244] also gather music info from notifications when screen is off --- .../externalevents/NotificationListener.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 16647ff6..d8e0f021 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -169,13 +169,6 @@ public class NotificationListener extends NotificationListenerService { return; } - Prefs prefs = GBApplication.getPrefs(); - if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) { - PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE); - if (powermanager.isScreenOn()) { - return; - } - } switch (GBApplication.getGrantedInterruptionFilter()) { case NotificationManager.INTERRUPTION_FILTER_ALL: break; @@ -193,6 +186,14 @@ public class NotificationListener extends NotificationListenerService { if (handleMediaSessionNotification(notification)) return; + Prefs prefs = GBApplication.getPrefs(); + if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) { + PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE); + if (powermanager.isScreenOn()) { + return; + } + } + if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) { return; } From 779699cd955aa5ba40127282c498b1d29a199f11 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 9 Dec 2016 23:31:32 +0100 Subject: [PATCH 016/244] Pebble: remove configuration stuff from TimeStylePebble handler, update weather keys This is not usable now, just playing around --- .../AppMessageHandlerTimeStylePebble.java | 100 +++++------------- 1 file changed, 25 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 9baeb477..ffb58076 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -1,48 +1,25 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; -import android.graphics.Color; import android.util.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; - import ru.gelin.android.weather.notification.ParcelableWeather2; public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { - public static final int KEY_SETTING_SIDEBAR_LEFT = 9; - public static final int KEY_CONDITION_CODE = 4; - public static final int KEY_FORECAST_CONDITION = 25; - public static final int KEY_FORECAST_TEMP_HIGH = 26; - public static final int KEY_FORECAST_TEMP_LOW = 27; - public static final int KEY_SETTING_ALTCLOCK_NAME = 28; - public static final int KEY_SETTING_ALTCLOCK_OFFSET = 29; - public static final int KEY_SETTING_BT_VIBE = 11; - public static final int KEY_SETTING_CLOCK_FONT_ID = 18; - public static final int KEY_SETTING_COLOR_BG = 7; - public static final int KEY_SETTING_COLOR_SIDEBAR = 8; - public static final int KEY_SETTING_COLOR_TIME = 6; - public static final int KEY_SETTING_DISABLE_WEATHER = 17; - public static final int KEY_SETTING_HOURLY_VIBE = 19; - public static final int KEY_SETTING_LANGUAGE_ID = 13; - public static final int KEY_SETTING_ONLY_SHOW_BATTERY_WHEN_LOW = 20; - public static final int KEY_SETTING_SHOW_BATTERY_PCT = 16; - public static final int KEY_SETTING_SHOW_LEADING_ZERO = 15; - public static final int KEY_SETTING_SIDEBAR_TEXT_COLOR = 12; - public static final int KEY_SETTING_USE_LARGE_FONTS = 21; - public static final int KEY_SETTING_USE_METRIC = 10; - public static final int KEY_TEMPERATURE = 3; - public static final int KEY_USE_NIGHT_ICON = 5; - public static final int KEY_WIDGET_0_ID = 22; - public static final int KEY_WIDGET_1_ID = 23; - public static final int KEY_WIDGET_2_ID = 24; + private static final int MESSAGE_KEY_WeatherCondition = 10000; + private static final int MESSAGE_KEY_WeatherForecastCondition = 10002; + private static final int MESSAGE_KEY_WeatherForecastHighTemp = 10003; + private static final int MESSAGE_KEY_WeatherForecastLowTemp = 10004; + private static final int MESSAGE_KEY_WeatherTemperature = 10001; + private static final int MESSAGE_KEY_WeatherUseNightIcon = 10025; private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerTimeStylePebble.class); @@ -51,62 +28,35 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { super(uuid, pebbleProtocol); } - private byte[] encodeTimeStylePebbleConfig() { - ArrayList> pairs = new ArrayList<>(); - //settings that give good legibility on pebble time - pairs.add(new Pair<>(KEY_SETTING_SIDEBAR_LEFT, (Object) 1)); - pairs.add(new Pair<>(KEY_SETTING_CLOCK_FONT_ID, (Object) 1)); - pairs.add(new Pair<>(KEY_SETTING_COLOR_BG, (Object) Color.parseColor("#ffffff"))); - pairs.add(new Pair<>(KEY_SETTING_COLOR_SIDEBAR, (Object) Color.parseColor("#00aaff"))); - pairs.add(new Pair<>(KEY_SETTING_COLOR_TIME, (Object) Color.parseColor("#000000"))); - pairs.add(new Pair<>(KEY_SETTING_SHOW_LEADING_ZERO, (Object) 1)); - pairs.add(new Pair<>(KEY_SETTING_LANGUAGE_ID, (Object) 2)); //2 = Deutsch - pairs.add(new Pair<>(KEY_SETTING_USE_METRIC, (Object) 1)); - pairs.add(new Pair<>(KEY_SETTING_SHOW_BATTERY_PCT, (Object) 1)); - - pairs.add(new Pair<>(KEY_WIDGET_0_ID, (Object) 7)); //7 = current weather - pairs.add(new Pair<>(KEY_WIDGET_1_ID, (Object) 2)); //2 = battery - pairs.add(new Pair<>(KEY_WIDGET_2_ID, (Object) 4)); //4 = Date - - byte[] ackMessage = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - byte[] testMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); - - byte[] weatherMessage=encodeTimeStylePebbleWeather(); - ByteBuffer buf = ByteBuffer.allocate(ackMessage.length + testMessage.length + weatherMessage.length); - - // encode ack and put in front of push message (hack for acknowledging the last message) - buf.put(ackMessage); - buf.put(testMessage); - buf.put(weatherMessage); - - return buf.array(); - } - private byte[] encodeTimeStylePebbleWeather() { ArrayList> pairs = new ArrayList<>(); ParcelableWeather2 weather = Weather.getInstance().getWeather2(); if (weather != null) { //TODO: use the night icons when night - pairs.add(new Pair<>(KEY_USE_NIGHT_ICON, (Object) 0)); - pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) (weather.currentTemp - 273))); - pairs.add(new Pair<>(KEY_CONDITION_CODE, (Object) Weather.mapToYahooCondition(weather.currentConditionCode))); - pairs.add(new Pair<>(KEY_FORECAST_CONDITION, (Object) Weather.mapToYahooCondition(weather.forecastConditionCode))); - pairs.add(new Pair<>(KEY_FORECAST_TEMP_HIGH, (Object) (weather.highTemp - 273))); - pairs.add(new Pair<>(KEY_FORECAST_TEMP_LOW, (Object) (weather.lowTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) 0)); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherTemperature, (Object) (weather.currentTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) Weather.mapToYahooCondition(weather.currentConditionCode))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) Weather.mapToYahooCondition(weather.forecastConditionCode))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weather.highTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weather.lowTemp - 273))); } - byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); - return weatherMessage; + return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); } @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - return null; - /* - GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeTimeStylePebbleConfig(); - return new GBDeviceEvent[]{sendBytes}; - */ + return pushMessage(); } -} + + @Override + public GBDeviceEvent[] pushMessage() { + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeTimeStylePebbleWeather(); + return new GBDeviceEvent[]{sendBytesAck, sendBytes}; + } +} \ No newline at end of file From a6a2c6d6d66f53892c0bd43addd94ecac76996bc Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 10 Dec 2016 15:09:22 +0100 Subject: [PATCH 017/244] Pebble: timestyle doesn't use Yahoo anymore. The values do not work anyway, because it's mapping them internally. See https://github.com/freakified/TimeStylePebble/tree/master/src/pkjs for the mappings. --- .../devices/pebble/AppMessageHandlerTimeStylePebble.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index ffb58076..4d31c4b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -34,10 +34,10 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { if (weather != null) { //TODO: use the night icons when night - pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) 0)); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) 1)); pairs.add(new Pair<>(MESSAGE_KEY_WeatherTemperature, (Object) (weather.currentTemp - 273))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) Weather.mapToYahooCondition(weather.currentConditionCode))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) Weather.mapToYahooCondition(weather.forecastConditionCode))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) (weather.currentConditionCode))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) (weather.forecastConditionCode))); pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weather.highTemp - 273))); pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weather.lowTemp - 273))); } From f63a7db5f98c453c81449f471761bf62571e25a0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 11 Dec 2016 00:08:57 +0100 Subject: [PATCH 018/244] Pebble: map owm conditions to TimeStyle icons This is probably not the way we should do it, just experimenting for personal use :P --- .../AppMessageHandlerTimeStylePebble.java | 75 ++++++++++++++++++- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 4d31c4b8..d20aa615 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -22,22 +22,89 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { private static final int MESSAGE_KEY_WeatherUseNightIcon = 10025; + private static final int ICON_CLEAR_DAY = 0; + private static final int ICON_CLEAR_NIGHT = 1; + private static final int ICON_CLOUDY_DAY = 2; + private static final int ICON_HEAVY_RAIN = 3; + private static final int ICON_HEAVY_SNOW = 4; + private static final int ICON_LIGHT_RAIN = 5; + private static final int ICON_LIGHT_SNOW = 6; + private static final int ICON_PARTLY_CLOUDY_NIGHT = 7; + private static final int ICON_PARTLY_CLOUDY = 8; + private static final int ICON_RAINING_AND_SNOWING = 9; + private static final int ICON_THUNDERSTORM = 10; + private static final int ICON_WEATHER_GENERIC = 11; + private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerTimeStylePebble.class); public AppMessageHandlerTimeStylePebble(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); } + /* + * converted to JAVA from original JS + */ + private int getIconForConditionCode(int conditionCode, boolean isNight) { + int generalCondition = conditionCode / 100; + int iconToLoad; + // determine the correct icon + switch (generalCondition) { + case 2: //thunderstorm + iconToLoad = ICON_THUNDERSTORM; + break; + case 3: //drizzle + iconToLoad = ICON_LIGHT_RAIN; + break; + case 5: //rain + if (conditionCode == 500) { + iconToLoad = ICON_LIGHT_RAIN; + } else if (conditionCode < 505) { + iconToLoad = ICON_HEAVY_RAIN; + } else if (conditionCode == 511) { + iconToLoad = ICON_RAINING_AND_SNOWING; + } else { + iconToLoad = ICON_LIGHT_RAIN; + } + break; + case 6: //snow + if (conditionCode == 600 || conditionCode == 620) { + iconToLoad = ICON_LIGHT_SNOW; + } else if (conditionCode > 610 && conditionCode < 620) { + iconToLoad = ICON_RAINING_AND_SNOWING; + } else { + iconToLoad = ICON_HEAVY_SNOW; + } + break; + case 7: // fog, dust, etc + iconToLoad = ICON_CLOUDY_DAY; + break; + case 8: // clouds + if (conditionCode == 800) { + iconToLoad = (!isNight) ? ICON_CLEAR_DAY : ICON_CLEAR_NIGHT; + } else if (conditionCode < 803) { + iconToLoad = (!isNight) ? ICON_PARTLY_CLOUDY : ICON_PARTLY_CLOUDY_NIGHT; + } else { + iconToLoad = ICON_CLOUDY_DAY; + } + break; + default: + iconToLoad = ICON_WEATHER_GENERIC; + break; + } + + return iconToLoad; + } + private byte[] encodeTimeStylePebbleWeather() { ArrayList> pairs = new ArrayList<>(); ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + boolean isNight = false; //TODO: use the night icons when night if (weather != null) { - //TODO: use the night icons when night - pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) 1)); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) (isNight ? 1 : 0))); pairs.add(new Pair<>(MESSAGE_KEY_WeatherTemperature, (Object) (weather.currentTemp - 273))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) (weather.currentConditionCode))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) (weather.forecastConditionCode))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) (getIconForConditionCode(weather.currentConditionCode, isNight)))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) (getIconForConditionCode(weather.forecastConditionCode, isNight)))); pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weather.highTemp - 273))); pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weather.lowTemp - 273))); } From 2b3592f35408b107e5f6d9c2792351bee6c82791 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 16 Dec 2016 23:47:59 +0100 Subject: [PATCH 019/244] Pebble: allow sending data to the pebble during installation on FW >= 3.0 It seems to be no problem to recieve notifications etc during app installation with non legacy firmwares :) --- .../service/devices/pebble/PebbleIoThread.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 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 45035477..8bbc5b57 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 @@ -486,10 +486,11 @@ class PebbleIoThread extends GBDeviceIoThread { if (bytes == null) { return; } - // block writes if app installation in in progress - if (mIsConnected && (!mIsInstalling || mInstallState == PebbleAppInstallState.WAIT_SLOT)) { - write_real(bytes); + // on FW < 3.0 block writes if app installation in in progress + if (!mIsConnected || (mPebbleProtocol.mFwMajor < 3 && mIsInstalling && mInstallState != PebbleAppInstallState.WAIT_SLOT)) { + return; } + write_real(bytes); } // FIXME: parts are supporsed to be generic code @@ -500,7 +501,7 @@ class PebbleIoThread extends GBDeviceIoThread { LOG.info("syncing time"); write(mPebbleProtocol.encodeSetTime()); } - write(mPebbleProtocol.encodeEnableAppLogs(prefs.getBoolean("pebble_enable_applogs",false))); + write(mPebbleProtocol.encodeEnableAppLogs(prefs.getBoolean("pebble_enable_applogs", false))); write(mPebbleProtocol.encodeReportDataLogSessions()); gbDevice.setState(GBDevice.State.INITIALIZED); return false; @@ -603,10 +604,6 @@ class PebbleIoThread extends GBDeviceIoThread { } void installApp(Uri uri, int appId) { - if (mIsInstalling) { - return; - } - if (uri.equals(Uri.parse("fake://health"))) { write(mPebbleProtocol.encodeActivateHealth(true)); write(mPebbleProtocol.encodeSetSaneDistanceUnit(true)); @@ -617,6 +614,10 @@ class PebbleIoThread extends GBDeviceIoThread { return; } + if (mIsInstalling) { + return; + } + String platformName = PebbleUtils.getPlatformName(gbDevice.getModel()); try { From 321298e08a6cca66b633575f56e0467f826870cc Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 17 Dec 2016 00:00:31 +0100 Subject: [PATCH 020/244] update CONTRIBUTORS.md (manually remove duplicates) --- CONTRIBUTORS.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1637e814..e876751d 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,27 +1,38 @@ Andreas Shimokawa - cpfeiffer - Daniele Gobbetti - Daniele Gobbetti - danielegobbetti Carsten Pfeiffer + Daniele Gobbetti Julien Pivotto + Steffen Liebergeld Lem Dulfo Sergey Trofimov - Daniele Gobbetti - cpfeiffer + JohnnySun + Uwe Hermann 0nse <0nse@users.noreply.github.com> + Gergely Peidl Christian Fischer Normano64 Ⲇⲁⲛⲓ Φi + xzovy xphnx Tarik Sekmen + Szymon Tomasz Stefanek + Roman Plevka rober Nicolò Balzarotti + Natanael Arndt Marc Schlaich kevlarcade + Kevin Richter Kasha + Ivan + Gilles MOREL + Gilles Émilien MOREL Chris Perelstein + Carlos Ferreira + atkyritsis + andre Alexey Afanasev + 6arms1leg And all the Transifex translators, which I cannot automatically list, at the moment. From 97aed43518786bf8c1441f81b6d63014fba2c462 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 17 Dec 2016 00:17:05 +0100 Subject: [PATCH 021/244] Pebble: fix for previous commit (potential crashes when installing and receiving notifications) --- .../gadgetbridge/service/devices/pebble/PebbleIoThread.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8bbc5b57..bd990d2d 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 @@ -595,7 +595,7 @@ class PebbleIoThread extends GBDeviceIoThread { } } - private void writeInstallApp(byte[] bytes) { + synchronized private void writeInstallApp(byte[] bytes) { if (!mIsInstalling) { return; } From 1f1a34cf258f50ad30e1855d2d73c25448f2844e Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 17 Dec 2016 18:16:28 +0100 Subject: [PATCH 022/244] Replace the contributors.md file with a self-generating contributors.rst file. The contributors.rst file is a bash quine originally found here https://gist.github.com/danielegobbetti/c691740ec6f815c75c065049fdc35243 --- CONTRIBUTORS.md | 39 ------------------------------ CONTRIBUTORS.rst | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 39 deletions(-) delete mode 100644 CONTRIBUTORS.md create mode 100644 CONTRIBUTORS.rst diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md deleted file mode 100644 index e876751d..00000000 --- a/CONTRIBUTORS.md +++ /dev/null @@ -1,39 +0,0 @@ - Andreas Shimokawa - Carsten Pfeiffer - Daniele Gobbetti - Julien Pivotto - Steffen Liebergeld - Lem Dulfo - Sergey Trofimov - JohnnySun - Uwe Hermann - 0nse <0nse@users.noreply.github.com> - Gergely Peidl - Christian Fischer - Normano64 - Ⲇⲁⲛⲓ Φi - xzovy - xphnx - Tarik Sekmen - Szymon Tomasz Stefanek - Roman Plevka - rober - Nicolò Balzarotti - Natanael Arndt - Marc Schlaich - kevlarcade - Kevin Richter - Kasha - Ivan - Gilles MOREL - Gilles Émilien MOREL - Chris Perelstein - Carlos Ferreira - atkyritsis - andre - Alexey Afanasev - 6arms1leg - -And all the Transifex translators, which I cannot automatically list, at the moment. - -git log --raw | grep "^Author: " | sort | uniq -c | sort -k 1 -n -r | cut -f 2- -d: > CONTRIBUTORS.md diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst new file mode 100644 index 00000000..e6698fda --- /dev/null +++ b/CONTRIBUTORS.rst @@ -0,0 +1,63 @@ +.. 2>/dev/null + names () + { + echo -e "\n exit;\n**Contributors (sorted by number of commits):**\n"; + git log --all --format='%aN:%aE' | sed 's/@users.github.com/@users.noreply.github.com/g' | awk 'BEGIN{FS=":"}{ct[$2]+=1;if (length($1) > length(e[$2])) {e[$2]=$1}}END{for (i in e) { n[e[i]]=i;c[e[i]]+=ct[i] }; for (a in n) print c[a]"\t* "a" <"n[a]">";}' | sort -n -r | cut -f 2- + } + quine () + { + { + echo ".. 2>/dev/null"; + declare -f names | sed -e 's/^[[:space:]]*/ /'; + declare -f quine | sed -e 's/^[[:space:]]*/ /'; + echo -e " quine\n"; + names; + echo -e "\nAnd all the Transifex translators, which I cannot automatically list, at the moment.\n\n*To update the contributors list just run this file with bash*" + } > CONTRIBUTORS.rst; + exit + } + quine + + + exit; +**Contributors (sorted by number of commits):** + +* Andreas Shimokawa +* Carsten Pfeiffer +* Daniele Gobbetti +* Julien Pivotto +* Steffen Liebergeld +* Lem Dulfo +* Sergey Trofimov +* JohnnySun +* Uwe Hermann +* Gergely Peidl +* 0nse <0nse@users.noreply.github.com> +* Christian Fischer +* Normano64 +* Ⲇⲁⲛⲓ Φi +* xzovy +* xphnx +* Tarik Sekmen +* Szymon Tomasz Stefanek +* Roman Plevka +* rober +* Nicolò Balzarotti +* Natanael Arndt +* Marc Schlaich +* kevlarcade +* Kevin Richter +* Kasha +* Ivan +* Gilles MOREL +* Gilles Émilien MOREL +* Chris Perelstein +* Carlos Ferreira +* atkyritsis +* andre +* Alexey Afanasev +* 6arms1leg + +And all the Transifex translators, which I cannot automatically list, at the moment. + +*To update the contributors list just run this file with bash* From 861c655b5d864455ec37b5602038fb0c4fad3b76 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 18 Dec 2016 10:40:30 +0100 Subject: [PATCH 023/244] Log the incoming notification flags. Should help further diagnosing #456 --- .../gadgetbridge/externalevents/NotificationListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 0c29c664..b8208441 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -197,12 +197,12 @@ public class NotificationListener extends NotificationListenerService { //don't forward group summary notifications to the wearable, they are meant for the android device only if ((notification.flags & Notification.FLAG_GROUP_SUMMARY) == Notification.FLAG_GROUP_SUMMARY) { - LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set"); + LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set. Notification flags: " + notification.flags); return; } if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) { - LOG.info("Not forwarding notification, FLAG_ONGOING_EVENT is set"); + LOG.info("Not forwarding notification, FLAG_ONGOING_EVENT is set. Notification flags: " + notification.flags); return; } From f1965c7b009efa485c8d0e50bbc92433ce6f4468 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 18 Dec 2016 11:03:54 +0100 Subject: [PATCH 024/244] Log the flags also when passing the notification to device #456 --- .../gadgetbridge/externalevents/NotificationListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index b8208441..5264422a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -258,7 +258,7 @@ public class NotificationListener extends NotificationListenerService { notificationSpec.type = AppNotificationType.getInstance().get(source); - LOG.info("Processing notification from source " + source); + LOG.info("Processing notification from source " + source + " with flags: " + notification.flags); dissectNotificationTo(notification, notificationSpec, preferBigText); notificationSpec.id = (int) sbn.getPostTime(); //FIMXE: a truly unique id would be better From 846c74aa86370fb88cb21445aeb8398757bdbd7c Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 18 Dec 2016 18:00:16 +0100 Subject: [PATCH 025/244] Forward also group summary notifications, if they contain wearable actions. Hopefully helps with #456 Tested with conversations and there is no regression. Also a specific workaround for telegram was removed, hopefully this does not break #395 --- .../externalevents/NotificationListener.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 5264422a..c3cd2b3c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -34,7 +34,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -190,19 +189,13 @@ public class NotificationListener extends NotificationListenerService { if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) { PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE); if (powermanager.isScreenOn()) { - LOG.info("Not forwarding notification, screen seems to be on and settings do not allow this"); +// LOG.info("Not forwarding notification, screen seems to be on and settings do not allow this"); return; } } - //don't forward group summary notifications to the wearable, they are meant for the android device only - if ((notification.flags & Notification.FLAG_GROUP_SUMMARY) == Notification.FLAG_GROUP_SUMMARY) { - LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set. Notification flags: " + notification.flags); - return; - } - if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) { - LOG.info("Not forwarding notification, FLAG_ONGOING_EVENT is set. Notification flags: " + notification.flags); +// LOG.info("Not forwarding notification, FLAG_ONGOING_EVENT is set. Notification flags: " + notification.flags); return; } @@ -266,8 +259,9 @@ public class NotificationListener extends NotificationListenerService { NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification); List actions = wearableExtender.getActions(); - if (actions.isEmpty() && notificationSpec.type == NotificationType.TELEGRAM) { - return; // workaround for duplicate telegram message + if (actions.isEmpty() && (notification.flags & Notification.FLAG_GROUP_SUMMARY) == Notification.FLAG_GROUP_SUMMARY) { //this could cause #395 to come back + LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set and no wearable action present. Notification flags: " + notification.flags); + return; } for (NotificationCompat.Action act : actions) { From 771ca948a4caa88acaa6f48adb69f90ba8ca3529 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Mon, 19 Dec 2016 18:20:32 +0100 Subject: [PATCH 026/244] Add changelog regarding #456 --- CHANGELOG.md | 3 +++ app/src/main/res/xml/changelog_master.xml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c8ebdb..ec7726c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version next +* Improved handling of notifications for some apps + ####Version 0.15.0 * New device: Liveview * Liveview: initial support (set the time and receive notifications) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 4f1ec60e..5141cf20 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + * Improved handling of notifications for some apps + New device: Liveview Liveview: initial support (set the time and receive notifications) From bd5dc6bfbc037da63a45fa755aa029ee5765b997 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 19 Dec 2016 23:28:06 +0100 Subject: [PATCH 027/244] Pebble 2/LE: Add setting to limit the MTU (for debugging broken BLE stacks) --- .../service/devices/pebble/ble/PebbleLESupport.java | 8 +++++++- app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/preferences.xml | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) 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 6fa3d2c2..883cc13d 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 @@ -10,6 +10,8 @@ import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; + public class PebbleLESupport { private static final Logger LOG = LoggerFactory.getLogger(PebbleLESupport.class); private final BluetoothDevice mBtDevice; @@ -19,6 +21,7 @@ public class PebbleLESupport { private PipedInputStream mPipedInputStream; private PipedOutputStream mPipedOutputStream; private int mMTU = 20; + private int mMTULimit = Integer.MAX_VALUE; boolean mIsConnected = false; public PebbleLESupport(Context context, final BluetoothDevice btDevice, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) throws IOException { @@ -31,6 +34,9 @@ public class PebbleLESupport { } catch (IOException e) { LOG.warn("could not connect input stream"); } + mMTULimit = GBApplication.getPrefs().getInt("pebble_mtu_limit", 512); + mMTULimit = Math.max(mMTULimit, 20); + mMTULimit = Math.min(mMTULimit, 512); mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice); if (mPebbleGATTServer.initialize()) { @@ -99,7 +105,7 @@ public class PebbleLESupport { } void setMTU(int mtu) { - mMTU = mtu; + mMTU = Math.min(mtu, mMTULimit); } private class PipeReader extends Thread { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09c215ed..a9d2e3e2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -120,6 +120,8 @@ 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 + Pebble 2/LE GATT MTU limit + If your Pebble 2/Pebble LE does not work as expected, try this setting to limit the MTU (valid range 20–512) Enable Watch App Logging Will cause logs from watch apps to be logged by Gadgetbridge (requires reconnect) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index efe3e4c7..b721a88f 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -341,6 +341,13 @@ android:key="pebble_force_le" android:summary="@string/pref_summary_pebble_forcele" android:title="@string/pref_title_pebble_forcele" /> + Date: Mon, 19 Dec 2016 23:40:12 +0100 Subject: [PATCH 028/244] bump version, update changelogs --- CHANGELOG.md | 3 ++- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec7726c6..67a75481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ###Changelog -####Version next +####Version 0.15.1 * Improved handling of notifications for some apps +* Pebble 2/LE: Add setting to limit GATT MTU for debugging broken BLE stacks ####Version 0.15.0 * New device: Liveview diff --git a/app/build.gradle b/app/build.gradle index 573105c7..92a026ec 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.15.0" - versionCode 77 + versionName "0.15.1" + versionCode 78 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 5141cf20..7af99208 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,7 +1,8 @@ - - * Improved handling of notifications for some apps + + Improved handling of notifications for some apps + Pebble 2/LE: Add setting to limit GATT MTU for debugging broken BLE stacks New device: Liveview From 2b777ecba9c6134d58cfc931e59313e26264d063 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 20 Dec 2016 22:58:16 +0100 Subject: [PATCH 029/244] update Spanish from transifex (THANKS) --- app/src/main/res/values-es/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3214d171..b0a40f60 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -98,6 +98,8 @@ Habilita características que no han sido probadas. ¡HABILÍTALO SOLO SI SABES LO QUE ESTÁS HACIENDO! Preferir siempre BLE Usar el soporte experimental de Pebble LE para todos los Pebble en lugar del bluetooth clásico. Requiere vincular \"Pebble LE\" si un Pebble no-LE ha sido vinculado antes. + Pebble 2/LE límite de GATT MTU + Si su Pebble 2/Pebble LE no funciona correctamente, pruebe esta opción para limitar el MTU (rango válido 20–512) Activar crear registros de la App del Reloj Producirá registros de las apps del reloj que Gadgetbridge guardará (necesita reconexión) Intentos de reconexión From 5b3ef8999f5270dd576aab9ea406a0ce52082251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 21 Dec 2016 12:51:25 +0000 Subject: [PATCH 030/244] Add preliminary support for HPlus devices, such as the Zeblaze Zeband (and many others) Working: Text and call notifications, setting most user data, date and time, heart rate monitoring, sleep monitoring (alfa) --- .../gadgetbridge/daogen/GBDaoGenerator.java | 12 + .../gadgetbridge/devices/SampleProvider.java | 1 + .../devices/hplus/HPlusConstants.java | 82 ++ .../devices/hplus/HPlusCoordinator.java | 202 +++++ .../devices/hplus/HPlusSampleProvider.java | 140 +++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 4 + .../devices/hplus/HPlusSleepRecord.java | 86 ++ .../service/devices/hplus/HPlusSupport.java | 802 ++++++++++++++++++ .../gadgetbridge/util/DeviceHelper.java | 2 + 10 files changed, 1332 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 63e5d713..223404ce 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -60,6 +60,7 @@ public class GBDaoGenerator { addPebbleHealthActivityKindOverlay(schema, user, device); addPebbleMisfitActivitySample(schema, user, device); addPebbleMorpheuzActivitySample(schema, user, device); + addHPlusHealthActivitySample(schema, user, device); new DaoGenerator().generateAll(schema, "app/src/main/java"); } @@ -221,6 +222,17 @@ public class GBDaoGenerator { return activitySample; } + private static Entity addHPlusHealthActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "HPlusHealthActivitySample"); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addByteArrayProperty("rawHPlusHealthData"); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + return activitySample; + } + private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) { activitySample.setSuperclass(superClass); activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider"); 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 fca1710e..3dafa35a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -24,6 +24,7 @@ public interface SampleProvider { int PROVIDER_PEBBLE_MISFIT = 3; int PROVIDER_PEBBLE_HEALTH = 4; int PROVIDER_MIBAND2 = 5; + int PROVIDER_HPLUS = 6; int PROVIDER_UNKNOWN = 100; // TODO: can also be removed diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java new file mode 100644 index 00000000..a24b1e86 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -0,0 +1,82 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + +import java.util.UUID; + +/** + * Message constants reverse-engineered by João Paulo Barraca, jpbarraca@gmail.com. + * + * @author João Paulo Barraca <jpbarraca@gmail.com> + */ +public final class HPlusConstants { + + public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("14702856-620a-3973-7c78-9cfff0876abd"); + public static final UUID UUID_CHARACTERISTIC_MEASURE = UUID.fromString("14702853-620a-3973-7c78-9cfff0876abd"); + public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); + + + public static final byte COUNTRY_CN = 1; + public static final byte COUNTRY_OTHER = 2; + + public static final byte CLOCK_24H = 0; + public static final byte CLOCK_12H = 1; + + public static final byte UNIT_METRIC = 0; + public static final byte UNIT_IMPERIAL = 1; + + public static final byte SEX_MALE = 0; + public static final byte SEX_FEMALE = 1; + + public static final byte HEARTRATE_MEASURE_ON = 11; + public static final byte HEARTRATE_MEASURE_OFF = 22; + + public static final byte HEARTRATE_ALLDAY_ON = 10; + public static final byte HEARTRATE_ALLDAY_OFF = -1; + + public static final byte[] COMMAND_SET_INIT1 = new byte[]{0x50,0x00,0x25,(byte) 0xb1,0x4a,0x00,0x00,0x27,0x10,0x05,0x02,0x00,(byte) 0xff,0x0a,(byte) 0xff,0x00,(byte) 0xff,(byte) 0xff,0x00,0x01}; + public static final byte[] COMMAND_SET_INIT2 = new byte[]{0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,(byte) 0xe0,0x0c,0x12,0x16,0x0a,0x10,0x00,0x00,0x00,0x00}; + + public static final byte[] COMMAND_SET_PREF_START = new byte[]{0x4f, 0x5a}; + public static final byte[] COMMAND_SET_PREF_START1 = new byte[]{0x4d}; + + public static final byte COMMAND_SET_PREF_COUNTRY = 0x22; + public static final byte COMMAND_SET_PREF_TIMEMODE = 0x47; + public static final byte COMMAND_SET_PREF_UNIT = 0x48; + public static final byte COMMAND_SET_PREF_SEX = 0x2d; + + public static final byte COMMAND_SET_PREF_DATE = 0x08; + public static final byte COMMAND_SET_PREF_TIME = 0x09; + public static final byte COMMAND_SET_PREF_WEEK = 0x2a; + public static final byte COMMAND_SET_PREF_SIT = 0x1e; + public static final byte COMMAND_SET_PREF_WEIGHT = 0x05; + public static final byte COMMAND_SET_PREF_HEIGHT = 0x04; + public static final byte COMMAND_SET_PREF_AGE = 0x2c; + public static final byte COMMAND_SET_PREF_GOAL = 0x26; + public static final byte COMMAND_SET_PREF_SCREENTIME = 0x0b; + public static final byte COMMAND_SET_PREF_BLOOD = 0x4e; //?? + public static final byte COMMAND_SET_PREF_FINDME = 0x0a; + public static final byte COMMAND_SET_PREF_SAVE = 0x17; + public static final byte COMMAND_SET_PREF_END = 0x4f; + public static final byte COMMAND_SET_INCOMMING_SOCIAL = 0x31; + public static final byte COMMAND_SET_INCOMMING_SMS = 0x40; + public static final byte COMMAND_SET_DISPLAY_TEXT = 0x43; + public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; + public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; + + public static final byte COMMAND_SET_INCOMMING_CALL = 65; + public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; + + public static final byte COMMAND_SET_CONF_SAVE = 0x17; + public static final byte COMMAND_SET_CONF_END = 0x4f; + + + public static final byte DATA_STATS = 0x33; + public static final byte DATA_SLEEP = 0x1A; + + + public static final String PREF_HPLUS_USER_ALIAS = "hplus_user_alias"; + public static final String PREF_HPLUS_FITNESS_GOAL = "hplus_fitness_goal"; + public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; + public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; + public static final String PREF_HPLUS_UNIT = "hplus_unit"; + public static final String PREF_HPLUS_TIMEMODE = "hplus_timemode"; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java new file mode 100644 index 00000000..4eceef13 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -0,0 +1,202 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.UserInfo; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HPlusCoordinator extends AbstractDeviceCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); + private static Prefs prefs = GBApplication.getPrefs(); + + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + LOG.debug("Looking for: " + name); + if (name != null && name.startsWith("HPLUS")) { + return DeviceType.HPLUS; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.HPLUS; + } + + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public Class getPrimaryActivity() { + return ChartsActivity.class; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return true; + } + + @Override + public boolean supportsActivityTracking() { + return true; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return new HPlusSampleProvider(device, session); + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsAlarmConfiguration() { + return true; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return true; + } + + @Override + public int getTapString() { + return R.string.tap_connected_device_for_activity; + } + + @Override + public String getManufacturer() { + return "Zeblaze"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + // nothing to delete, yet + } + + public static int getFitnessGoal(String address) throws IllegalArgumentException { + Prefs prefs = GBApplication.getPrefs(); + return prefs.getInt(HPlusConstants.PREF_HPLUS_FITNESS_GOAL + "_" + address, 10000); + } + + /** + * Returns the user info from the user configured data in the preferences. + * + * @param hplusAddress + * @throws IllegalArgumentException when the user info can not be created + */ + public static UserInfo getConfiguredUserInfo(String hplusAddress) throws IllegalArgumentException { + ActivityUser activityUser = new ActivityUser(); + + UserInfo info = UserInfo.create( + hplusAddress, + prefs.getString(HPlusConstants.PREF_HPLUS_USER_ALIAS, null), + activityUser.getGender(), + activityUser.getAge(), + activityUser.getHeightCm(), + activityUser.getWeightKg(), + 0 + ); + return info; + } + + public static byte getCountry(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); + + } + + public static byte getTimeMode(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_TIMEMODE + "_" + address, 0); + } + + public static byte getUnit(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_UNIT + "_" + address, 0); + } + + public static byte getUserWeight(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) activityUser.getWeightKg(); + } + + public static byte getUserHeight(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) activityUser.getHeightCm(); + } + + public static byte getUserAge(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) activityUser.getAge(); + } + + public static byte getUserSex(String address) { + ActivityUser activityUser = new ActivityUser(); + + int gender = activityUser.getGender(); + + return (byte) gender; + + } + + public static int getGoal(String address) { + ActivityUser activityUser = new ActivityUser(); + + return activityUser.getStepsGoal(); + } + + public static byte getScreenTime(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME + "_" + address, 5); + + } + + public static byte getAllDayHR(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); + + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java new file mode 100644 index 00000000..67b535a3 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java @@ -0,0 +1,140 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import android.content.Context; +import android.support.annotation.NonNull; + +import java.util.Collections; +import java.util.List; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.query.QueryBuilder; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public class HPlusSampleProvider extends AbstractSampleProvider { + + public static final int TYPE_DEEP_SLEEP = 4; + 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; + + private GBDevice mDevice; + private DaoSession mSession; + + public HPlusSampleProvider(GBDevice device, DaoSession session) { + super(device, session); + + mSession = session; + mDevice = device;; + } + + public int getID() { + return SampleProvider.PROVIDER_HPLUS; + } + + 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; + } + } + + 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; + } + } + + + @Override + public List getAllActivitySamples(int timestamp_from, int timestamp_to) { + List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); + + Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); + if (dbDevice == null) { + // no device, no samples + return Collections.emptyList(); + } + + QueryBuilder qb = getSession().getHPlusHealthActivitySampleDao().queryBuilder(); + + qb.where(HPlusHealthActivitySampleDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivitySampleDao.Properties.Timestamp.ge(timestamp_from)) + .where(HPlusHealthActivitySampleDao.Properties.Timestamp.le(timestamp_to)); + + List sampleList = qb.build().list(); + + for (HPlusHealthActivitySample sample : sampleList) { + if (timestamp_from <= sample.getTimestamp() && sample.getTimestamp() < timestamp_to) { + sample.setRawKind(sample.getRawKind()); + } + } + detachFromSession(); + return samples; + } + @NonNull + @Override + protected de.greenrobot.dao.Property getTimestampSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.Timestamp; + } + + @Override + public HPlusHealthActivitySample createActivitySample() { + return new HPlusHealthActivitySample(); + } + + @Override + protected de.greenrobot.dao.Property getRawKindSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.RawKind; + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity; //TODO: Calculate actual value + } + + @NonNull + @Override + protected de.greenrobot.dao.Property getDeviceIdentifierSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.DeviceId; + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getHPlusHealthActivitySampleDao(); + } +} 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 5c9d2663..882a8205 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -13,6 +13,7 @@ public enum DeviceType { MIBAND2(11), VIBRATISSIMO(20), LIVEVIEW(30), + HPLUS(40), TEST(1000); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 71c3dc8d..af25c8a7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Suppo import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class DeviceSupportFactory { @@ -96,6 +97,9 @@ public class DeviceSupportFactory { case LIVEVIEW: deviceSupport = new ServiceDeviceSupport(new LiveviewSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case HPLUS: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java new file mode 100644 index 00000000..bed5d581 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java @@ -0,0 +1,86 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; + + + +public class HPlusSleepRecord { + private long bedTimeStart; + private long bedTimeEnd; + private int deepSleepSeconds; + private int spindleSeconds; + private int remSleepSeconds; + private int wakeupTime; + private int wakeupCount; + private int enterSleepSeconds; + private byte[] rawData; + + HPlusSleepRecord(byte[] data) { + rawData = data; + int year = data[2] * 256 + data[1]; + int month = data[3]; + int day = data[4]; + + enterSleepSeconds = data[6] * 256 + data[5]; + spindleSeconds = data[8] * 256 + data[7]; + deepSleepSeconds = data[10] * 256 + data[9]; + remSleepSeconds = data[12] * 256 + data[11]; + wakeupTime = data[14] * 256 + data[13]; + wakeupCount = data[16] * 256 + data[15]; + int hour = data[17]; + int minute = data[18]; + + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month); + c.set(Calendar.DAY_OF_MONTH, day); + c.set(Calendar.HOUR, hour); + c.set(Calendar.MINUTE, minute); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + bedTimeStart = (c.getTimeInMillis() / 1000L); + bedTimeEnd = bedTimeStart + enterSleepSeconds + spindleSeconds + deepSleepSeconds + remSleepSeconds + wakeupTime; + } + + byte[] getRawData() { + + return rawData; + } + + public long getBedTimeStart() { + return bedTimeStart; + } + + public long getBedTimeEnd() { + return bedTimeEnd; + } + + public int getDeepSleepSeconds() { + return deepSleepSeconds; + } + + public int getSpindleSeconds() { + return spindleSeconds; + } + + public int getRemSleepSeconds() { + return remSleepSeconds; + } + + public int getWakeupTime() { + return wakeupTime; + } + + public int getWakeupCount() { + return wakeupCount; + } + + public int getEnterSleepSeconds() { + return enterSleepSeconds; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java new file mode 100644 index 00000000..7737b519 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -0,0 +1,802 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.support.v4.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +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.ConditionalWriteAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; + + +public class HPlusSupport extends AbstractBTLEDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(HPlusSupport.class); + + private BluetoothGattCharacteristic ctrlCharacteristic = null; + private BluetoothGattCharacteristic measureCharacteristic = null; + + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String s = intent.getAction(); + if (s.equals(DeviceInfoProfile.ACTION_DEVICE_INFO)) { + handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO)); + } + } + }; + + public HPlusSupport() { + super(LOG); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); + addSupportedService(HPlusConstants.UUID_SERVICE_HP); + + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + IntentFilter intentFilter = new IntentFilter(); + + broadcastManager.registerReceiver(mReceiver, intentFilter); + } + + @Override + public void dispose() { + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + broadcastManager.unregisterReceiver(mReceiver); + super.dispose(); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + + measureCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE); + ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + + + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); + + //Fill device info + requestDeviceInfo(builder); + + getDevice().setFirmwareVersion("0"); + getDevice().setFirmwareVersion2("0"); + + //Initialize device + setInitValues(builder); + + builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); + + UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + BluetoothGattDescriptor descriptor = measureCharacteristic.getDescriptor(uuid); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + + builder.setGattCallback(this); + builder.notify(measureCharacteristic, true); + + setInitialized(builder); + return builder; + } + + private HPlusSupport setInitValues(TransactionBuilder builder){ + LOG.debug("Set Init Values"); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT1); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT2); + return this; + } + + private HPlusSupport sendUserInfo(TransactionBuilder builder){ + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START1); + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_SAVE}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_END}); + return this; + } + + + private HPlusSupport setCountry(TransactionBuilder transaction) { + LOG.info("Attempting to set country..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setTimeMode(TransactionBuilder transaction) { + LOG.info("Attempting to set Time Mode..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIMEMODE, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setUnit(TransactionBuilder transaction) { + LOG.info("Attempting to set Units..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_UNIT, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setCurrentDate(TransactionBuilder transaction) { + LOG.info("Attempting to set Current Date..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + Calendar c = Calendar.getInstance(); + int year = c.get(Calendar.YEAR) - 1900; + int month = c.get(Calendar.MONTH); + int day = c.get(Calendar.DAY_OF_MONTH); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_DATE, + (byte) (year / 256), + (byte) (year % 256), + (byte) (month), + (byte) (day) + }; + } + }); + return this; + } + + private HPlusSupport setCurrentTime(TransactionBuilder transaction) { + LOG.info("Attempting to set Current Time..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + Calendar c = Calendar.getInstance(); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIME, + (byte) c.get(Calendar.HOUR_OF_DAY), + (byte) c.get(Calendar.MINUTE), + (byte) c.get(Calendar.SECOND) + }; + } + }); + return this; + } + + + private HPlusSupport setDayOfWeek(TransactionBuilder transaction) { + LOG.info("Attempting to set Day Of Week..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + Calendar c = Calendar.getInstance(); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEEK, + (byte) c.get(Calendar.DAY_OF_WEEK) + }; + } + }); + return this; + } + + + private HPlusSupport setSIT(TransactionBuilder transaction) { + LOG.info("Attempting to set SIT..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + Calendar c = Calendar.getInstance(); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SIT, + 0, 0, 0, 0, 0 + }; + } + }); + return this; + } + + private HPlusSupport setWeight(TransactionBuilder transaction) { + LOG.info("Attempting to set Weight..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEIGHT, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setHeight(TransactionBuilder transaction) { + LOG.info("Attempting to set Height..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_HEIGHT, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setAge(TransactionBuilder transaction) { + LOG.info("Attempting to set Age..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_AGE, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setSex(TransactionBuilder transaction) { + LOG.info("Attempting to set Sex..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserSex(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SEX, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setGoal(TransactionBuilder transaction) { + LOG.info("Attempting to set Sex..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + int value = HPlusCoordinator.getGoal(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_GOAL, + (byte) (value / 256), + (byte) (value % 256) + }; + } + }); + return this; + } + + + private HPlusSupport setScreenTime(TransactionBuilder transaction) { + LOG.info("Attempting to set Screentime..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SCREENTIME, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setAllDayHeart(TransactionBuilder transaction) { + LOG.info("Attempting to set All Day HR..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setAlarm(TransactionBuilder transaction) { + LOG.info("Attempting to set Alarm..."); + return this; + } + + private HPlusSupport setBlood(TransactionBuilder transaction) { + LOG.info("Attempting to set Blood..."); + return this; + } + + + private HPlusSupport setFindMe(TransactionBuilder transaction) { + LOG.info("Attempting to set Findme..."); + return this; + } + + private HPlusSupport requestDeviceInfo(TransactionBuilder builder) { + LOG.debug("Requesting Device Info!"); + BluetoothGattCharacteristic deviceName = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME); + builder.read(deviceName); + return this; + } + + private void setInitialized(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); + } + + + @Override + public boolean useAutoConnect() { + return true; + } + + @Override + public void pair() { + LOG.debug("Pair"); + } + + private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { + LOG.warn("Device info: " + info); + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + LOG.debug("Got Notification"); + showText(notificationSpec.body); + } + + + @Override + public void onSetTime() { + TransactionBuilder builder = new TransactionBuilder("vibration"); + setCurrentDate(builder); + setCurrentTime(builder); + + } + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onSetCallState(CallSpec callSpec) { + switch(callSpec.command){ + case CallSpec.CALL_INCOMING: { + showText(callSpec.name, callSpec.number); + break; + } + } + + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + LOG.debug("Canned Messages: "+cannedMessagesSpec); + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + + } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppInfoReq() { + + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + + } + + @Override + public void onAppDelete(UUID uuid) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config) { + + } + + @Override + public void onAppReorder(UUID[] uuids) { + + } + + @Override + public void onFetchActivityData() { + + } + + @Override + public void onReboot() { + + } + + @Override + public void onHeartRateTest() { + LOG.debug("On HeartRateTest"); + + getQueue().clear(); + + TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); + byte state = 0; + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, 0x10}); //Set Real Time... ? + builder.queue(getQueue()); + + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + LOG.debug("Set Real Time HR Measurement: " + enable); + + getQueue().clear(); + + TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); + byte state = 0; + + if(enable) + state = HPlusConstants.HEARTRATE_ALLDAY_ON; + else + state = HPlusConstants.HEARTRATE_ALLDAY_OFF; + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, state}); + builder.queue(getQueue()); + } + + @Override + public void onFindDevice(boolean start) { + LOG.debug("Find Me"); + + getQueue().clear(); + ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + + TransactionBuilder builder = new TransactionBuilder("findMe"); + + byte[] msg = new byte[2]; + msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; + + if(start) + msg[1] = 1; + else + msg[1] = 0; + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } + + @Override + public void onSetConstantVibration(int intensity) { + LOG.debug("Vibration Trigger"); + + getQueue().clear(); + + ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + + TransactionBuilder builder = new TransactionBuilder("vibration"); + + byte[] msg = new byte[15]; + msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; + + for(int i = 0;i 12) { + message = title.substring(0, 12); + }else { + message = title; + for(int i = message.length(); i < 12; i++) + message += ""; + } + } + message += body; + + int length = message.length() / 17; + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_INCOMMING_SOCIAL, (byte) length}); + + int remaining = 0; + + if(message.length() % 17 > 0) + remaining = length + 1; + else + remaining = length; + + msg[1] = (byte) remaining; + int message_index = 0; + int i = 3; + + for(int j=0; j < message.length(); j++){ + msg[i++] = (byte) message.charAt(j); + + if(i == msg.length){ + message_index ++; + msg[2] = (byte) message_index; + builder.write(ctrlCharacteristic, msg); + + msg = msg.clone(); + for(i=3; i < msg.length; i++) + msg[i] = 32; + + if(message_index < remaining) + i = 3; + else + break; + } + } + + msg[2] = (byte) remaining; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } + + public boolean isExpectedDevice(BluetoothDevice device) { + return true; + } + + public void close() { + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } + + UUID characteristicUUID = characteristic.getUuid(); + byte[] data = characteristic.getValue(); + if(data.length == 0) + return true; + + switch(data[0]){ + case HPlusConstants.DATA_STATS: + return processDataStats(data); + case HPlusConstants.DATA_SLEEP: + return processSleepStats(data); + default: + LOG.info("Unhandled characteristic changed: " + characteristicUUID); + + } + return false; + } + + private boolean processSleepStats(byte[] data){ + LOG.debug("Process Sleep Stats"); + + if(data.length < 19) { + LOG.error("Invalid Sleep Message Length " + data.length); + return false; + } + HPlusSleepRecord record = new HPlusSleepRecord(data); + + 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); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) + .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, record.getRawData() ) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + + }catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + + private boolean processDataStats(byte[]data){ + LOG.debug("Process Data Stats"); + + if(data.length < 15) { + LOG.error("Invalid Stats Message Length " + data.length); + return false; + } + double distance = ( (int) data[4] * 256 + data[3]) / 100.0; + + int x = (int) data[6] * 256 + data[5]; + int y = (int) data[8] * 256 + data[7]; + int calories = x + y; + + int bpm = (data[11] == -1) ? HPlusHealthActivitySample.NOT_MEASURED : data[11]; + + 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); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + + if (bpm != HPlusHealthActivitySample.NOT_MEASURED) { + HPlusHealthActivitySample sample = createActivitySample(device, user, ts, provider); + sample.setHeartRate(bpm); + provider.addGBActivitySample(sample); + } + }catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + + return true; + } + + public HPlusHealthActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { + HPlusHealthActivitySample sample = new HPlusHealthActivitySample(); + sample.setDevice(device); + sample.setUser(user); + sample.setTimestamp(timestampInSeconds); + sample.setProvider(provider); + + return sample; + } +} 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 0b0c6a8d..c57abb93 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -22,6 +22,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; @@ -167,6 +168,7 @@ public class DeviceHelper { result.add(new PebbleCoordinator()); result.add(new VibratissimoCoordinator()); result.add(new LiveviewCoordinator()); + result.add(new HPlusCoordinator()); return result; } From 119028827dca5f4d6d60bb29dedfb49460368644 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 21 Dec 2016 21:51:20 +0100 Subject: [PATCH 031/244] update Japanese from transifex (thanks) --- app/src/main/res/values-ja/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 21200fee..ddddedfa 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -98,6 +98,8 @@ テストされていない機能を有効にします。何をしているかわかっている場合のみ有効にしてください! 常に BLE を好みにする すべてのPebbleに対して、BT クラシックではなく実験的なPebble LEサポートを使用します。非LEで一度接続された後に \"Pebble LE\" をペアリングする必要があります + Pebble 2/LE GATT MTU 制限 + Pebble 2/Pebble LE が期待どおりに機能しない場合は、この設定を試して MTU を制限してください (有効範囲 20-512) ウォッチアプリのログ記録を有効にする Gadgetbridgeがウォッチアプリからログを記録するようにする (再接続が必要です) 再接続の試行 From 8c80146e16f37d20af9f0cad287f89c61f09fe19 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 21 Dec 2016 22:41:26 +0100 Subject: [PATCH 032/244] translate some German strings --- app/src/main/res/values-de/strings.xml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0f46a0a7..846d85b2 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -22,8 +22,11 @@ Löschen Löschen und aus dem Zwischenspeicher entfernen Erneut installieren + Im Pebble Appstore suchen aktivieren deaktivieren + HRM aktivieren + HRM deaktivieren Konfigurieren Nach oben @@ -85,12 +88,20 @@ Standort Bestimmen Breitengrad Längengrad + Automatisch Standort aktualisieren + Versuche den aktuellen Standort zur Laufzeit abzufragen und nutze die gespeicherten Standort falls das fehlschlägt Bitte ungefähre Standortbestimmung einschalten Standort wurde bestimmt Benachrichtigungsprotokoll erzwingen Diese Option erzwingt das neuste Benachrichtigungsprotokoll abhängig von der Firmwareversion. NUR EINSCHALTEN, WENN DU WEISST WAS DU TUST! Ungetestete Features freischalten Schaltet ungetetestete Features frei. TU DIES NUR, WENN DU WEIßT, WAS DU TUST! + BLE immer bevorzugen + Nutze den experimentellen LE support für alle Pebbles anstelle von BT classic. Setzt voraus, dass die \"Pebble LE\" gepaart wird, nachdem die nicht-LE Pebble einmal verbunden war. + Pebble 2/LE GATT MTU Limit + Wenn deine Pebble 2/Pebble LE nicht so wie erwartet funktioniert, versuche die MTU zu begrenzen (erlaubte Werte zwischen 20–512) + Watch App Logging einschalten + Schreibt logs von Watch Apps in Gadgetbridge logs (Pebble muss nach Ändern der Option erneut verbunden werden) Neuverbindungsversuche nicht verbunden verbinde @@ -126,7 +137,7 @@ Gerät paaren Verwende den Android Bluetooth Paaren-Dialog um Dein Gerät zu paaren. Paare Dein Mi Band - Paarung mit %s… + Pairing mit %s… Kein MAC Adresse bekommen, kann nicht paaren. Gerätespezifische Einstellungen Mi Band Einstellungen @@ -297,4 +308,5 @@ Wenn Du schon deine Daten importiert hast und mit dem Ergebnis zufrieden bist, k Löschen Vibration + From ae9ebc1be8782841752da1e20895de1d31a1291a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 21 Dec 2016 23:57:57 +0000 Subject: [PATCH 033/244] Refactoring some parts. Added support for param synchronisation with band --- .../gadgetbridge/daogen/GBDaoGenerator.java | 2 + .../devices/hplus/HPlusConstants.java | 18 +- .../devices/hplus/HPlusCoordinator.java | 36 ++- .../service/devices/hplus/HPlusSupport.java | 237 ++++++++++++------ 4 files changed, 210 insertions(+), 83 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 223404ce..e6cb8ddf 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -226,6 +226,8 @@ public class GBDaoGenerator { Entity activitySample = addEntity(schema, "HPlusHealthActivitySample"); addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); activitySample.addByteArrayProperty("rawHPlusHealthData"); + activitySample.addIntProperty("rawHPlusCalories").notNull(); + activitySample.addIntProperty("rawHPlusDistance").notNull(); activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index a24b1e86..17882caa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -56,9 +56,6 @@ public final class HPlusConstants { public static final byte COMMAND_SET_PREF_FINDME = 0x0a; public static final byte COMMAND_SET_PREF_SAVE = 0x17; public static final byte COMMAND_SET_PREF_END = 0x4f; - public static final byte COMMAND_SET_INCOMMING_SOCIAL = 0x31; - public static final byte COMMAND_SET_INCOMMING_SMS = 0x40; - public static final byte COMMAND_SET_DISPLAY_TEXT = 0x43; public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; @@ -68,10 +65,18 @@ public final class HPlusConstants { public static final byte COMMAND_SET_CONF_SAVE = 0x17; public static final byte COMMAND_SET_CONF_END = 0x4f; + public static final byte COMMAND_SET_PREFS = 0x50; + public static final byte COMMAND_SET_SIT_INTERVAL = 0x51; + + public static final byte DATA_STATS = 0x33; public static final byte DATA_SLEEP = 0x1A; + public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; + public static final byte COMMAND_ACTION_INCOMMING_SMS = 0x40; + public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; + public static final String PREF_HPLUS_USER_ALIAS = "hplus_user_alias"; public static final String PREF_HPLUS_FITNESS_GOAL = "hplus_fitness_goal"; @@ -79,4 +84,11 @@ public final class HPlusConstants { public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; public static final String PREF_HPLUS_UNIT = "hplus_unit"; public static final String PREF_HPLUS_TIMEMODE = "hplus_timemode"; + public static final String PREF_HPLUS_WRIST = "hplus_wrist"; + public static final String PREF_HPLUS_SWALERT = "hplus_sw_alert"; + public static final String PREF_HPLUS_ALERT_TIME = "hplus_alert_time"; + public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time"; + public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time"; + public static final String PREF_HPLUS_COUNTRY = "hplus_country"; + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 4eceef13..b90e20c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -145,7 +145,7 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getCountry(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_COUNTRY + "_" + address, 10); } @@ -199,4 +199,38 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); } + + public static byte getSocial(String address) { + + //TODO: Figure what this is. Returning the default value + + return (byte) 255; + } + + public static byte getUserWrist(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10); + + } + + public static boolean getSWAlertTime(String address) { + return (boolean) prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); + + + } + + public static int getAlertTime(String address) { + return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); + + } + + public static int getSITStartTime(String address) { + return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); + + } + + public static int getSITEndTime(String address) { + return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); + + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 7737b519..eca31a31 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -35,7 +35,9 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; @@ -99,14 +101,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); - //Fill device info - requestDeviceInfo(builder); - getDevice().setFirmwareVersion("0"); getDevice().setFirmwareVersion2("0"); //Initialize device setInitValues(builder); + syncPreferences(builder); builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); @@ -121,22 +121,78 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return builder; } - private HPlusSupport setInitValues(TransactionBuilder builder){ + private HPlusSupport setInitValues(TransactionBuilder builder) { LOG.debug("Set Init Values"); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT1); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT2); return this; } - private HPlusSupport sendUserInfo(TransactionBuilder builder){ + private HPlusSupport sendUserInfo(TransactionBuilder builder) { builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START1); + syncPreferences(builder); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_SAVE}); builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_END}); return this; } + private HPlusSupport syncPreferences(TransactionBuilder transaction) { + LOG.info("Attempting to sync preferences..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte sex = HPlusCoordinator.getUserSex(getDevice().getAddress()); + byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); + byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); + byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + byte country = HPlusCoordinator.getCountry(getDevice().getAddress()); + byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? + byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); + byte alertTimeHour = 0; + byte alertTimeMinute = 0; + + if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { + int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); + + alertTimeHour = (byte) (t / 256); + alertTimeMinute = (byte) (t % 256); + } + + byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); + byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + sex, + age, + bodyHeight, + bodyWeight, + 0, + 0, + (byte) (goal / 256), + (byte) (goal % 256), + displayTime, + country, + 0, + social, + allDayHeart, + wrist, + alertTimeHour, + alertTimeMinute, + unit, + timemode + }; + } + }); + return this; + } private HPlusSupport setCountry(TransactionBuilder transaction) { LOG.info("Attempting to set country..."); @@ -200,10 +256,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return new byte[]{ HPlusConstants.COMMAND_SET_PREF_DATE, - (byte) (year / 256), - (byte) (year % 256), - (byte) (month), - (byte) (day) + (byte) (year / 256), + (byte) (year % 256), + (byte) (month), + (byte) (day) }; } }); @@ -251,11 +307,30 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { @Override protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); + int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); + int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); + + Calendar now = Calendar.getInstance(); return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SIT, - 0, 0, 0, 0, 0 + HPlusConstants.COMMAND_SET_SIT_INTERVAL, + (byte) (startTime / 256), + (byte) (startTime % 256), + (byte) (endTime / 256), + (byte) (endTime % 256), + 0, + 0, + (byte) (now.get(Calendar.YEAR) / 256), + (byte) (now.get(Calendar.YEAR) % 256), + (byte) (now.get(Calendar.MONTH) + 1), + (byte) (now.get(Calendar.DAY_OF_MONTH)), + (byte) (now.get(Calendar.HOUR)), + (byte) (now.get(Calendar.MINUTE)), + (byte) (now.get(Calendar.SECOND)), + 0, + 0, + 0, + 0 }; } }); @@ -443,7 +518,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetCallState(CallSpec callSpec) { - switch(callSpec.command){ + switch (callSpec.command) { case CallSpec.CALL_INCOMING: { showText(callSpec.name, callSpec.number); break; @@ -454,7 +529,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { - LOG.debug("Canned Messages: "+cannedMessagesSpec); + LOG.debug("Canned Messages: " + cannedMessagesSpec); } @Override @@ -535,7 +610,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); byte state = 0; - if(enable) + if (enable) state = HPlusConstants.HEARTRATE_ALLDAY_ON; else state = HPlusConstants.HEARTRATE_ALLDAY_OFF; @@ -556,7 +631,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte[] msg = new byte[2]; msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; - if(start) + if (start) msg[1] = 1; else msg[1] = 0; @@ -577,7 +652,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte[] msg = new byte[15]; msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; - for(int i = 0;i 12) { - message = title.substring(0, 12); - }else { - message = title; - for(int i = message.length(); i < 12; i++) - message += ""; - } + if (title != null) { + if (title.length() > 12) { + message = title.substring(0, 12); + } else { + message = title; + for (int i = message.length(); i < 12; i++) + message += " "; } - message += body; + } + message += body; - int length = message.length() / 17; + int length = message.length() / 17; - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_INCOMMING_SOCIAL, (byte) length}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_ACTION_INCOMING_SOCIAL, (byte) 255}); - int remaining = 0; + int remaining = 0; - if(message.length() % 17 > 0) - remaining = length + 1; - else - remaining = length; + if (message.length() % 17 > 0) + remaining = length + 1; + else + remaining = length; - msg[1] = (byte) remaining; - int message_index = 0; - int i = 3; + msg[1] = (byte) remaining; + int message_index = 0; + int i = 3; - for(int j=0; j < message.length(); j++){ - msg[i++] = (byte) message.charAt(j); + for (int j = 0; j < message.length(); j++) { + msg[i++] = (byte) message.charAt(j); - if(i == msg.length){ - message_index ++; - msg[2] = (byte) message_index; - builder.write(ctrlCharacteristic, msg); + if (i == msg.length) { + message_index++; + msg[2] = (byte) message_index; + builder.write(ctrlCharacteristic, msg); - msg = msg.clone(); - for(i=3; i < msg.length; i++) - msg[i] = 32; + msg = msg.clone(); + for (i = 3; i < msg.length; i++) + msg[i] = 32; - if(message_index < remaining) - i = 3; - else - break; - } + if (message_index < remaining) + i = 3; + else + break; } + } - msg[2] = (byte) remaining; + msg[2] = (byte) remaining; - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); } public boolean isExpectedDevice(BluetoothDevice device) { @@ -703,14 +778,14 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { UUID characteristicUUID = characteristic.getUuid(); byte[] data = characteristic.getValue(); - if(data.length == 0) + if (data.length == 0) return true; - switch(data[0]){ + switch (data[0]) { case HPlusConstants.DATA_STATS: - return processDataStats(data); + return processDataStats(data); case HPlusConstants.DATA_SLEEP: - return processSleepStats(data); + return processSleepStats(data); default: LOG.info("Unhandled characteristic changed: " + characteristicUUID); @@ -718,10 +793,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return false; } - private boolean processSleepStats(byte[] data){ + private boolean processSleepStats(byte[] data) { LOG.debug("Process Sleep Stats"); - if(data.length < 19) { + if (data.length < 19) { LOG.error("Invalid Sleep Message Length " + data.length); return false; } @@ -735,13 +810,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { int ts = (int) (System.currentTimeMillis() / 1000); HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); - Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) - .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, record.getRawData() ) - .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + //TODO: Store Sample. How? - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + //provider.addGBActivitySample(record); - }catch (GBException e) { + + } catch (GBException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); @@ -751,14 +825,16 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } - private boolean processDataStats(byte[]data){ + private boolean processDataStats(byte[] data) { + //TODO: Store Calories and Distance. How? + LOG.debug("Process Data Stats"); - if(data.length < 15) { + if (data.length < 15) { LOG.error("Invalid Stats Message Length " + data.length); return false; } - double distance = ( (int) data[4] * 256 + data[3]) / 100.0; + double distance = ((int) data[4] * 256 + data[3]) / 100.0; int x = (int) data[6] * 256 + data[5]; int y = (int) data[8] * 256 + data[7]; @@ -778,9 +854,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { if (bpm != HPlusHealthActivitySample.NOT_MEASURED) { HPlusHealthActivitySample sample = createActivitySample(device, user, ts, provider); sample.setHeartRate(bpm); + sample.setSteps(0); + sample.setRawIntensity(ActivitySample.NOT_MEASURED); + sample.setRawKind(HPlusSampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? provider.addGBActivitySample(sample); } - }catch (GBException e) { + } catch (GBException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); From 6c186329dff933323500ec4e550a8a7364286b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 00:08:14 +0000 Subject: [PATCH 034/244] Cleanup HPlusSampleProvider --- .../devices/hplus/HPlusSampleProvider.java | 36 ++----------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java index 67b535a3..018c56a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java @@ -25,12 +25,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class HPlusSampleProvider extends AbstractSampleProvider { - public static final int TYPE_DEEP_SLEEP = 4; - 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; private GBDevice mDevice; private DaoSession mSession; @@ -47,37 +41,11 @@ public class HPlusSampleProvider extends AbstractSampleProvider Date: Fri, 23 Dec 2016 00:10:38 +0000 Subject: [PATCH 035/244] Improved device filter by considering the existence of a service UUID --- .../devices/hplus/HPlusCoordinator.java | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index b90e20c5..76da9c20 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -4,9 +4,13 @@ package nodomain.freeyourgadget.gadgetbridge.devices.hplus; * @author João Paulo Barraca <jpbarraca@gmail.com> */ +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.os.ParcelUuid; import android.support.annotation.NonNull; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -29,17 +33,35 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + public class HPlusCoordinator extends AbstractDeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); private static Prefs prefs = GBApplication.getPrefs(); + @NonNull + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid mi2Service = new ParcelUuid(HPlusConstants.UUID_SERVICE_HP); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mi2Service).build(); + return Collections.singletonList(filter); + } + + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { + if (candidate.supportsService(HPlusConstants.UUID_SERVICE_HP)) { + return DeviceType.HPLUS; + } + String name = candidate.getDevice().getName(); LOG.debug("Looking for: " + name); if (name != null && name.startsWith("HPLUS")) { return DeviceType.HPLUS; } + return DeviceType.UNKNOWN; } @@ -123,27 +145,6 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return prefs.getInt(HPlusConstants.PREF_HPLUS_FITNESS_GOAL + "_" + address, 10000); } - /** - * Returns the user info from the user configured data in the preferences. - * - * @param hplusAddress - * @throws IllegalArgumentException when the user info can not be created - */ - public static UserInfo getConfiguredUserInfo(String hplusAddress) throws IllegalArgumentException { - ActivityUser activityUser = new ActivityUser(); - - UserInfo info = UserInfo.create( - hplusAddress, - prefs.getString(HPlusConstants.PREF_HPLUS_USER_ALIAS, null), - activityUser.getGender(), - activityUser.getAge(), - activityUser.getHeightCm(), - activityUser.getWeightKg(), - 0 - ); - return info; - } - public static byte getCountry(String address) { return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_COUNTRY + "_" + address, 10); @@ -213,23 +214,23 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static boolean getSWAlertTime(String address) { - return (boolean) prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); + return prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); } public static int getAlertTime(String address) { - return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); } public static int getSITStartTime(String address) { - return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); } public static int getSITEndTime(String address) { - return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); } From 2b78f2708f9182d153c51097cd4690977b38bd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 01:21:05 +0000 Subject: [PATCH 036/244] Cleanup according to PR Review --- .../devices/hplus/HPlusConstants.java | 3 +- .../devices/hplus/HPlusCoordinator.java | 31 +- .../devices/hplus/HPlusSampleProvider.java | 26 - .../service/devices/hplus/HPlusSupport.java | 595 ++++++++++-------- 4 files changed, 329 insertions(+), 326 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 17882caa..a22990b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -71,6 +71,7 @@ public final class HPlusConstants { public static final byte DATA_STATS = 0x33; + public static final byte DATA_STEPS = 0x36; public static final byte DATA_SLEEP = 0x1A; public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; @@ -78,8 +79,6 @@ public final class HPlusConstants { public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; - public static final String PREF_HPLUS_USER_ALIAS = "hplus_user_alias"; - public static final String PREF_HPLUS_FITNESS_GOAL = "hplus_fitness_goal"; public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; public static final String PREF_HPLUS_UNIT = "hplus_unit"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 76da9c20..d1cf8e25 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -141,8 +141,9 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static int getFitnessGoal(String address) throws IllegalArgumentException { - Prefs prefs = GBApplication.getPrefs(); - return prefs.getInt(HPlusConstants.PREF_HPLUS_FITNESS_GOAL + "_" + address, 10000); + ActivityUser activityUser = new ActivityUser(); + + return activityUser.getStepsGoal(); } public static byte getCountry(String address) { @@ -161,28 +162,25 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { public static byte getUserWeight(String address) { ActivityUser activityUser = new ActivityUser(); - return (byte) activityUser.getWeightKg(); + return (byte) (activityUser.getWeightKg() & 0xFF); } public static byte getUserHeight(String address) { ActivityUser activityUser = new ActivityUser(); - return (byte) activityUser.getHeightCm(); + return (byte) (activityUser.getHeightCm() & 0xFF); } public static byte getUserAge(String address) { ActivityUser activityUser = new ActivityUser(); - return (byte) activityUser.getAge(); + return (byte) (activityUser.getAge() & 0xFF); } public static byte getUserSex(String address) { ActivityUser activityUser = new ActivityUser(); - int gender = activityUser.getGender(); - - return (byte) gender; - + return (byte) (activityUser.getGender() & 0xFF); } public static int getGoal(String address) { @@ -192,46 +190,37 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getScreenTime(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME + "_" + address, 5); - + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME + "_" + address, 5) & 0xFF); } public static byte getAllDayHR(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); - + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10) & 0xFF); } public static byte getSocial(String address) { - //TODO: Figure what this is. Returning the default value return (byte) 255; } public static byte getUserWrist(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10); - + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10) & 0xFF); } public static boolean getSWAlertTime(String address) { return prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); - - } public static int getAlertTime(String address) { return prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); - } public static int getSITStartTime(String address) { return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); - } public static int getSITEndTime(String address) { return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java index 018c56a6..a26f6be2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java @@ -48,32 +48,6 @@ public class HPlusSampleProvider extends AbstractSampleProvider getAllActivitySamples(int timestamp_from, int timestamp_to) { - List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); - - Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); - if (dbDevice == null) { - // no device, no samples - return Collections.emptyList(); - } - - QueryBuilder qb = getSession().getHPlusHealthActivitySampleDao().queryBuilder(); - - qb.where(HPlusHealthActivitySampleDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivitySampleDao.Properties.Timestamp.ge(timestamp_from)) - .where(HPlusHealthActivitySampleDao.Properties.Timestamp.le(timestamp_to)); - - List sampleList = qb.build().list(); - - for (HPlusHealthActivitySample sample : sampleList) { - if (timestamp_from <= sample.getTimestamp() && sample.getTimestamp() < timestamp_to) { - sample.setRawKind(sample.getRawKind()); - } - } - detachFromSession(); - return samples; - } @NonNull @Override protected de.greenrobot.dao.Property getTimestampSampleProperty() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index eca31a31..8914f198 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -7,17 +7,18 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.support.v4.content.LocalBroadcastManager; +import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.UUID; @@ -35,14 +36,13 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; -import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; @@ -50,9 +50,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSuppo 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.ConditionalWriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; +import nodomain.freeyourgadget.gadgetbridge.util.GB; public class HPlusSupport extends AbstractBTLEDeviceSupport { @@ -110,10 +110,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); - UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); - BluetoothGattDescriptor descriptor = measureCharacteristic.getDescriptor(uuid); - descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); - builder.setGattCallback(this); builder.notify(measureCharacteristic, true); @@ -123,6 +119,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setInitValues(TransactionBuilder builder) { LOG.debug("Set Init Values"); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT1); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT2); return this; @@ -141,71 +138,61 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport syncPreferences(TransactionBuilder transaction) { LOG.info("Attempting to sync preferences..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte sex = HPlusCoordinator.getUserSex(getDevice().getAddress()); - byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); - byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); - byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); - int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); - byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); - byte country = HPlusCoordinator.getCountry(getDevice().getAddress()); - byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? - byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); - byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); - byte alertTimeHour = 0; - byte alertTimeMinute = 0; + byte sex = HPlusCoordinator.getUserSex(getDevice().getAddress()); + byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); + byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); + byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + byte country = HPlusCoordinator.getCountry(getDevice().getAddress()); + byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? + byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); + byte alertTimeHour = 0; + byte alertTimeMinute = 0; - if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { - int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); + if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { + int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); - alertTimeHour = (byte) (t / 256); - alertTimeMinute = (byte) (t % 256); - } + alertTimeHour = (byte) ((t / 256) & 0xff); + alertTimeMinute = (byte) (t % 256); + } - byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); - byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); + byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); + byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_COUNTRY, - sex, - age, - bodyHeight, - bodyWeight, - 0, - 0, - (byte) (goal / 256), - (byte) (goal % 256), - displayTime, - country, - 0, - social, - allDayHeart, - wrist, - alertTimeHour, - alertTimeMinute, - unit, - timemode - }; - } + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + sex, + age, + bodyHeight, + bodyWeight, + 0, + 0, + (byte) ((goal / 256) & 0xff), + (byte) (goal % 256), + displayTime, + country, + 0, + social, + allDayHeart, + wrist, + alertTimeHour, + alertTimeMinute, + unit, + timemode }); return this; } private HPlusSupport setCountry(TransactionBuilder transaction) { LOG.info("Attempting to set country..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_COUNTRY, - (byte) value - }; - } + byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + value }); return this; } @@ -213,73 +200,57 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setTimeMode(TransactionBuilder transaction) { LOG.info("Attempting to set Time Mode..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_TIMEMODE, - (byte) value - }; - } + byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIMEMODE, + value }); return this; } private HPlusSupport setUnit(TransactionBuilder transaction) { LOG.info("Attempting to set Units..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_UNIT, - (byte) value - }; - } + + byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_UNIT, + value }); return this; } private HPlusSupport setCurrentDate(TransactionBuilder transaction) { LOG.info("Attempting to set Current Date..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); - int year = c.get(Calendar.YEAR) - 1900; - int month = c.get(Calendar.MONTH); - int day = c.get(Calendar.DAY_OF_MONTH); + Calendar c = Calendar.getInstance(); + int year = c.get(Calendar.YEAR) - 1900; + int month = c.get(Calendar.MONTH); + int day = c.get(Calendar.DAY_OF_MONTH); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_DATE, + (byte) ((year / 256) & 0xff), + (byte) (year % 256), + (byte) (month), + (byte) (day) - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_DATE, - (byte) (year / 256), - (byte) (year % 256), - (byte) (month), - (byte) (day) - }; - } }); return this; } private HPlusSupport setCurrentTime(TransactionBuilder transaction) { LOG.info("Attempting to set Current Time..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_TIME, - (byte) c.get(Calendar.HOUR_OF_DAY), - (byte) c.get(Calendar.MINUTE), - (byte) c.get(Calendar.SECOND) - }; - } + Calendar c = Calendar.getInstance(); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIME, + (byte) c.get(Calendar.HOUR_OF_DAY), + (byte) c.get(Calendar.MINUTE), + (byte) c.get(Calendar.SECOND) + }); return this; } @@ -287,16 +258,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setDayOfWeek(TransactionBuilder transaction) { LOG.info("Attempting to set Day Of Week..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_WEEK, - (byte) c.get(Calendar.DAY_OF_WEEK) - }; - } + Calendar c = Calendar.getInstance(); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEEK, + (byte) c.get(Calendar.DAY_OF_WEEK) }); return this; } @@ -304,67 +271,56 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setSIT(TransactionBuilder transaction) { LOG.info("Attempting to set SIT..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); - int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); - Calendar now = Calendar.getInstance(); + int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); + int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); + + Calendar now = Calendar.getInstance(); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_SIT_INTERVAL, + (byte) ((startTime / 256) & 0xff), + (byte) (startTime % 256), + (byte) ((endTime / 256) & 0xff), + (byte) (endTime % 256), + 0, + 0, + (byte) ((now.get(Calendar.YEAR) / 256) & 0xff), + (byte) (now.get(Calendar.YEAR) % 256), + (byte) (now.get(Calendar.MONTH) + 1), + (byte) (now.get(Calendar.DAY_OF_MONTH)), + (byte) (now.get(Calendar.HOUR)), + (byte) (now.get(Calendar.MINUTE)), + (byte) (now.get(Calendar.SECOND)), + 0, + 0, + 0, + 0 - return new byte[]{ - HPlusConstants.COMMAND_SET_SIT_INTERVAL, - (byte) (startTime / 256), - (byte) (startTime % 256), - (byte) (endTime / 256), - (byte) (endTime % 256), - 0, - 0, - (byte) (now.get(Calendar.YEAR) / 256), - (byte) (now.get(Calendar.YEAR) % 256), - (byte) (now.get(Calendar.MONTH) + 1), - (byte) (now.get(Calendar.DAY_OF_MONTH)), - (byte) (now.get(Calendar.HOUR)), - (byte) (now.get(Calendar.MINUTE)), - (byte) (now.get(Calendar.SECOND)), - 0, - 0, - 0, - 0 - }; - } }); return this; } private HPlusSupport setWeight(TransactionBuilder transaction) { LOG.info("Attempting to set Weight..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_WEIGHT, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEIGHT, + value + }); return this; } private HPlusSupport setHeight(TransactionBuilder transaction) { LOG.info("Attempting to set Height..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_HEIGHT, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_HEIGHT, + value + }); return this; } @@ -372,32 +328,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAge(TransactionBuilder transaction) { LOG.info("Attempting to set Age..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_AGE, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_AGE, + value + }); return this; } private HPlusSupport setSex(TransactionBuilder transaction) { LOG.info("Attempting to set Sex..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserSex(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SEX, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserSex(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SEX, + value + }); return this; } @@ -405,17 +353,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setGoal(TransactionBuilder transaction) { LOG.info("Attempting to set Sex..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - int value = HPlusCoordinator.getGoal(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_GOAL, - (byte) (value / 256), - (byte) (value % 256) - }; - } + int value = HPlusCoordinator.getGoal(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_GOAL, + (byte) ((value / 256) & 0xff), + (byte) (value % 256) + }); return this; } @@ -423,32 +367,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setScreenTime(TransactionBuilder transaction) { LOG.info("Attempting to set Screentime..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SCREENTIME, - (byte) value - }; - } + byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SCREENTIME, + value + }); return this; } private HPlusSupport setAllDayHeart(TransactionBuilder transaction) { LOG.info("Attempting to set All Day HR..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, - (byte) value - }; - } + byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, + value + }); return this; } @@ -456,17 +392,20 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAlarm(TransactionBuilder transaction) { LOG.info("Attempting to set Alarm..."); + //TODO: Find how to set alarms return this; } private HPlusSupport setBlood(TransactionBuilder transaction) { LOG.info("Attempting to set Blood..."); + //TODO: Find what blood means for the band return this; } private HPlusSupport setFindMe(TransactionBuilder transaction) { LOG.info("Attempting to set Findme..."); + //TODO: Find how this works return this; } @@ -499,16 +438,16 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { LOG.debug("Got Notification"); + //TODO: Show different notifications acccording to source as Band supports this showText(notificationSpec.body); } @Override public void onSetTime() { - TransactionBuilder builder = new TransactionBuilder("vibration"); + TransactionBuilder builder = new TransactionBuilder("time"); setCurrentDate(builder); setCurrentTime(builder); - } @Override @@ -594,11 +533,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getQueue().clear(); TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); - byte state = 0; builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, 0x10}); //Set Real Time... ? builder.queue(getQueue()); - } @Override @@ -608,7 +545,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getQueue().clear(); TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); - byte state = 0; + byte state; if (enable) state = HPlusConstants.HEARTRATE_ALLDAY_ON; @@ -623,20 +560,23 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { public void onFindDevice(boolean start) { LOG.debug("Find Me"); - getQueue().clear(); - ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + try { + TransactionBuilder builder = performInitialized("findMe"); - TransactionBuilder builder = new TransactionBuilder("findMe"); + byte[] msg = new byte[2]; + msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; - byte[] msg = new byte[2]; - msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; + if (start) + msg[1] = 1; + else + msg[1] = 0; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } catch (IOException e) { + GB.toast(getContext(), "Error toogling Find Me: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } - if (start) - msg[1] = 1; - else - msg[1] = 0; - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); } @Override @@ -645,18 +585,20 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getQueue().clear(); - ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + try { + TransactionBuilder builder = performInitialized("vibration"); - TransactionBuilder builder = new TransactionBuilder("vibration"); + byte[] msg = new byte[15]; + msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; - byte[] msg = new byte[15]; - msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; + for (int i = 0; i < msg.length - 1; i++) + msg[i + 1] = (byte) "GadgetBridge".charAt(i); - for (int i = 0; i < msg.length - 1; i++) - msg[i + 1] = (byte) "GadgetBridge".charAt(i); - - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } catch (IOException e) { + GB.toast(getContext(), "Error setting Vibration: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } } @Override @@ -688,7 +630,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { LOG.debug("Test New Function"); - } @@ -699,67 +640,71 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private void showText(String title, String body) { LOG.debug("Show Notification"); - TransactionBuilder builder = new TransactionBuilder("showText"); - if (ctrlCharacteristic == null) - ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + try { + TransactionBuilder builder = performInitialized("notification"); - byte[] msg = new byte[20]; - for (int i = 0; i < msg.length; i++) - msg[i] = 32; - msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT; + byte[] msg = new byte[20]; + for (int i = 0; i < msg.length; i++) + msg[i] = 32; - String message = ""; + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT; - if (title != null) { - if (title.length() > 12) { - message = title.substring(0, 12); - } else { - message = title; - for (int i = message.length(); i < 12; i++) - message += " "; + String message = ""; + + if (title != null) { + if (title.length() > 12) { + message = title.substring(0, 12); + } else { + message = title; + for (int i = message.length(); i < 12; i++) + message += " "; + } } - } - message += body; + message += body; - int length = message.length() / 17; + int length = message.length() / 17; - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_ACTION_INCOMING_SOCIAL, (byte) 255}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_ACTION_INCOMING_SOCIAL, (byte) 255}); - int remaining = 0; + int remaining; - if (message.length() % 17 > 0) - remaining = length + 1; - else - remaining = length; + if (message.length() % 17 > 0) + remaining = length + 1; + else + remaining = length; - msg[1] = (byte) remaining; - int message_index = 0; - int i = 3; + msg[1] = (byte) remaining; + int message_index = 0; + int i = 3; - for (int j = 0; j < message.length(); j++) { - msg[i++] = (byte) message.charAt(j); + for (int j = 0; j < message.length(); j++) { + msg[i++] = (byte) message.charAt(j); - if (i == msg.length) { - message_index++; - msg[2] = (byte) message_index; - builder.write(ctrlCharacteristic, msg); + if (i == msg.length) { + message_index++; + msg[2] = (byte) message_index; + builder.write(ctrlCharacteristic, msg); - msg = msg.clone(); - for (i = 3; i < msg.length; i++) - msg[i] = 32; + msg = msg.clone(); + for (i = 3; i < msg.length; i++) + msg[i] = 32; - if (message_index < remaining) - i = 3; - else - break; + if (message_index < remaining) + i = 3; + else + break; + } } + + msg[2] = (byte) remaining; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + }catch(IOException e){ + GB.toast(getContext(), "Error showing device Notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } - - msg[2] = (byte) remaining; - - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); } public boolean isExpectedDevice(BluetoothDevice device) { @@ -786,6 +731,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return processDataStats(data); case HPlusConstants.DATA_SLEEP: return processSleepStats(data); + case HPlusConstants.DATA_STEPS: + return processStepStats(data); + default: LOG.info("Unhandled characteristic changed: " + characteristicUUID); @@ -793,6 +741,99 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return false; } + /* + Receives a message containing the status of the day. + */ + private boolean processDayStats(byte[] data) { + int a = data[4] * 256 + data[5]; + if (a < 144) { + int slot = a * 2; // 10 minute slots as an offset from 0:00 AM + int avgHR = data[1]; //Average Heart Rate + int steps = data[2] * 256 + data[3]; // Steps in this period + + //?? data[6]; + int timeInactive = data[7]; + + LOG.debug("Day Stats: Slot: " + slot + " HR: " + avgHR + " Steps: " + steps + " TimeInactive: " + timeInactive); + + 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); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + + //TODO: Store Sample. How? + + //provider.addGBActivitySample(record); + + + } catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + } else + LOG.error("Invalid day stats"); + + return true; + } + + private boolean processStepStats(byte[] data) { + LOG.debug("Process Step Stats"); + + if (data.length < 19) { + LOG.error("Invalid Steps Message Length " + data.length); + return false; + } + /* + This is a dump of the entire day. + */ + int year = data[9] + data[10] * 256; + short month = data[11]; + short day = data[12]; + int steps = data[2] * 256 + data[1]; + + float distance = ((float) (data[3] + data[4] * 256) / 100.0f); + + /* + unknown fields + short s12 = (short)(data[5] + data[6] * 256); + short s13 = (short)(data[7] + data[8] * 256); + short s16 = (short)(data[13]) + data[14] * 256); + short s17 = data[15]; + short s18 = data[16]; + */ + + + LOG.debug("Step Stats: Year: " + year + " Month: " + month + " Day:"); + 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); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + + //TODO: Store Sample. How? + + //provider.addGBActivitySample(record); + + + } catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + private boolean processSleepStats(byte[] data) { LOG.debug("Process Sleep Stats"); @@ -856,7 +897,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { sample.setHeartRate(bpm); sample.setSteps(0); sample.setRawIntensity(ActivitySample.NOT_MEASURED); - sample.setRawKind(HPlusSampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? provider.addGBActivitySample(sample); } } catch (GBException e) { From 9a338c9baefae05bc66e58bc8d5e009d6835d05c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 01:35:18 +0000 Subject: [PATCH 037/244] HPlus: Fix text notification length --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 8914f198..181b8123 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -653,11 +653,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { String message = ""; if (title != null) { - if (title.length() > 12) { + if (title.length() > 17) { message = title.substring(0, 12); } else { message = title; - for (int i = message.length(); i < 12; i++) + for (int i = message.length(); i < 17; i++) message += " "; } } From 9dd5967f4e558e40bb77a79915ecc08150ccc8ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 10:14:03 +0000 Subject: [PATCH 038/244] HPlus: Set date and time on connect --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 181b8123..c3669729 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -106,6 +106,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //Initialize device setInitValues(builder); + setCurrentDate(builder); + setCurrentTime(builder); + syncPreferences(builder); builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); @@ -233,7 +236,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { HPlusConstants.COMMAND_SET_PREF_DATE, (byte) ((year / 256) & 0xff), (byte) (year % 256), - (byte) (month), + (byte) (month + 1), (byte) (day) }); From cd915598b03f728953568be416754fa3758f673c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 11:46:20 +0000 Subject: [PATCH 039/244] HPlus: Improved handling of incomming calls --- .../devices/hplus/HPlusConstants.java | 9 ++- .../service/devices/hplus/HPlusSupport.java | 75 ++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index a22990b8..7f8ac3ba 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -59,7 +59,7 @@ public final class HPlusConstants { public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; - public static final byte COMMAND_SET_INCOMMING_CALL = 65; + public static final byte COMMAND_SET_INCOMING_CALL = 0x41; public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; public static final byte COMMAND_SET_CONF_SAVE = 0x17; @@ -75,8 +75,13 @@ public final class HPlusConstants { public static final byte DATA_SLEEP = 0x1A; public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; - public static final byte COMMAND_ACTION_INCOMMING_SMS = 0x40; + public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; + public static final byte[] COMMAND_ACTION_INCOMING_CALL = new byte[] {6, -86}; + public static final byte COMMAND_ACTION_DISPLAY_TEXT_CENTER = 0x23; + public static final byte COMMAND_ACTION_DISPLAY_TEXT_NAME = 0x3F; + public static final byte COMMAND_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312? + public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index c3669729..4797ce5a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -462,7 +462,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { public void onSetCallState(CallSpec callSpec) { switch (callSpec.command) { case CallSpec.CALL_INCOMING: { - showText(callSpec.name, callSpec.number); + showIncomingCall(callSpec.name, callSpec.number); break; } } @@ -636,6 +636,79 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } + private void showIncomingCall(String name, String number){ + LOG.debug("Show Incoming Call"); + + try { + TransactionBuilder builder = performInitialized("incomingCallIcon"); + + //Enable call notifications + builder.write(ctrlCharacteristic, new byte[] {HPlusConstants.COMMAND_SET_INCOMING_CALL, 1 }); + + //Show Call Icon + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_ACTION_INCOMING_CALL); + + //builder = performInitialized("incomingCallText"); + builder.queue(getQueue()); + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + byte[] msg = new byte[13]; + + builder = performInitialized("incomingCallNumber"); + + //Show call number + for (int i = 0; i < msg.length; i++) + msg[i] = ' '; + + for(int i = 0; i < number.length() && i < (msg.length - 1); i++) + msg[i + 1] = (byte) number.charAt(i); + + + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_CENTER; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + builder = performInitialized("incomingCallText"); + + //Show call name + //Must call twice, otherwise nothing happens + for (int i = 0; i < msg.length; i++) + msg[i] = ' '; + + for(int i = 0; i < name.length() && i < (msg.length - 1); i++) + msg[i + 1] = (byte) name.charAt(i); + + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_NAME; + builder.write(ctrlCharacteristic, msg); + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_NAME_CN; + builder.write(ctrlCharacteristic, msg); + + builder.queue(getQueue()); + }catch(IOException e){ + GB.toast(getContext(), "Error showing incoming call: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + + } + } + private void showText(String message) { showText(null, message); } From 88f2d2ee4f440d64870a2d9036f023a053b0665b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 12:20:06 +0000 Subject: [PATCH 040/244] HPlus: Fixed notification title size --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 4797ce5a..ece3eb06 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -722,7 +722,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte[] msg = new byte[20]; for (int i = 0; i < msg.length; i++) - msg[i] = 32; + msg[i] = ' '; msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT; @@ -730,7 +730,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { if (title != null) { if (title.length() > 17) { - message = title.substring(0, 12); + message = title.substring(0, 17); } else { message = title; for (int i = message.length(); i < 17; i++) @@ -764,7 +764,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { msg = msg.clone(); for (i = 3; i < msg.length; i++) - msg[i] = 32; + msg[i] = ' '; if (message_index < remaining) i = 3; From 649e20ad04fc038303fe1c15ab2f7ab524364553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Sat, 24 Dec 2016 00:05:51 +0000 Subject: [PATCH 041/244] HPlus: Ignore duplicated messages from band --- .../gadgetbridge/devices/hplus/HPlusConstants.java | 1 + .../service/devices/hplus/HPlusSupport.java | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 7f8ac3ba..f69bb453 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -72,6 +72,7 @@ public final class HPlusConstants { public static final byte DATA_STATS = 0x33; public static final byte DATA_STEPS = 0x36; + public static final byte DATA_SLEEP = 0x1A; public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index ece3eb06..c3704ffe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -61,6 +61,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private BluetoothGattCharacteristic ctrlCharacteristic = null; private BluetoothGattCharacteristic measureCharacteristic = null; + private byte[] lastDataStats = null; + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -108,7 +110,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { setInitValues(builder); setCurrentDate(builder); setCurrentTime(builder); - syncPreferences(builder); builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); @@ -449,6 +450,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetTime() { TransactionBuilder builder = new TransactionBuilder("time"); + setCurrentDate(builder); setCurrentTime(builder); } @@ -651,6 +653,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //builder = performInitialized("incomingCallText"); builder.queue(getQueue()); + //TODO: Use WaitAction try { Thread.sleep(200); } catch (InterruptedException e) { @@ -728,6 +731,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { String message = ""; + //TODO: Create StringUtils.pad and StringUtils.truncate if (title != null) { if (title.length() > 17) { message = title.substring(0, 17); @@ -951,6 +955,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { LOG.error("Invalid Stats Message Length " + data.length); return false; } + + //Ignore duplicate packets + if(data.equals(lastDataStats)) + return true; + + lastDataStats = data.clone(); + double distance = ((int) data[4] * 256 + data[3]) / 100.0; int x = (int) data[6] * 256 + data[5]; From f027dc20056570d4017ca36e8b65ec32ba89e2d1 Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Sat, 24 Dec 2016 14:40:03 +0100 Subject: [PATCH 042/244] Added (missing) battery status display feature for Mi Band 2 to the changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a75481..ace37b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ####Version 0.15.1 * Improved handling of notifications for some apps * Pebble 2/LE: Add setting to limit GATT MTU for debugging broken BLE stacks +* Mi Band 2: Display battery status ####Version 0.15.0 * New device: Liveview From b5225145d43a6dab98c031c756ca6796a8ae4f83 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 24 Dec 2016 22:00:24 +0100 Subject: [PATCH 043/244] Fix crash with unknown notification sources on Mi Band (maybe other non-Pebble devices also affected) Pebble low level code had an own check for notification type being null, no we set it to UNKNOWN early This regression was introduced in 0.15.0 though "Revamp Notification types Pebble (#453)" Fixes #468 --- .../gadgetbridge/externalevents/NotificationListener.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index c3cd2b3c..b033c741 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -251,6 +252,10 @@ public class NotificationListener extends NotificationListenerService { notificationSpec.type = AppNotificationType.getInstance().get(source); + if (notificationSpec.type == null) { + notificationSpec.type = NotificationType.UNKNOWN; + } + LOG.info("Processing notification from source " + source + " with flags: " + notification.flags); dissectNotificationTo(notification, notificationSpec, preferBigText); From 0bdcdbae54c66f6fb8a2b146af042861037e4f3e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 24 Dec 2016 23:04:01 +0100 Subject: [PATCH 044/244] update CHANGELOG, bump version --- 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 67a75481..da45a0c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version 0.15.2 +* Mi Band: Fix crash with unknown notification sources + ####Version 0.15.1 * Improved handling of notifications for some apps * Pebble 2/LE: Add setting to limit GATT MTU for debugging broken BLE stacks diff --git a/app/build.gradle b/app/build.gradle index 92a026ec..a4caf211 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.15.1" - versionCode 78 + versionName "0.15.2" + versionCode 79 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 7af99208..1c3b676f 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: Fix crash with unknown notification sources + Improved handling of notifications for some apps Pebble 2/LE: Add setting to limit GATT MTU for debugging broken BLE stacks From 5fb05e85462c265df211c223d778df83509d6586 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 24 Dec 2016 23:19:22 +0100 Subject: [PATCH 045/244] update xml changelog for 0.15.1 also --- app/src/main/res/xml/changelog_master.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 1c3b676f..b3a558fa 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -6,6 +6,7 @@ Improved handling of notifications for some apps Pebble 2/LE: Add setting to limit GATT MTU for debugging broken BLE stacks + Mi Band 2: Display battery status New device: Liveview From 3441192d19fbf42700c5a5a9f835359fd9505796 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 25 Dec 2016 23:11:49 +0100 Subject: [PATCH 046/244] Pebble 2: Fix Pebble Classic FW 3.x app variant being priorized over native Pebble 2 app variant Fixes #475 --- .../gadgetbridge/devices/pebble/PBWReader.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index 3e3b4710..53be8a97 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -124,10 +124,12 @@ public class PBWReader { ZipInputStream zis = new ZipInputStream(afin); ZipEntry ze; + boolean found = false; try { while ((ze = zis.getNextEntry()) != null) { if (ze.getName().startsWith(dir)) { platformDir = dir; + found = true; break; } } @@ -135,6 +137,9 @@ public class PBWReader { } catch (IOException e) { e.printStackTrace(); } + if (found) { + break; + } } if (platform.equals("chalk") && platformDir.equals("")) { From 4925dec9f63ff5064dfd43acb136ab1593bcc164 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 25 Dec 2016 23:19:37 +0100 Subject: [PATCH 047/244] bump version, update CHANGELOG, not yet ready for release --- CHANGELOG.md | 5 +++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87320c83..949564e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ###Changelog +####Version 0.16.0 +* New device: ZeBand +* ZeBand: Initial experimental support +* Pebble 2: Fix Pebble Classic FW 3.x app variant being priorized over native Pebble 2 app variant + ####Version 0.15.2 * Mi Band: Fix crash with unknown notification sources diff --git a/app/build.gradle b/app/build.gradle index a4caf211..e985816d 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.15.2" - versionCode 79 + versionName "0.16.0" + versionCode 80 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index b3a558fa..7b1b8cb0 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,10 @@ + + New device: ZeBand + ZeBand: Initial experimental support + Pebble 2: Fix Pebble Classic FW 3.x app variant being priorized over native Pebble 2 app variant + Mi Band: Fix crash with unknown notification sources From 999d3e325218b0c4f0ae1054a02fabbc6bf0d6f9 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 25 Dec 2016 23:26:44 +0100 Subject: [PATCH 048/244] Mi1: Attempt at throttling notifications during sync #438 --- .../miband/operations/FetchActivityOperation.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) 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 ab4fd5d6..01950313 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 @@ -55,6 +55,7 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { private final boolean hasExtendedActivityData; private static class ActivityStruct { + private int lastNotifiedProgress; private final byte[] activityDataHolder; private final int activityDataHolderSize; //index of the buffer above @@ -129,6 +130,7 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { public void bufferFlushed(int minutes) { activityDataTimestampProgress.add(Calendar.MINUTE, minutes); activityDataHolderProgress = 0; + lastNotifiedProgress = 0; } } @@ -199,9 +201,16 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { } else { bufferActivityData(value); } - LOG.debug("activity data: length: " + value.length + ", remaining bytes: " + activityStruct.activityDataRemainingBytes); + if (LOG.isDebugEnabled()) { + LOG.debug("activity data: length: " + value.length + ", remaining bytes: " + activityStruct.activityDataRemainingBytes); + } - GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data), true, (int) (((float) (activityStruct.activityDataUntilNextHeader - activityStruct.activityDataRemainingBytes)) / activityStruct.activityDataUntilNextHeader * 100), getContext()); + int progress = (int) (((float) (activityStruct.activityDataUntilNextHeader - activityStruct.activityDataRemainingBytes)) / activityStruct.activityDataUntilNextHeader * 100); + // avoid too many notifications overloading the system + if (progress - activityStruct.lastNotifiedProgress >= 8) { + activityStruct.lastNotifiedProgress = progress; + GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data), true, progress, getContext()); + } if (activityStruct.isBlockFinished()) { sendAckDataTransfer(activityStruct.activityDataTimestampToAck, activityStruct.activityDataUntilNextHeader); From 305bd7600caa016c4e165f718c8b5ffeab234d99 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 26 Dec 2016 00:23:02 +0100 Subject: [PATCH 049/244] Fix current realtime steps calculations #450 timestamp is already in seconds instead of milliseconds Also: port to REALTIME_SAMPLES --- .../charts/LiveActivityFragment.java | 30 +++++++++---------- .../gadgetbridge/model/DeviceService.java | 5 ---- .../service/devices/miband/MiBandSupport.java | 20 ++----------- .../devices/miband2/MiBand2Support.java | 21 ++----------- 4 files changed, 21 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index 12b1d152..e86fd90a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -136,7 +136,7 @@ public class LiveActivityFragment extends AbstractChartFragment { throw new IllegalArgumentException("delta in seconds is <= 0 -- time change?"); } - int oneMinute = 60 * 1000; + int oneMinute = 60; float factor = oneMinute / seconds; int result = (int) (stepsDelta * factor); if (result > MAX_STEPS_PER_MINUTE) { @@ -152,24 +152,25 @@ public class LiveActivityFragment extends AbstractChartFragment { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { - case DeviceService.ACTION_REALTIME_STEPS: { - int steps = intent.getIntExtra(DeviceService.EXTRA_REALTIME_STEPS, 0); - int timestamp = translateTimestampFrom(intent); - addEntries(steps, timestamp); - break; - } - case DeviceService.ACTION_HEARTRATE_MEASUREMENT: { - int heartRate = intent.getIntExtra(DeviceService.EXTRA_HEART_RATE_VALUE, 0); - int timestamp = translateTimestampFrom(intent); - if (isValidHeartRateValue(heartRate)) { - setCurrentHeartRate(heartRate, timestamp); - } + case DeviceService.ACTION_REALTIME_SAMPLES: { + ActivitySample sample = (ActivitySample) intent.getSerializableExtra(DeviceService.EXTRA_REALTIME_SAMPLE); + addSample(sample); break; } } } }; + private void addSample(ActivitySample sample) { + int heartRate = sample.getHeartRate(); + int timestamp = tsTranslation.shorten(sample.getTimestamp()); + if (isValidHeartRateValue(heartRate)) { + setCurrentHeartRate(heartRate, timestamp); + } + int steps = sample.getSteps(); + addEntries(steps, timestamp); + } + private int translateTimestampFrom(Intent intent) { return translateTimestamp(intent.getLongExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis())); } @@ -251,8 +252,7 @@ public class LiveActivityFragment extends AbstractChartFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { IntentFilter filterLocal = new IntentFilter(); - filterLocal.addAction(DeviceService.ACTION_REALTIME_STEPS); - filterLocal.addAction(DeviceService.ACTION_HEARTRATE_MEASUREMENT); + filterLocal.addAction(DeviceService.ACTION_REALTIME_SAMPLES); heartRateValues = new ArrayList<>(); tsTranslation = new TimestampTranslation(); 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 82fd3958..c36808e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -38,11 +38,6 @@ public interface DeviceService extends EventHandler { String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms"; String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps"; String ACTION_REALTIME_SAMPLES = PREFIX + ".action.realtime_samples"; - /** - * Use EXTRA_REALTIME_SAMPLE instead - */ - @Deprecated - String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps"; String ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT = PREFIX + ".action.realtime_hr_measurement"; String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support"; String ACTION_HEARTRATE_MEASUREMENT = PREFIX + ".action.hr_measurement"; 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 45fcc9fd..059089a5 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 @@ -954,23 +954,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { sample.setRawIntensity(ActivitySample.NOT_MEASURED); sample.setRawKind(MiBandSampleProvider.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); + 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); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index e1667f6f..a9b86646 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -70,7 +70,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.Dev import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.CheckAuthenticationNeededAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.FetchActivityOperation; @@ -983,23 +982,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { 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); + 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); From 8719cadc4373cf482ac8ee04c1d479104de66264 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 26 Dec 2016 01:38:20 +0100 Subject: [PATCH 050/244] Mi Band: fix live activity messing up stepcount #448 live samples now report relative steps, not absolute to the current day's stepcount. Also live samples' steps should NOT be added to the database since they are already counted in the regular stepcount. --- .../charts/LiveActivityFragment.java | 30 ++++++++----------- .../service/devices/miband/MiBandSupport.java | 12 ++++++-- .../miband/RealtimeSamplesSupport.java | 22 +++++++++++--- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index e86fd90a..23bef14b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -97,32 +97,26 @@ public class LiveActivityFragment extends AbstractChartFragment { return maxStepsPerMinute; } - public void updateCurrentSteps(int newSteps, int timestamp) { + public void updateCurrentSteps(int stepsDelta, int timestamp) { try { if (steps == 0) { - steps = newSteps; + steps += stepsDelta; lastTimestamp = timestamp; - if (newSteps > 0) { - initialSteps = newSteps; - } +// if (stepsDelta > 0) { +// initialSteps = stepsDelta; +// } return; } - if (newSteps >= steps) { - int stepsDelta = newSteps - steps; - int timeDelta = timestamp - lastTimestamp; - currentStepsPerMinute = calculateStepsPerMinute(stepsDelta, timeDelta); - if (currentStepsPerMinute > maxStepsPerMinute) { - maxStepsPerMinute = currentStepsPerMinute; - maxStepsResetCounter = 0; - } - steps = newSteps; - lastTimestamp = timestamp; - } else { - // TODO: handle new day? - + int timeDelta = timestamp - lastTimestamp; + currentStepsPerMinute = calculateStepsPerMinute(stepsDelta, timeDelta); + if (currentStepsPerMinute > maxStepsPerMinute) { + maxStepsPerMinute = currentStepsPerMinute; + maxStepsResetCounter = 0; } + steps += stepsDelta; + lastTimestamp = timestamp; } catch (Exception ex) { GB.toast(LiveActivityFragment.this.getContext(), ex.getMessage(), Toast.LENGTH_SHORT, GB.ERROR, ex); } 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 059089a5..33a81bc2 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 @@ -950,16 +950,22 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { MiBandSampleProvider provider = new MiBandSampleProvider(gbDevice, session); MiBandActivitySample sample = createActivitySample(device, user, ts, provider); sample.setHeartRate(getHeartrateBpm()); - sample.setSteps(getSteps()); sample.setRawIntensity(ActivitySample.NOT_MEASURED); sample.setRawKind(MiBandSampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? + LOG.debug("Storing realtime sample: " + sample); + provider.addGBActivitySample(sample); + + // set the steps only afterwards, since realtime steps are also recorded + // in the regular samples and we must not count them twice + // Note: we know that the DAO sample is never committed again, so we simply + // change the value here in memory. + sample.setSteps(getSteps()); + 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); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java index a160af8c..990fdc62 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java @@ -22,6 +22,7 @@ public abstract class RealtimeSamplesSupport { protected int steps; protected int heartrateBpm; + private int lastSteps; // subclasses may add more private Timer realtimeStorageTimer; @@ -56,12 +57,22 @@ public abstract class RealtimeSamplesSupport { return realtimeStorageTimer != null; } - public void setSteps(int stepsPerMinute) { + public synchronized void setSteps(int stepsPerMinute) { this.steps = stepsPerMinute; } - public int getSteps() { - return steps; + public synchronized int getSteps() { + if (lastSteps == 0) { + return 0; // wait until we have a delta between two samples + } + if (steps == ActivitySample.NOT_MEASURED) { + return 0; + } + int delta = steps - lastSteps; + if (delta < 0) { + return 0; + } + return delta; } public void setHeartrateBpm(int hrBpm) { @@ -77,7 +88,10 @@ public abstract class RealtimeSamplesSupport { resetCurrentValues(); } - protected void resetCurrentValues() { + protected synchronized void resetCurrentValues() { + if (steps >= lastSteps) { + lastSteps = steps; + } steps = ActivitySample.NOT_MEASURED; heartrateBpm = ActivitySample.NOT_MEASURED; } From da494cde7b773e89ae96a646e7592aa2abb64ccb Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 26 Dec 2016 11:33:01 +0100 Subject: [PATCH 051/244] Handle live activity for Mi2 and Mi1 in the same way #448 Realtime samples now use ActivitySample.NOT_MEASURED for unknown or invalid values. --- .../activities/charts/LiveActivityFragment.java | 12 ++++-------- .../service/devices/miband/MiBandSupport.java | 5 ++++- .../devices/miband/RealtimeSamplesSupport.java | 13 +++++++++---- .../service/devices/miband2/MiBand2Support.java | 14 ++++++++++++-- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index 23bef14b..ade11b84 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -72,8 +72,6 @@ public class LiveActivityFragment extends AbstractChartFragment { private TimestampTranslation tsTranslation; private class Steps { - private int initialSteps; - private int steps; private int lastTimestamp; private int currentStepsPerMinute; @@ -90,7 +88,7 @@ public class LiveActivityFragment extends AbstractChartFragment { } public int getTotalSteps() { - return steps - initialSteps; + return steps; } public int getMaxStepsPerMinute() { @@ -102,10 +100,6 @@ public class LiveActivityFragment extends AbstractChartFragment { if (steps == 0) { steps += stepsDelta; lastTimestamp = timestamp; - -// if (stepsDelta > 0) { -// initialSteps = stepsDelta; -// } return; } @@ -162,7 +156,9 @@ public class LiveActivityFragment extends AbstractChartFragment { setCurrentHeartRate(heartRate, timestamp); } int steps = sample.getSteps(); - addEntries(steps, timestamp); + if (steps != ActivitySample.NOT_MEASURED) { + addEntries(steps, timestamp); + } } private int translateTimestampFrom(Intent intent) { 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 33a81bc2..23d7bb23 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 @@ -953,7 +953,6 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { sample.setRawIntensity(ActivitySample.NOT_MEASURED); sample.setRawKind(MiBandSampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? - LOG.debug("Storing realtime sample: " + sample); provider.addGBActivitySample(sample); // set the steps only afterwards, since realtime steps are also recorded @@ -962,6 +961,10 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { // change the value here in memory. sample.setSteps(getSteps()); + if (LOG.isDebugEnabled()) { + LOG.debug("realtime sample: " + sample); + } + Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample); LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java index 990fdc62..f0b83c2e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java @@ -61,12 +61,17 @@ public abstract class RealtimeSamplesSupport { this.steps = stepsPerMinute; } + /** + * Returns the number of steps recorded since the last measurements. If no + * steps are available yet, ActivitySample.NOT_MEASURED is returned. + * @return + */ public synchronized int getSteps() { - if (lastSteps == 0) { - return 0; // wait until we have a delta between two samples - } if (steps == ActivitySample.NOT_MEASURED) { - return 0; + return ActivitySample.NOT_MEASURED; + } + if (lastSteps == 0) { + return ActivitySample.NOT_MEASURED; // wait until we have a delta between two samples } int delta = steps - lastSteps; if (delta < 0) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index a9b86646..a89ff44c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -982,12 +982,22 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { sample.setRawIntensity(ActivitySample.NOT_MEASURED); sample.setRawKind(MiBand2SampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? + provider.addGBActivitySample(sample); + + // set the steps only afterwards, since realtime steps are also recorded + // in the regular samples and we must not count them twice + // Note: we know that the DAO sample is never committed again, so we simply + // change the value here in memory. + sample.setSteps(getSteps()); + + if (LOG.isDebugEnabled()) { + LOG.debug("realtime sample: " + sample); + } + 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); } From bb8aff8c99226e5a1cc54a434cecf08fa48c53a3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 26 Dec 2016 12:51:20 +0100 Subject: [PATCH 052/244] Fix axis label color live activity (dark theme) --- .../gadgetbridge/activities/charts/LiveActivityFragment.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index ade11b84..3e757236 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -367,6 +367,9 @@ public class LiveActivityFragment extends AbstractChartFragment { // chart.getXAxis().setPosition(XAxis.XAxisPosition.TOP); chart.getXAxis().setDrawLabels(false); chart.getXAxis().setEnabled(false); + chart.getXAxis().setTextColor(CHART_TEXT_COLOR); + chart.getAxisLeft().setTextColor(CHART_TEXT_COLOR); + chart.setBackgroundColor(BACKGROUND_COLOR); chart.getDescription().setTextColor(DESCRIPTION_COLOR); chart.getDescription().setText(title); From 440a5e071fec3f3b59d1c54dc448854f04ded4e5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 26 Dec 2016 23:53:24 +0100 Subject: [PATCH 053/244] Try to support "Subsonic" android app (#474) --- .../externalevents/MusicPlaybackReceiver.java | 8 +++++++- .../gadgetbridge/service/DeviceCommunicationService.java | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index 3edfb881..a47c8f4d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -29,7 +29,13 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { MusicSpec musicSpec = new MusicSpec(); musicSpec.artist = intent.getStringExtra("artist"); musicSpec.album = intent.getStringExtra("album"); - musicSpec.track = intent.getStringExtra("track"); + if (intent.hasExtra("track")) { + musicSpec.track = intent.getStringExtra("track"); + } + else if (intent.hasExtra("title")) { + musicSpec.track = intent.getStringExtra("title"); + } + musicSpec.duration = intent.getIntExtra("duration", 0) / 1000; if (!lastMusicSpec.equals(musicSpec)) { 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 8c25a793..9e90bfea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -576,6 +576,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mMusicPlaybackReceiver = new MusicPlaybackReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("com.android.music.metachanged"); + filter.addAction("net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED"); //filter.addAction("com.android.music.playstatechanged"); registerReceiver(mMusicPlaybackReceiver, filter); } From 353bd4651bc26787f9c77a00565ffdc2fd3bf875 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 27 Dec 2016 12:20:59 +0100 Subject: [PATCH 054/244] Show Gadgetbridge in the "share" system dialog for installing firmwares, apps etc. --- app/src/main/AndroidManifest.xml | 9 +++++++++ .../gadgetbridge/activities/FwAppInstallerActivity.java | 3 +++ 2 files changed, 12 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 10d64c1f..bf45f5fb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -191,6 +191,15 @@ + + + + + + + + + Date: Tue, 27 Dec 2016 13:02:02 +0100 Subject: [PATCH 055/244] Pebble: support opening files from the system share intent --- .../devices/pebble/PBWReader.java | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index 53be8a97..508f3a7f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -2,7 +2,9 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import android.content.ContentResolver; import android.content.Context; +import android.database.Cursor; import android.net.Uri; +import android.provider.MediaStore; import org.json.JSONException; import org.json.JSONObject; @@ -57,6 +59,7 @@ public class PBWReader { private short mAppVersion; private int mIconId; private int mFlags; + private long fileSize; private JSONObject mAppKeys = null; @@ -66,7 +69,17 @@ public class PBWReader { InputStream fin = new BufferedInputStream(cr.openInputStream(uri)); - if (uri.toString().endsWith(".pbl")) { + Uri filePathUri = uri; + if (uri.getScheme().toString().compareTo("content") == 0) { + Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); + if (cursor.moveToFirst()) { + int name_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME); + int size_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE); + filePathUri = Uri.parse(cursor.getString(name_index)); + fileSize = cursor.getLong(size_index); + } + } + if (filePathUri.toString().endsWith(".pbl")) { STM32CRC stm32crc = new STM32CRC(); try { byte[] buf = new byte[2000]; @@ -83,10 +96,12 @@ public class PBWReader { int crc = stm32crc.getResult(); // language file app = new GBDeviceApp(UUID.randomUUID(), "Language File", "unknown", "unknown", GBDeviceApp.Type.UNKNOWN); - File f = new File(uri.getPath()); - + if (fileSize == 0) { + File f = new File(uri.getPath()); + fileSize = f.length(); + } pebbleInstallables = new ArrayList<>(); - pebbleInstallables.add(new PebbleInstallable("lang", (int) f.length(), crc, PebbleProtocol.PUTBYTES_TYPE_FILE)); + pebbleInstallables.add(new PebbleInstallable("lang", (int) fileSize, crc, PebbleProtocol.PUTBYTES_TYPE_FILE)); isValid = true; isLanguage = true; @@ -95,7 +110,7 @@ public class PBWReader { String platformDir = ""; - if (!uri.toString().endsWith(".pbz")) { + if (!filePathUri.toString().endsWith(".pbz")) { /* * for aplite and basalt it is possible to install 2.x apps which have no subfolder * we still prefer the subfolders if present. From fed56387828ebfcfc44011842762a8b13289ccba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 28 Dec 2016 13:50:56 +0000 Subject: [PATCH 056/244] HPlus: Refactor Sex into Gender and convert value appropriatelly --- .../gadgetbridge/devices/hplus/HPlusConstants.java | 4 ++-- .../gadgetbridge/devices/hplus/HPlusCoordinator.java | 7 +++++-- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index f69bb453..8cfe6a90 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -23,8 +23,8 @@ public final class HPlusConstants { public static final byte UNIT_METRIC = 0; public static final byte UNIT_IMPERIAL = 1; - public static final byte SEX_MALE = 0; - public static final byte SEX_FEMALE = 1; + public static final byte PREF_VALUE_GENDER_MALE = 0; + public static final byte PREF_VALUE_GENDER_FEMALE = 1; public static final byte HEARTRATE_MEASURE_ON = 11; public static final byte HEARTRATE_MEASURE_OFF = 22; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index d1cf8e25..b8848ebe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -177,10 +177,13 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return (byte) (activityUser.getAge() & 0xFF); } - public static byte getUserSex(String address) { + public static byte getUserGender(String address) { ActivityUser activityUser = new ActivityUser(); - return (byte) (activityUser.getGender() & 0xFF); + if (activityUser.getGender() == ActivityUser.GENDER_MALE) + return HPlusConstants.PREF_VALUE_GENDER_MALE; + + return HPlusConstants.PREF_VALUE_GENDER_FEMALE; } public static int getGoal(String address) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index c3704ffe..a944fa49 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -143,7 +143,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport syncPreferences(TransactionBuilder transaction) { LOG.info("Attempting to sync preferences..."); - byte sex = HPlusCoordinator.getUserSex(getDevice().getAddress()); + byte gender = HPlusCoordinator.getUserGender(getDevice().getAddress()); byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); @@ -167,8 +167,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_COUNTRY, - sex, + HPlusConstants.COMMAND_SET_PREFS, + gender, age, bodyHeight, bodyWeight, @@ -345,7 +345,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setSex(TransactionBuilder transaction) { LOG.info("Attempting to set Sex..."); - byte value = HPlusCoordinator.getUserSex(getDevice().getAddress()); + byte value = HPlusCoordinator.getUserGender(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.COMMAND_SET_PREF_SEX, value From a135f51d315d897413f609d661760ae3f137a5c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 28 Dec 2016 13:52:51 +0000 Subject: [PATCH 057/244] HPlus: Improve initial configuration process and refactor constants --- .../devices/hplus/HPlusConstants.java | 46 +++++++++---------- .../service/devices/hplus/HPlusSupport.java | 40 ++++++++-------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 8cfe6a90..d0f450fa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -14,35 +14,33 @@ public final class HPlusConstants { public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); - public static final byte COUNTRY_CN = 1; - public static final byte COUNTRY_OTHER = 2; + public static final byte PREF_VALUE_COUNTRY_CN = 1; + public static final byte PREF_VALUE_COUNTRY_OTHER = 2; - public static final byte CLOCK_24H = 0; - public static final byte CLOCK_12H = 1; + public static final byte PREF_VALUE_CLOCK_24H = 0; + public static final byte PREF_VALUE_CLOCK_12H = 1; - public static final byte UNIT_METRIC = 0; - public static final byte UNIT_IMPERIAL = 1; + public static final byte PREF_VALUE_UNIT_METRIC = 0; + public static final byte PREF_VALUE_UNIT_IMPERIAL = 1; public static final byte PREF_VALUE_GENDER_MALE = 0; public static final byte PREF_VALUE_GENDER_FEMALE = 1; - public static final byte HEARTRATE_MEASURE_ON = 11; - public static final byte HEARTRATE_MEASURE_OFF = 22; + public static final byte PREF_VALUE_HEARTRATE_MEASURE_ON = 11; + public static final byte PREF_VALUE_HEARTRATE_MEASURE_OFF = 22; - public static final byte HEARTRATE_ALLDAY_ON = 10; - public static final byte HEARTRATE_ALLDAY_OFF = -1; + public static final byte PREF_VALUE_HEARTRATE_ALLDAY_ON = 10; + public static final byte PREF_VALUE_HEARTRATE_ALLDAY_OFF = -1; - public static final byte[] COMMAND_SET_INIT1 = new byte[]{0x50,0x00,0x25,(byte) 0xb1,0x4a,0x00,0x00,0x27,0x10,0x05,0x02,0x00,(byte) 0xff,0x0a,(byte) 0xff,0x00,(byte) 0xff,(byte) 0xff,0x00,0x01}; - public static final byte[] COMMAND_SET_INIT2 = new byte[]{0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,(byte) 0xe0,0x0c,0x12,0x16,0x0a,0x10,0x00,0x00,0x00,0x00}; + public static final byte INCOMING_CALL_STATE_DISABLED_THRESHOLD = 0x7B; + public static final byte INCOMING_CALL_STATE_ENABLED = (byte) 0xAA; public static final byte[] COMMAND_SET_PREF_START = new byte[]{0x4f, 0x5a}; public static final byte[] COMMAND_SET_PREF_START1 = new byte[]{0x4d}; - public static final byte COMMAND_SET_PREF_COUNTRY = 0x22; public static final byte COMMAND_SET_PREF_TIMEMODE = 0x47; public static final byte COMMAND_SET_PREF_UNIT = 0x48; public static final byte COMMAND_SET_PREF_SEX = 0x2d; - public static final byte COMMAND_SET_PREF_DATE = 0x08; public static final byte COMMAND_SET_PREF_TIME = 0x09; public static final byte COMMAND_SET_PREF_WEEK = 0x2a; @@ -58,23 +56,16 @@ public final class HPlusConstants { public static final byte COMMAND_SET_PREF_END = 0x4f; public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; - public static final byte COMMAND_SET_INCOMING_CALL = 0x41; - public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; - public static final byte COMMAND_SET_CONF_SAVE = 0x17; public static final byte COMMAND_SET_CONF_END = 0x4f; - public static final byte COMMAND_SET_PREFS = 0x50; public static final byte COMMAND_SET_SIT_INTERVAL = 0x51; + public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; - public static final byte DATA_STATS = 0x33; - public static final byte DATA_STEPS = 0x36; - - public static final byte DATA_SLEEP = 0x1A; - + //Actions to device public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; @@ -84,6 +75,15 @@ public final class HPlusConstants { public static final byte COMMAND_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312? + //Incoming Messages + public static final byte DATA_STATS = 0x33; + public static final byte DATA_STEPS = 0x36; + public static final byte DATA_DAY_SUMMARY = 0x38; + public static final byte DATA_DAY_SUMMARY_ALT = 0x39; + public static final byte DATA_SLEEP = 0x1A; + public static final byte DATA_INCOMING_CALL_STATE = 0x18; + + public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index a944fa49..c93a4f65 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -107,10 +107,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getDevice().setFirmwareVersion2("0"); //Initialize device - setInitValues(builder); - setCurrentDate(builder); - setCurrentTime(builder); - syncPreferences(builder); + syncPreferences(builder); //Sync preferences + setSIT(builder); //Sync SIT Interval + setCurrentDate(builder); // Sync Current Date + setCurrentTime(builder); // Sync Current Time builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); @@ -121,14 +121,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return builder; } - private HPlusSupport setInitValues(TransactionBuilder builder) { - LOG.debug("Set Init Values"); - - builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT1); - builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT2); - return this; - } - private HPlusSupport sendUserInfo(TransactionBuilder builder) { builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START1); @@ -553,9 +545,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte state; if (enable) - state = HPlusConstants.HEARTRATE_ALLDAY_ON; + state = HPlusConstants.PREF_VALUE_HEARTRATE_ALLDAY_ON; else - state = HPlusConstants.HEARTRATE_ALLDAY_OFF; + state = HPlusConstants.PREF_VALUE_HEARTRATE_ALLDAY_OFF; builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, state}); builder.queue(getQueue()); @@ -813,7 +805,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return processSleepStats(data); case HPlusConstants.DATA_STEPS: return processStepStats(data); - + case HPlusConstants.DATA_DAY_SUMMARY: + case HPlusConstants.DATA_DAY_SUMMARY_ALT: + return processDaySummary(data); + case HPlusConstants.DATA_INCOMING_CALL_STATE: + return processIncomingCallState(data); default: LOG.info("Unhandled characteristic changed: " + characteristicUUID); @@ -821,18 +817,25 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return false; } + private boolean processIncomingCallState(byte[] data){ + LOG.debug("Process Incoming Call State"); + //Disabled now + return true; + } /* Receives a message containing the status of the day. */ - private boolean processDayStats(byte[] data) { + private boolean processDaySummary(byte[] data) { + LOG.debug("Process Day Summary"); + int a = data[4] * 256 + data[5]; if (a < 144) { int slot = a * 2; // 10 minute slots as an offset from 0:00 AM - int avgHR = data[1]; //Average Heart Rate + int avgHR = data[1]; //Average Heart Rate ? int steps = data[2] * 256 + data[3]; // Steps in this period //?? data[6]; - int timeInactive = data[7]; + int timeInactive = data[7]; // ? LOG.debug("Day Stats: Slot: " + slot + " HR: " + avgHR + " Steps: " + steps + " TimeInactive: " + timeInactive); @@ -997,6 +1000,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return true; } + public HPlusHealthActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { HPlusHealthActivitySample sample = new HPlusHealthActivitySample(); sample.setDevice(device); From 9cea2fc3bdc95013bad8fd335006925e05b4fc59 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 28 Dec 2016 22:54:07 +0100 Subject: [PATCH 058/244] Update changelog for 0.16 --- CHANGELOG.md | 10 ++++++---- app/src/main/res/xml/changelog_master.xml | 9 ++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 949564e4..24985ba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ ###Changelog ####Version 0.16.0 -* New device: ZeBand -* ZeBand: Initial experimental support -* Pebble 2: Fix Pebble Classic FW 3.x app variant being priorized over native Pebble 2 app variant - +* New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca +* ZeBand: Initial support: notifications, heart rate, sleep monitoring, user configuration, date+time +* Pebble 2: Fix Pebble Classic FW 3.x app variant being prioritized over native Pebble 2 app variant +* Charts (Live Activity): Fix axis labels color in dark theme +* Mi Band: Fix ginormous step count when using Live Activity +* Mi Band: Improved performance during activity sync ####Version 0.15.2 * Mi Band: Fix crash with unknown notification sources diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 7b1b8cb0..94692336 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,9 +1,12 @@ - New device: ZeBand - ZeBand: Initial experimental support - Pebble 2: Fix Pebble Classic FW 3.x app variant being priorized over native Pebble 2 app variant + New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca + ZeBand: Initial support: notifications, heart rate, sleep monitoring, user configuration, date+time + Pebble 2: Fix Pebble Classic FW 3.x app variant being prioritized over native Pebble 2 app variant + Charts (Live Activity): Fix axis labels color in dark theme + Mi Band: Fix ginormous step count when using Live Activity + Mi Band: Improved performance during activity sync Mi Band: Fix crash with unknown notification sources From 0646eda646c78d01a139adc072cf9096a5d945df Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 01:07:26 +0100 Subject: [PATCH 059/244] Fix file potential handle leaks --- .../gadgetbridge/util/FileUtils.java | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java index 3898818e..66de19b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -46,14 +47,20 @@ public class FileUtils { } } + /** + * Copies the contents of the given input stream to the destination file. + * @param inputStream the contents to write. Note: the caller has to close the input stream! + * @param destFile the file to write to + * @throws IOException + */ public static void copyStreamToFile(InputStream inputStream, File destFile) throws IOException { - FileOutputStream fout = new FileOutputStream(destFile); - byte[] buf = new byte[4096]; - while (inputStream.available() > 0) { - int bytes = inputStream.read(buf); - fout.write(buf, 0, bytes); + try (FileOutputStream fout = new FileOutputStream(destFile)) { + byte[] buf = new byte[4096]; + while (inputStream.available() > 0) { + int bytes = inputStream.read(buf); + fout.write(buf, 0, bytes); + } } - fout.close(); } public static void copyURItoFile(Context ctx, Uri uri, File destFile) throws IOException { @@ -62,29 +69,47 @@ public class FileUtils { } ContentResolver cr = ctx.getContentResolver(); - InputStream fin; - try { - fin = new BufferedInputStream(cr.openInputStream(uri)); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return; + InputStream in = cr.openInputStream(uri); + if (in == null) { + throw new IOException("unable to open input stream: " + uri); + } + try (InputStream fin = new BufferedInputStream(in)) { + copyStreamToFile(fin, destFile); + fin.close(); } - copyStreamToFile(fin, destFile); - fin.close(); } + /** + * Returns the textual contents of the given file. The contents is expected to be + * in UTF-8 encoding. + * @param file the file to read + * @return the file contents as a newline-delimited string + * @throws IOException + * @see #getStringFromFile(File, String) + */ public static String getStringFromFile(File file) throws IOException { + return getStringFromFile(file, StandardCharsets.UTF_8.name()); + } + + /** + * Returns the textual contents of the given file. The contents will be interpreted using the + * given encoding. + * @param file the file to read + * @return the file contents as a newline-delimited string + * @throws IOException + * @see #getStringFromFile(File) + */ + public static String getStringFromFile(File file, String encoding) throws IOException { FileInputStream fin = new FileInputStream(file); - BufferedReader reader = new BufferedReader(new InputStreamReader(fin)); - StringBuilder sb = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(fin, encoding))) { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + return sb.toString(); } - reader.close(); - fin.close(); - return sb.toString(); } /** From a96a747119ae6762bd84e964cd6b78b20b397162 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 01:29:28 +0100 Subject: [PATCH 060/244] Pebble: fix resource leak on app installation --- .../devices/pebble/PBWInstallHandler.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java index 70ed7f37..7e9bf7e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java @@ -60,6 +60,10 @@ public class PBWInstallHandler implements InstallHandler { installActivity.setInfoText("file not found"); installActivity.setInstallEnabled(false); return; + } catch (IOException e) { + installActivity.setInfoText("error reading file"); + installActivity.setInstallEnabled(false); + return; } if (!mPBWReader.isValid()) { @@ -168,18 +172,22 @@ public class PBWInstallHandler implements InstallHandler { } InputStream jsConfigFile = mPBWReader.getInputStreamFile("pebble-js-app.js"); - if (jsConfigFile != null) { - outputFile = new File(destDir, app.getUUID().toString() + "_config.js"); try { + outputFile = new File(destDir, app.getUUID().toString() + "_config.js"); FileUtils.copyStreamToFile(jsConfigFile, outputFile); } catch (IOException e) { LOG.error("Failed to open output file: " + e.getMessage(), e); + } finally { + try { + jsConfigFile.close(); + } catch (IOException e) { + } } } - } + @Override public boolean isValid() { // always pretend it is valid, as we can't know yet about hw/fw version return true; From aadde7d1cad5a2b147b6f909af8e9260b93e4ca4 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 01:41:47 +0100 Subject: [PATCH 061/244] Port to UriHelper, fix potential resource leak --- .../devices/pebble/PBWReader.java | 177 ++++++++---------- .../devices/pebble/PebbleIoThread.java | 5 +- .../gadgetbridge/util/UriHelper.java | 150 +++++++++++++++ 3 files changed, 227 insertions(+), 105 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index 508f3a7f..50f8ef9d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -1,19 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; -import android.content.ContentResolver; import android.content.Context; -import android.database.Cursor; import android.net.Uri; -import android.provider.MediaStore; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -28,6 +23,7 @@ import java.util.zip.ZipInputStream; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.UriHelper; public class PBWReader { private static final Logger LOG = LoggerFactory.getLogger(PBWReader.class); @@ -47,8 +43,7 @@ public class PBWReader { fwFileTypesMap.put("resources", PebbleProtocol.PUTBYTES_TYPE_SYSRESOURCES); } - private final Uri uri; - private final ContentResolver cr; + private final UriHelper uriHelper; private GBDeviceApp app; private ArrayList pebbleInstallables = null; private boolean isFirmware = false; @@ -59,121 +54,48 @@ public class PBWReader { private short mAppVersion; private int mIconId; private int mFlags; - private long fileSize; private JSONObject mAppKeys = null; - public PBWReader(Uri uri, Context context, String platform) throws FileNotFoundException { - this.uri = uri; - cr = context.getContentResolver(); + public PBWReader(Uri uri, Context context, String platform) throws IOException { + uriHelper = UriHelper.get(uri, context); - InputStream fin = new BufferedInputStream(cr.openInputStream(uri)); - - Uri filePathUri = uri; - if (uri.getScheme().toString().compareTo("content") == 0) { - Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); - if (cursor.moveToFirst()) { - int name_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME); - int size_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE); - filePathUri = Uri.parse(cursor.getString(name_index)); - fileSize = cursor.getLong(size_index); - } - } - if (filePathUri.toString().endsWith(".pbl")) { + if (uriHelper.getFileName().endsWith(".pbl")) { STM32CRC stm32crc = new STM32CRC(); - try { + try (InputStream fin = uriHelper.openInputStream()) { byte[] buf = new byte[2000]; while (fin.available() > 0) { int count = fin.read(buf); stm32crc.addData(buf, count); } - fin.close(); - } catch (IOException e) { - e.printStackTrace(); - return; } - int crc = stm32crc.getResult(); // language file app = new GBDeviceApp(UUID.randomUUID(), "Language File", "unknown", "unknown", GBDeviceApp.Type.UNKNOWN); - if (fileSize == 0) { - File f = new File(uri.getPath()); - fileSize = f.length(); - } pebbleInstallables = new ArrayList<>(); - pebbleInstallables.add(new PebbleInstallable("lang", (int) fileSize, crc, PebbleProtocol.PUTBYTES_TYPE_FILE)); + pebbleInstallables.add(new PebbleInstallable("lang", (int) uriHelper.getFileSize(), crc, PebbleProtocol.PUTBYTES_TYPE_FILE)); isValid = true; isLanguage = true; return; } - String platformDir = ""; - - if (!filePathUri.toString().endsWith(".pbz")) { - /* - * for aplite and basalt it is possible to install 2.x apps which have no subfolder - * we still prefer the subfolders if present. - * chalk needs to be its subfolder - */ - String[] platformDirs; - switch (platform) { - case "basalt": - platformDirs = new String[]{"basalt/"}; - break; - case "chalk": - platformDirs = new String[]{"chalk/"}; - break; - case "diorite": - platformDirs = new String[]{"diorite/", "aplite/"}; - break; - case "emery": - platformDirs = new String[]{"emery/", "basalt/"}; - break; - default: - platformDirs = new String[]{"aplite/"}; - } - - for (String dir : platformDirs) { - InputStream afin = new BufferedInputStream(cr.openInputStream(uri)); - - ZipInputStream zis = new ZipInputStream(afin); - ZipEntry ze; - boolean found = false; - try { - while ((ze = zis.getNextEntry()) != null) { - if (ze.getName().startsWith(dir)) { - platformDir = dir; - found = true; - break; - } - } - zis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - if (found) { - break; - } - } - - if (platform.equals("chalk") && platformDir.equals("")) { - return; - } + String platformDir = determinePlatformDir(uriHelper, platform); + if (platform.equals("chalk") && platformDir.equals("")) { + return; } + LOG.info("using platformdir: '" + platformDir + "'"); String appName = null; String appCreator = null; String appVersion = null; UUID appUUID = null; - ZipInputStream zis = new ZipInputStream(fin); ZipEntry ze; pebbleInstallables = new ArrayList<>(); byte[] buffer = new byte[1024]; int count; - - try { + try (ZipInputStream zis = new ZipInputStream(uriHelper.openInputStream())) { while ((ze = zis.getNextEntry()) != null) { String fileName = ze.getName(); if (fileName.equals(platformDir + "manifest.json")) { @@ -269,7 +191,6 @@ public class PBWReader { // more follows but, not interesting for us } } - zis.close(); if (appUUID != null && appName != null && appCreator != null && appVersion != null) { GBDeviceApp.Type appType = GBDeviceApp.Type.APP_GENERIC; @@ -280,11 +201,58 @@ public class PBWReader { } app = new GBDeviceApp(appUUID, appName, appCreator, appVersion, appType); } - } catch (IOException e) { - e.printStackTrace(); } } + /** + * Determines the platform dir to use for the given uri and platform. + * @param uriHelper + * @param platform + * @return the platform dir to use + * @throws IOException + */ + private String determinePlatformDir(UriHelper uriHelper, String platform) throws IOException { + String platformDir = ""; + + if (uriHelper.getFileName().endsWith(".pbz")) { + return platformDir; + } + /* + * for aplite and basalt it is possible to install 2.x apps which have no subfolder + * we still prefer the subfolders if present. + * chalk needs to be its subfolder + */ + String[] platformDirs; + switch (platform) { + case "basalt": + platformDirs = new String[]{"basalt/"}; + break; + case "chalk": + platformDirs = new String[]{"chalk/"}; + break; + case "diorite": + platformDirs = new String[]{"diorite/", "aplite/"}; + break; + case "emery": + platformDirs = new String[]{"emery/", "basalt/"}; + break; + default: + platformDirs = new String[]{"aplite/"}; + } + + for (String dir : platformDirs) { + try (ZipInputStream zis = new ZipInputStream(uriHelper.openInputStream())) { + ZipEntry ze; + while ((ze = zis.getNextEntry()) != null) { + if (ze.getName().startsWith(dir)) { + return dir; + } + } + } + } + return platformDir; + } + public boolean isFirmware() { return isFirmware; } @@ -302,28 +270,29 @@ public class PBWReader { } public InputStream getInputStreamFile(String filename) { - InputStream fin; - try { - fin = new BufferedInputStream(cr.openInputStream(uri)); - if (isLanguage) { - return fin; + if (isLanguage) { + try { + return uriHelper.openInputStream(); + } catch (FileNotFoundException e) { + LOG.warn("file not found: " + e); + return null; } - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; } - ZipInputStream zis = new ZipInputStream(fin); + ZipInputStream zis = null; ZipEntry ze; try { + zis = new ZipInputStream(uriHelper.openInputStream()); while ((ze = zis.getNextEntry()) != null) { if (ze.getName().equals(filename)) { - return zis; + return zis; // return WITHOUT closing the stream! } } zis.close(); } catch (Throwable e) { try { - zis.close(); + if (zis != null) { + zis.close(); + } } catch (IOException e1) { // ignore } 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 bd990d2d..035fa17b 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 @@ -623,7 +623,10 @@ class PebbleIoThread extends GBDeviceIoThread { try { mPBWReader = new PBWReader(uri, getContext(), platformName); } catch (FileNotFoundException e) { - LOG.warn("file not found!"); + LOG.warn("file not found: " + e.getMessage(), e); + return; + } catch (IOException e) { + LOG.warn("unable to read file: " + e.getMessage(), e); return; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java new file mode 100644 index 00000000..9c5d9b67 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java @@ -0,0 +1,150 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class UriHelper { + @NonNull + private final Uri uri; + @NonNull + private final Context context; + private String fileName; + private long fileSize; + @Nullable + private File file; + + private UriHelper(@NonNull Uri uri, @NonNull Context context) { + this.uri = uri; + this.context = context; + } + + /** + * Returns the uri as passed to #get(Uri, Context) + */ + @NonNull + public Uri getUri() { + return uri; + } + + /** + * Returns the context as passed to #get(Uri, Context) + */ + @NonNull + public Context getContext() { + return context; + } + + /** + * Returns an immutable helper to access the given Uri. In case the uri cannot be read/resolved + * an IOException is thrown. + * @param uri the uri to access + * @param context the context for accessing uris + * @throws IOException + */ + @NonNull + public static UriHelper get(@NonNull Uri uri, @NonNull Context context) throws FileNotFoundException, IOException { + UriHelper helper = new UriHelper(uri, context); + helper.resolveMetadata(); + return helper; + } + + /** + * Opens a stream to read the contents of the uri. + * Note: the caller has to close the stream after usage. + * Every invocation of this method will open a new stream. + * @throws FileNotFoundException + */ + @NonNull + public InputStream openInputStream() throws FileNotFoundException { + ContentResolver cr = context.getContentResolver(); + InputStream inputStream = cr.openInputStream(uri); + if (inputStream != null) { + return new BufferedInputStream(inputStream); + } + throw new FileNotFoundException("Unable to open inputstream for " + uri); + } + + /** + * Returns the content length (file size) in bytes + */ + public long getFileSize() { + return fileSize; + } + + /** + * Returns the name of the file referenced by the Uri. Does not include the path. + */ + @NonNull + public String getFileName() { + return fileName; + } + + /** + * Returns the file behind the uri, or null in case it is not a file:/ Uri. + * @return the file or null + */ + @Nullable + public File getFile() { + return file; + } + + private void resolveMetadata() throws IOException { + String uriScheme = uri.getScheme(); + if (ContentResolver.SCHEME_CONTENT.equals(uriScheme)) { + Cursor cursor = context.getContentResolver().query( + uri, + new String[] { + MediaStore.MediaColumns.DISPLAY_NAME, + MediaStore.MediaColumns.SIZE + }, null, null, null); + if (cursor == null) { + throw new IOException("Unable to query metadata for: " + uri); + } + if (cursor.moveToFirst()) { + int name_index = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME); + if (name_index == -1) { + throw new IOException("Unable to retrieve name for: " + uri); + } + int size_index = cursor.getColumnIndex(MediaStore.MediaColumns.SIZE); + if (size_index == -1) { + throw new IOException("Unable to retrieve size for: " + uri); + } + try { + fileName = cursor.getString(name_index); + if (fileName == null) { + throw new IOException("Unable to retrieve name for: " + uri); + } + fileSize = cursor.getLong(size_index); + if (fileSize < 0) { + throw new IOException("Unable to retrieve size for: " + uri); + } + } catch (Exception ex) { + throw new IOException("Unable to retrieve metadata for: " + uri + ": " + ex.getMessage()); + } + } + } else if (ContentResolver.SCHEME_FILE.equals(uriScheme)) { + file = new File(uri.getPath()); + if (!file.exists()) { + throw new FileNotFoundException("Does not exist: " + file); + } + fileName = file.getName(); + fileSize = file.length(); + } else if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uriScheme)) { + // we could actually read it, but I don't see how we can determine the file size + throw new IOException("Unsupported scheme for uri: " + uri); + } else { + throw new IOException("Unsupported scheme for uri: " + uri); + } + } +} From df1fe7c5b84bdca0eeeff3208e7a3459142e7d51 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 01:46:00 +0100 Subject: [PATCH 062/244] Port to UriHelper --- .../gadgetbridge/devices/miband/AbstractMiBandFWHelper.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java index b328bafa..76faf56e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java @@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.UriHelper; /** * Also see Mi1SFirmwareInfo. @@ -26,12 +27,13 @@ public abstract class AbstractMiBandFWHelper { private final byte[] fw; public AbstractMiBandFWHelper(Uri uri, Context context) throws IOException { + UriHelper uriHelper = UriHelper.get(uri, context); String pebblePattern = ".*\\.(pbw|pbz|pbl)"; - if (uri.getPath().matches(pebblePattern)) { + if (uriHelper.getFileName().matches(pebblePattern)) { throw new IOException("Firmware has a filename that looks like a Pebble app/firmware."); } - try (InputStream in = new BufferedInputStream(context.getContentResolver().openInputStream(uri))) { + try (InputStream in = new BufferedInputStream(uriHelper.openInputStream())) { this.fw = FileUtils.readAll(in, 1024 * 1024); // 1 MB determineFirmwareInfo(fw); } catch (IOException ex) { From 5f48b89dc5149d20142a78cd598fc37276b76965 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 29 Dec 2016 17:12:44 +0100 Subject: [PATCH 063/244] Update changelog --- CHANGELOG.md | 2 ++ app/src/main/res/xml/changelog_master.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24985ba3..6d885865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * Charts (Live Activity): Fix axis labels color in dark theme * Mi Band: Fix ginormous step count when using Live Activity * Mi Band: Improved performance during activity sync +* Support sharing firmwares/watchapps/watchfaces to Gadgetbridge + ####Version 0.15.2 * Mi Band: Fix crash with unknown notification sources diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 94692336..669064ea 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -7,6 +7,7 @@ Charts (Live Activity): Fix axis labels color in dark theme Mi Band: Fix ginormous step count when using Live Activity Mi Band: Improved performance during activity sync + Support sharing firmwares/watchapps/watchfaces to Gadgetbridge Mi Band: Fix crash with unknown notification sources From b1914a140c6b921c9c42b3ec01f92f22199eed7e Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 29 Dec 2016 17:14:14 +0100 Subject: [PATCH 064/244] Update contributors list and script (so that it only counts contributions to master branch) --- CONTRIBUTORS.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index e6698fda..ca95966b 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -2,7 +2,7 @@ names () { echo -e "\n exit;\n**Contributors (sorted by number of commits):**\n"; - git log --all --format='%aN:%aE' | sed 's/@users.github.com/@users.noreply.github.com/g' | awk 'BEGIN{FS=":"}{ct[$2]+=1;if (length($1) > length(e[$2])) {e[$2]=$1}}END{for (i in e) { n[e[i]]=i;c[e[i]]+=ct[i] }; for (a in n) print c[a]"\t* "a" <"n[a]">";}' | sort -n -r | cut -f 2- + git log --format='%aN:%aE' origin/master | sed 's/@users.github.com/@users.noreply.github.com/g' | awk 'BEGIN{FS=":"}{ct[$2]+=1;if (length($1) > length(e[$2])) {e[$2]=$1}}END{for (i in e) { n[e[i]]=i;c[e[i]]+=ct[i] }; for (a in n) print c[a]"\t* "a" <"n[a]">";}' | sort -n -r | cut -f 2- } quine () { @@ -26,15 +26,17 @@ * Carsten Pfeiffer * Daniele Gobbetti * Julien Pivotto +* João Paulo Barraca * Steffen Liebergeld * Lem Dulfo * Sergey Trofimov * JohnnySun * Uwe Hermann -* Gergely Peidl * 0nse <0nse@users.noreply.github.com> +* Gergely Peidl * Christian Fischer * Normano64 +* 6arms1leg * Ⲇⲁⲛⲓ Φi * xzovy * xphnx @@ -56,7 +58,6 @@ * atkyritsis * andre * Alexey Afanasev -* 6arms1leg And all the Transifex translators, which I cannot automatically list, at the moment. From e77c4e7bdb74bca3e101ee5b9e36b7caabbcaa63 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 21:08:52 +0100 Subject: [PATCH 065/244] Mention ZeBand as supported device --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 85dd7da0..53585a88 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ need to create an account and transmit any of your data to the vendor's servers. * Mi Band 2 * Vibratissimo (experimental) * Liveview +* HPlus Devices (e.g. ZeBand) ## Features (Pebble) From 5d3c45d2c0063782e63b59ff8e28832e9f2b3663 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 23:23:20 +0100 Subject: [PATCH 066/244] Mi2: Remember and use last synced timestamp in preferences (instead of using the last sample's timestamp in the database. The database also contains manual hr measurements and live activity samples, so we would miss activity data before the last manual measurement. Closes #478 --- .../operations/FetchActivityOperation.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 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 7f32886e..cc70c439 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 @@ -2,6 +2,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; import android.widget.Toast; import org.slf4j.Logger; @@ -23,7 +25,6 @@ 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; @@ -73,7 +74,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { builder.notify(characteristicFetch, true); BluetoothGattCharacteristic characteristicActivityData = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA); - GregorianCalendar sinceWhen = getLastSuccessfulSynchronizedTime(); + GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE, 0x01 }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES))); builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply builder.notify(characteristicActivityData, true); @@ -81,26 +82,28 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { builder.queue(getQueue()); } - 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((long) timestamp * 1000); - return calendar; - } - } catch (Exception ex) { - LOG.error("Error querying for latest activity sample, synchronizing the last 10 days", ex); + private GregorianCalendar getLastSuccessfulSyncTime() { + long timeStampMillis = GBApplication.getPrefs().getLong(getLastSyncTimeKey(), 0); + if (timeStampMillis != 0) { + GregorianCalendar calendar = BLETypeConversions.createCalendar(); + calendar.setTimeInMillis(timeStampMillis); + return calendar; } - GregorianCalendar calendar = BLETypeConversions.createCalendar(); calendar.add(Calendar.DAY_OF_MONTH, -10); return calendar; } + private void saveLastSyncTimestamp(@NonNull GregorianCalendar timestamp) { + SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit(); + editor.putLong(getLastSyncTimeKey(), timestamp.getTimeInMillis()); + editor.apply(); + } + + private String getLastSyncTimeKey() { + return getDevice().getAddress() + "_" + "lastSyncTimeMillis"; + } + @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { @@ -147,6 +150,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { } sampleProvider.addGBActivitySamples(samples.toArray(new MiBandActivitySample[0])); + saveLastSyncTimestamp(timestamp); LOG.info("Mi2 activity data: last sample timestamp: " + DateTimeUtils.formatDateTime(timestamp.getTime())); } catch (Exception ex) { From bf777800d2ec8e45bf663d2bc613c3d08f74407a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 23:27:49 +0100 Subject: [PATCH 067/244] Update changelog for #478 --- 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 6d885865..36554d68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Charts (Live Activity): Fix axis labels color in dark theme * Mi Band: Fix ginormous step count when using Live Activity * Mi Band: Improved performance during activity sync +* Mi Band 2: Fix activity data missing after doing manual hr measurements or live activity * Support sharing firmwares/watchapps/watchfaces to Gadgetbridge ####Version 0.15.2 diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 669064ea..41cf05c7 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -7,6 +7,7 @@ Charts (Live Activity): Fix axis labels color in dark theme Mi Band: Fix ginormous step count when using Live Activity Mi Band: Improved performance during activity sync + Mi Band 2: Fix activity data missing after doing manual hr measurements or live activity Support sharing firmwares/watchapps/watchfaces to Gadgetbridge From cde3b369682569628cf62cb4b2f592f68e76c88b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 23:30:18 +0100 Subject: [PATCH 068/244] Updated translations from transifex (thanks!) --- app/src/main/res/values-fr/strings.xml | 107 ++++++++++++++----------- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 5 +- app/src/main/res/values-pl/strings.xml | 78 +++++++++++++++++- app/src/main/res/values-ru/strings.xml | 5 +- app/src/main/res/values-uk/strings.xml | 5 +- 6 files changed, 144 insertions(+), 58 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f634a340..46d61389 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -6,12 +6,13 @@ Déboguer Quitter Synchroniser - Moniteur de sommeil (ALPHA) - Trouver l\'appareil + Suivi du sommeil (ALPHA) + Retrouver un appareil perdu... Prendre une capture d\'écran Déconnexion - Supprimer l\'appareil - Supprimer %1$s + Supprimer l’appareil + Supprimer %1$s + Ceci va supprimer l’appareil et toutes les données associées ! Déboguer Gestionnaire d\'application @@ -21,8 +22,11 @@ Supprimer Supprimer et effacer du cache Réinstaller + Rechercher dans le magasin d’application Pebble Activer Désactiver + Activer la mesure du rythme cardiaque + Désactiver la mesure du rythme cardiaque Configurer Haut de page @@ -32,7 +36,7 @@ Vous êtes sur le point d\'installer le micrologiciel %s à la place de celui qui est actuellement sur votre Mi Band. Vous êtes sur le point d\'installer les micrologiciels %1$s et %2$s à la place de ceux qui sont actuellement sur votre Mi Band. Ce micrologiciel a été testé et est connu pour être compatible avec Gadgetbridge. - Ce micrologiciel n\'a pas été testé et peut ne pas être compatible avec Gadgetbridge.\n\nIl n\'est pas conseillé de flasher votre Mi Band. + Ce micrologiciel n\'a pas été testé et peut ne pas être compatible avec Gadgetbridge.\n\nIl n\'est pas conseillé de le flasher sur votre Mi Band. Si vous désirez continuer et que tout fonctionne correctement par la suite, SVP informez-en les développeurs de Gadgetbridge pour demander l\'ajout de ce micrologiciel à leur liste: %s Paramètres @@ -51,47 +55,53 @@ Notifications Répétitions Appels téléphoniques - SMS + Textos K9-Mail Messages Pebble Support des applications qui envoient des notification à Pebble par PebbleKit. Support des notifications génériques ... y compris quand l\'écran est allumé Ne Pas Déranger - Arrêter l’envoie des Notification non-désirée en mode Ne Pas Déranger. + Arrêter l’envoi des notifications non-désirées en mode Ne Pas Déranger. toujours quand l\'écran est éteint jamais Applications bloquées Modèles de messages Réponses - Suffixe commun + Suffixe fréquent Raccrocher Mise à jour Pebble Options développeur Adresse Mi Band Paramètres Pebble - Traqueur d\'activité + Traqueurs d\'activité Traqueur d\'activité préféré Synchroniser Pebble Health Synchroniser Misfit Synchroniser Morpheuz - Permettre l\'accès d\'applications tierces Android + Permettre l\'accès aux applications tierces Android Activer le support expérimental pour les applications Android utilisant PebbleKit Lever et coucher de soleil - Envoyer heures de lever et coucher du soleil dans la timeline Pebble en fonction de l\'emplacement + Envoyer les heures de lever et coucher du soleil dans l’historique Pebble en fonction de l\'emplacement Emplacement 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 + Garder l’emplacement à jour + Essayer de garder la localisation à jour pendant le fonctionnement, sinon utiliser l’emplacement enregistré. + Veuillez activer la localisation réseau Emplacement obtenu - Protocole des notifications en vigueur + Forcer le protocole de notification Cette option force l\'utilisation du plus récent protocole de notification qui dépend de la version du micrologiciel. ACTIVEZ-LA UNIQUEMENT SI VOUS SAVEZ CE QUE VOUS FAITES! Activer les fonctionnalités non-testées Activer les fonctionnalités non-testées. ACTIVEZ UNIQUEMENT SI VOUS SAVEZ CE QUE VOUS FAITES! + Toujours préférer le BLE + Utiliser le support expérimental du LE pour toutes les Pebble au lieu du Bluetooth classique ; cela requiert l’appairage d\'une \"Pebble LE\" après qu’une non-LE ai déjà été connectée + Limite du GATT MTU de Pebble 2/LE + Si votre Pebble 2/LE ne fonctionne pas correctement, essayez d\'activer cette option pour limiter le MTU (plage valide 20-512) + Activer les logs des Watch App + Ceci permettra à Gadgetbridge de conserver les logs des Watch App (requiert une reconnexion) Tentatives de reconnexion non connecté connexion en cours @@ -106,29 +116,33 @@ Le Bluetooth n\'est pas supporté. Le Bluetooth est désactivé. Cliquez sur l\'appareil pour ouvrir le gestionnaire d\'application - Cliquez sur l\'appareil pour ouvrir l\'activité + Cliquez sur l\'appareil pour ouvrir le gestionnaire d’activité Cliquez sur connecter pour envoyer une vibration Tapotter sur le périphérique pour le connecter. - Connexion impossible. L’adresse Bluetooth est-elle invalide? + Connexion impossible. L’adresse Bluetooth est-elle valide? Gadgetbridge est en fonctionnement - Installation du binaire %1$d/%2$d - échec d\'installation! - Installation réalisé + Installation du fichier %1$d/%2$d + échec de l\'installation ! + Installation réalisée avec succès VOUS TENTEZ D\'INSTALLER UN MICROLOGICIEL, PROCÉDEZ À VOS PROPRES RISQUES.\n\n\nCe micrologiciel est pour la version de matériel: %s Vous êtes sur le point d\'installer l\'application suivante:\n\n\n%1$s Version %2$s par %3$s\n N.D. Initialisé %1$s par %2$s - Découvrir les appareils - Arrêter le balayage - Démarrer le balayage + Scanner les appareils + Arrêter le scan + Démarrer le scan Connecter un nouvel appareil %1$s (%2$s) - Coupler l\'appareil - Utiliser le couplement Bluetooth d\'Android pour coupler l\'appareil - Coupler votre Mi Band - Coupler avec %s... - Aucune adresse mac fournie, ne peut être couplé + Appairer l\'appareil + Utiliser l’appairage Bluetooth d\'Android pour appairer l\'appareil + Appairer votre Mi Band + Appairage avec %s... + Création d’un lien avec %1$s (%2$s) + Impossible se s’appairer avec %1$s (%2$s) + Création du lien en cours : %1$s (%2$s) + Déjà lié avec %1$s (%2$s), connexion... + Aucune adresse MAC fournie, ne peut être appairé Paramètres spécifiques à l\'appareil Paramètres Mi Band Homme @@ -139,7 +153,7 @@ Aucune donnée utilisateur valide fournie, utilisation de données fictives pour le moment Quand votre Mi Band vibre et clignote, appuyez dessus plusieurs fois d\'affilée. Installer - Rendre votre montre découvrable. Pour l\'instant les appareils connectés ne seront probablement pas découvert. Si votre montre n’apparaît pas après 2 minutes, essayer à nouveau après avoir redémarré. + Mettez votre appareil en mode visible. Les appareils déjà connectés ne sont pas visibles. Sur Android 6 ou supérieur, vous devez activer la localisation (ex. GPS). Si votre appareil n\'est pas visible après 2 minutes, réessayez après avoir redémarré votre téléphone. Note: Image de l\'appareil Nom/Pseudo @@ -149,7 +163,7 @@ Initialisation Récupération des données d\'activité De %1$s à %2$s - Côté de port du bracelet + Port main gauche ou droite? Profil de vibration Saccadé Court @@ -159,15 +173,15 @@ Sonnette Réveil Vibration - Notification SMS - Clavardage (Chat) - Navigation - Réseaux sociaux + Essayer + Notification Texto Paramètres des vibrations Notification générique - Notification Pebble - Notification e-mail + Notification des Mails Notification d\'appels entrants + Tchat + Navigation + Réseau social Trouver l\'appareil perdu Annuler pour arrêter les vibrations Votre activité @@ -193,7 +207,7 @@ Impossible de trouver un gestionnaire pour installer ce fichier. Impossible d\'installer le ficher suivant: %1$s Impossible d\'installer le micrologiciel spécifié: il ne correspond pas à la version du matériel de votre Pebble. - S\'il vous plait patientez pendant la détermination de l\'état de l\'installation... + Veuillez patienter pendant la détermination de l\'état de l\'installation... Niveau de batterie faible! %1$s batterie restante: %2$s%% Dernière charge: %s \n @@ -218,7 +232,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. @@ -241,11 +255,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 - - Allumer quand le poignet se lève - About to transfer data since %1$s + Mi2 : Format de la date + Heure + + Allumer l\'écran lors d\'un mouvement + Sur le point de transférer des données depuis %1$s en attente de reconnexion A propos de vous Année de naissance @@ -256,7 +270,7 @@ authentification requise ZzZz Ajouter un widget - Durée préférée de sommeil en heures + Préférer le mode heure pendant le sommeil Une alarme a été enregistré pour %1$02d:%2$02d Modèle: %1$s Micrologiciel: %1$s @@ -304,11 +318,14 @@ Si vous avez déjà importé vos données et êtes satisfait du résultat, vous Échec de la destruction de la base de donnée. Voulez vous détruire les anciennes activités de la base ? Voulez vous vraiment détruire entièrement la base de donnée ? Toutes vos données non importé seront perdu. - Les ancienne données d\'activité ont été effacés correctement. + Les anciennes données d\'activité ont été effacées correctement. Échec de la destruction de l\'ancienne base de donnée. Écraser Annuler Supprimer Vibration + + Appairage avec une Pebble + Une fenêtre d’appairage va s’afficher sur votre téléphone. Si cela ne se produit pas, regardez dans vos notifications et acceptez la demande d’appairage. Acceptez ensuite la demande d’appairage sur votre Pebble. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index fe603a9c..288bd93d 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -138,7 +138,6 @@ Dati dell\'utente non inseriti, vengono usati dati d\'esempio. Quando la Mi Band vibra e lampeggia, dalle qualche leggero colpetto. Installa - Imposta il tuo dispositivo perchè sia rilevabile. I dispositivi attualmente connessi non saranno probabilmente rilevati. Se non vedi il tuo dispositivo entro un paio di minuti, riprova dopo avere riavviato il dispositivo Android. Nota: Immagine dispositivo Nome / Soprannome @@ -299,4 +298,5 @@ Si possono importare i dati da Mi Band, Pebble Health e Morpheuz, NON quelli di Cancella Vibrazione + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 4c993ae8..2f42f669 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -108,7 +108,6 @@ 올바르지 않은 사용자 정보입니다. 일단 임시 사용자 정보를 사용합니다. Mi Band가 진동하고 깜빡일 때, 연달아 몇 번 두드리세요. 설치 - 기기를 발견 가능하도록 설정하세요. 현재 연결된 기기들은 발견될 수 없습니다. 2분이 지나도 기기가 나타나지 않는다면 재부팅 후 다시 시도해보세요. 알림: 기기 이미지 이름/별명 @@ -131,8 +130,6 @@ SMS 알림 진동 설정 일반 알림 - Pebble 알림 - Mail 알림 걸려오는 전화 알림 잃어버린 기기 찾기 진동을 멈추려면 취소를 선택하세요. @@ -226,4 +223,6 @@ 심박수 삭제 + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 885356aa..2a4484ce 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -3,17 +3,32 @@ Gadgetbridge Gadgetbridge Ustawienia - Usuń błąd + Debuguj Zakończ Synchronizuj Monitor snu (ALPHA) Odnajdź zagubione urządzenie - Zrób printscreen + Zrób screena Rozłącz + Usuń urządzenie + Usuń %1$s + To usunie urządzenie oraz zgromadzone dane Usuń błąd Zarządzanie aplikacjami + Aplikacje w pamięci + Zainstalowane aplikacje + Zainstalowane tarcze Usuń + Odinstaluj i usuń z pamięci + Zainstaluj ponownie + Szukaj w Pebble Appstore + Aktywuj + Deaktywuj + Aktywuj HRM + Deaktywuj HRM + Konfiguruj + Przejdź do góry Czarna lista powiadomień @@ -26,11 +41,16 @@ Ustawienia Ustawienia ogólne Połącz z urządzeniem gdy Bluetooth jest włączone + Łącz automatycznie Domyślny odtwarzacz muzyki Domyślny Data i godzina Synchronizuj czas Synchronizuj czas urządzenia podczas połączenia gdy czas lub strefa czasowa zmienia się na Androidzie + Motyw + Jasny + Ciemny + Język Powiadomienia Powtórzenia Połączenia @@ -39,19 +59,41 @@ Wiadomości Pebble Obsługa ogólnych powiadomień ... także gdy ekran jest włączony + Nie przeszkadzaj zawsze gdy ekran jest wyłączony nigdy Czarna lista aplikacji + Wiadomości zwrotne + Odpowiedzi + Uaktualnij na Pebble Ustawienia programisty Adres Mi Band Ustawienia Pebble + Monitory aktywności + Preferowany monitor + Synchronizuj Pebble Health + Synchronizuj Misfit + Synchronizuj Morpheuz Zezwól zewnętrznym aplikacjom Android na dostęp Włącz eksperymentalną obsługę aplikacji android przez PebbleKit + Wschód i zachód + Wyślij wschód i zachód do linii czasu Pebble bazując na lokalizacji + Lokalizacja + Otrzymaj lokalizację + Szerokość + Długość + Utrzymuj aktualną lokalizację + Włącz usługę lokalizacji + Lokalizacja otrzymana Wymuś protokół komunikacji Ta opcja wymusza użycie najnowszego protokołu powiadomień w zależności od wersji firmware. WŁĄCZ JEDYNIE JEŚLI WIESZ CO ROBISZ! Włącz nietestowane funkcje Włącz nie testowane funkcje. WŁĄCZ JEDYNIE JEŚLI WIESZ CO ROBISZ! + Preferuj BLE + Pebble 2/LE GATT limit MTU + Jeśli Twój Pebble 2/ Pebble LE nie działa jak należy spróbuj ustawić ten limit MTU (zakres 20-512) + Włącz logowanie aplikacji Próby ponownego połączenia nie połączony łącze @@ -65,6 +107,8 @@ To jest testowe powiadomienie z Gadgetbridge Bluetooth nie jest obsługiwane Bluetooth jest wyłączone + Tapnij urządzenie aby uruchomić menadżer aplikacji + Dotknij urządzenie aby połączyć Nie można połączyć. Adres BT nieprawidłowy? Gadgetbridge działa Instalowanie binarki %1$d/%2$d @@ -116,9 +160,10 @@ Powiadomienie SMS Ustawienia wibracji Ogólne powiadomienia - Powiadomienia Pebble Powiadomienia email Powiadomienia o połaczeniach przychodzących + Nawigacja + Sieć spolecznościowa Odnajdź zagubione urządzenie Anuluj by przerwać wibracje. Twoja aktywność @@ -173,6 +218,7 @@ Nie wysyłaj danych aktywności Gdy dane aktywności nie są przesłane na opaskę, wtedy nie będą usuwane. Przydatne gdy Gadgetbridge jest używany wraz z innymi aplikacjami Dane aktywności będą zachowane na Mi Band nawet po synchronizacji. Przydatne gdy Gadgetbridge jest używany z innymi aplikacjami. + To może pomóc na urządzeniach gdzie uaktualnienie kończy się błędem Historia kroków Aktualnie kroków/min Kroków łącznie @@ -188,12 +234,38 @@ Niekompatybilny firmware Ten firmware nie jest kompatybilny z urządzeniem Alarmy zarezerwowane dla nadchodzących zdarzeń + Użyj czujnika tętna alby poprawić detekcje snu + Mi2: Format daty + Czas oczekiwanie na ponowne połaczenie O tobie Data urodzenia Płeć Wzrost w cm Waga w kg + Dodaj widget + Preferowana długość snu w godzinach + Hardware: %1$s + Firmware: %1$s + Trwa aktualizacja firmware + Puls + Puls + Import bazydanych + Import starszych danych aktywności + Zarządzanie bazą danych + Zarządzanie bazą danych + Importuj / Usuń starą bazę danych + Wyeksportowano do: %1$s + Błąd eksportu bazy: %1$s + Zaimportować? + Naprawdę chcesz napisać bazę? Wszystkie Twoje dane zostaną zastąpione. + Błąd importu bazy: %1$s + Nie znaleziono aktywności, nic do importu. + Nadpisz + Anuluj Usuń + + Wibracje + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2041edd1..7284db28 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -99,7 +99,6 @@ Сопряжение устройств Для сопряжения устройств используйте диалог Android. Сопряжение вашего Mi Band - Сопряжение с %s… MAC-адрес не был передан, сопряжение не удалось. Настройки устройства Настройки Mi Band @@ -133,8 +132,6 @@ SMS-уведомление Настройки вибро Общие уведомления - Уведомления Pebble - Уведомления почты Уведомления о входящем звонке Найти потерянное устройство Отмените, чтобы остановить вибро @@ -223,4 +220,6 @@ Обновление прошивки в процессе Удалить + + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3779ce4a..1d966d84 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -105,7 +105,6 @@ Створення пари з пристроєм Для створення пари із пристроєм використовуйте діалог Android. Створення пари із вашим Mi—Band - Створення пари із %s… MAC-адресу не було передано, не вдалося створити пару. Параметри пристрою Параметри Mi—Band @@ -138,8 +137,6 @@ SMS-сповіщення Параметри вібро Загальні сповіщення - Сповіщення Pebble - Сповіщення пошти Сповіщення під час вхідного дзвінку Знайти загублений пристрій Скасуйте, аби зупинити вібро @@ -220,4 +217,6 @@ ПЗ: %1$s Вилучити + + From b2d9c357e790dbb06497f64f83bed8f0450a7888 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 29 Dec 2016 23:46:18 +0100 Subject: [PATCH 069/244] Update changelog for Subsonic #474 --- 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 36554d68..aa1ae6d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Mi Band: Improved performance during activity sync * Mi Band 2: Fix activity data missing after doing manual hr measurements or live activity * Support sharing firmwares/watchapps/watchfaces to Gadgetbridge +* Support for the "Subsonic" music player (#474) ####Version 0.15.2 * Mi Band: Fix crash with unknown notification sources diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 41cf05c7..d9e4591a 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -9,6 +9,7 @@ Mi Band: Improved performance during activity sync Mi Band 2: Fix activity data missing after doing manual hr measurements or live activity Support sharing firmwares/watchapps/watchfaces to Gadgetbridge + Support for the "Subsonic" music player (#474) Mi Band: Fix crash with unknown notification sources From 240c81ecb4ae8f09c88487a5b29a6faed0d9876d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 30 Dec 2016 15:26:44 +0100 Subject: [PATCH 070/244] Pebble: implement weather related protocol encoding this is not yet connected to anything... --- .../devices/pebble/PebbleProtocol.java | 172 +++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) 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 762e7166..39bd0cee 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 @@ -87,8 +87,10 @@ public class PebbleProtocol extends GBDeviceProtocol { static final byte BLOBDB_APP = 2; static final byte BLOBDB_REMINDER = 3; static final byte BLOBDB_NOTIFICATION = 4; + static final byte BLOBDB_WEATHER = 5; static final byte BLOBDB_CANNED_MESSAGES = 6; static final byte BLOBDB_PREFERENCES = 7; + static final byte BLOBDB_APPSETTINGS = 9; static final byte BLOBDB_APPGLANCE = 11; static final byte BLOBDB_SUCCESS = 1; @@ -378,6 +380,8 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_ZERO = new UUID(0, 0); + private static final UUID UUID_LOCATION = UUID.fromString("2c7e6a86-51e5-4ddd-b606-db43d1e4ad28"); // might be the location of "Berlin" or "Auto" + private final Map mAppMessageHandlers = new HashMap<>(); public PebbleProtocol(GBDevice device) { @@ -534,7 +538,15 @@ public class PebbleProtocol extends GBDeviceProtocol { @Override public byte[] encodeFindDevice(boolean start) { - return encodeSetCallState("Where are you?", "Gadgetbridge", start ? CallSpec.CALL_INCOMING : CallSpec.CALL_END); + // return encodeSetCallState("Where are you?", "Gadgetbridge", start ? CallSpec.CALL_INCOMING : CallSpec.CALL_END); + int ts = (int) (System.currentTimeMillis() / 1000); + + if (start) { + return encodeActivateWeather(true); + } else { + //return encodeWeatherPin(ts, "Weather", "1°/-1°", "Gadgetbridge is Sunny", "Berlin", 37); + return encodeWeatherForecast(ts, "Berlin", 0, 5, -5, 1, "Sexy", 7, 2, 1); + } } private byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies) { @@ -765,6 +777,21 @@ public class PebbleProtocol extends GBDeviceProtocol { activate ? new byte[]{0x01} : new byte[]{0x00}); } + byte[] encodeActivateWeather(boolean activate) { + if (activate) { + ByteBuffer buf = ByteBuffer.allocate(0x61); + buf.put((byte) 1); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putLong(UUID_LOCATION.getMostSignificantBits()); + buf.putLong(UUID_LOCATION.getLeastSignificantBits()); + // disable remaining 5 possible location + buf.put(new byte[60 - 16]); + return encodeBlobdb("weatherApp", BLOBDB_INSERT, BLOBDB_APPSETTINGS, buf.array()); + } else { + return encodeBlobdb("weatherApp", BLOBDB_DELETE, BLOBDB_APPSETTINGS, null); + } + } + byte[] encodeReportDataLogSessions() { return encodeSimpleMessage(ENDPOINT_DATALOG, DATALOG_REPORTSESSIONS); } @@ -1005,6 +1032,149 @@ public class PebbleProtocol extends GBDeviceProtocol { return buf.array(); } + private byte[] encodeWeatherPin(int timestamp, String title, String subtitle, String body, String location, int iconId) { + final short NOTIFICATION_PIN_LENGTH = 46; + final short ACTION_LENGTH_MIN = 10; + + String[] parts = {title, subtitle, body, location, "test", "test"}; + + // Calculate length first + byte actions_count = 1; + short actions_length; + String remove_string = "Remove"; + actions_length = (short) (ACTION_LENGTH_MIN * actions_count + remove_string.getBytes().length); + + byte attributes_count = 3; + short attributes_length = (short) (21 + actions_length); + if (parts != null) { + for (String s : parts) { + if (s == null || s.equals("")) { + continue; + } + attributes_count++; + attributes_length += (3 + s.getBytes().length); + } + } + + UUID uuid = UUID.fromString("61b22bc8-1e29-460d-a236-3fe409a43901"); + + short pin_length = (short) (NOTIFICATION_PIN_LENGTH + attributes_length); + + ByteBuffer buf = ByteBuffer.allocate(pin_length); + + // pin (46 bytes) + buf.order(ByteOrder.BIG_ENDIAN); + buf.putLong(uuid.getMostSignificantBits()); + buf.putLong(uuid.getLeastSignificantBits()); + buf.putLong(uuid.getMostSignificantBits()); + buf.putLong(uuid.getLeastSignificantBits() | 0xff); + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.putInt(timestamp); // 32-bit timestamp + buf.putShort((short) 0); // duration + buf.put((byte) 0x02); // type (0x02 = pin) + buf.putShort((short) 0x0001); // flags 0x0001 = ? + buf.put((byte) 0x06); // layout (0x06 = weather) + buf.putShort(attributes_length); // total length of all attributes and actions in bytes + buf.put(attributes_count); + buf.put(actions_count); + + byte attribute_id = 0; + // Encode Pascal-Style Strings + if (parts != null) { + for (String s : parts) { + attribute_id++; + if (s == null || s.equals("")) { + continue; + } + + int partlength = s.getBytes().length; + if (partlength > 512) partlength = 512; + if (attribute_id == 4) { + buf.put((byte) 11); + } else if (attribute_id == 5) { + buf.put((byte) 25); + } else if (attribute_id == 6) { + buf.put((byte) 26); + } else { + buf.put(attribute_id); + } + buf.putShort((short) partlength); + buf.put(s.getBytes(), 0, partlength); + } + } + + buf.put((byte) 4); // icon + buf.putShort((short) 4); // length of int + buf.putInt(0x80000000 | iconId); + + buf.put((byte) 6); // icon + buf.putShort((short) 4); // length of int + buf.putInt(0x80000000 | iconId); + + buf.put((byte) 14); // last updated + buf.putShort((short) 4); // length of int + buf.putInt(timestamp); + + // remove action + buf.put((byte) 123); // action id + buf.put((byte) 0x09); // remove + buf.put((byte) 0x01); // number attributes + buf.put((byte) 0x01); // attribute id (title) + buf.putShort((short) remove_string.getBytes().length); + buf.put(remove_string.getBytes()); + + return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array()); + } + + private byte[] encodeWeatherForecast(int timestamp, String location, int tempNow, int tempHighToday, int tempLowToday, int conditionCodeToday, String conditionToday, int tempHighTomorrow, int tempLowTomorrow, int conditionCodeTomorrow) { + final short WEATHER_FORECAST_LENGTH = 20; + + String[] parts = {location, conditionToday}; + + // Calculate length first + short attributes_length = 0; + if (parts != null) { + for (String s : parts) { + if (s == null || s.equals("")) { + continue; + } + attributes_length += (2 + s.getBytes().length); + } + } + + short pin_length = (short) (WEATHER_FORECAST_LENGTH + attributes_length); + + ByteBuffer buf = ByteBuffer.allocate(pin_length); + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.put((byte) 3); // unknown, always 3? + buf.putShort((short) tempNow); + buf.put((byte) 1); + buf.putShort((short) tempHighToday); + buf.putShort((short) tempLowToday); + buf.put((byte) 4); + buf.putShort((short) tempHighTomorrow); + buf.putShort((short) tempLowTomorrow); + buf.putInt(timestamp); + buf.put((byte) 0); // automatic location + buf.putShort(attributes_length); + + // Encode Pascal-Style Strings + if (parts != null) { + for (String s : parts) { + if (s == null || s.equals("")) { + continue; + } + + int partlength = s.getBytes().length; + if (partlength > 512) partlength = 512; + buf.putShort((short) partlength); + buf.put(s.getBytes(), 0, partlength); + } + } + + return encodeBlobdb(UUID_LOCATION, BLOBDB_INSERT, BLOBDB_WEATHER, buf.array()); + } + private byte[] encodeActionResponse(UUID uuid, int iconId, String caption) { short length = (short) (29 + caption.getBytes().length); ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + length); From 0e9ce5d1866d05bb414d048c6a1915ac1101ec5e Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 30 Dec 2016 19:58:56 +0100 Subject: [PATCH 071/244] Pebble: get min/max temperature also for the forecast --- .../pebble/AppMessageHandlerTimeStylePebble.java | 4 ++-- .../weather/notification/ParcelableWeather2.java | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index d20aa615..784603a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -105,8 +105,8 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { pairs.add(new Pair<>(MESSAGE_KEY_WeatherTemperature, (Object) (weather.currentTemp - 273))); pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) (getIconForConditionCode(weather.currentConditionCode, isNight)))); pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) (getIconForConditionCode(weather.forecastConditionCode, isNight)))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weather.highTemp - 273))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weather.lowTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weather.todayHighTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weather.todayLowTemp - 273))); } return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); diff --git a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java index 8defe1e0..0fc34ab2 100644 --- a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java +++ b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java @@ -23,8 +23,10 @@ public class ParcelableWeather2 implements Parcelable { public int currentConditionCode = 3200; String[] forecastConditionType = null; public int forecastConditionCode = 3200; - public int lowTemp = 0; - public int highTemp = 0; + public int todayLowTemp = 0; + public int todayHighTemp = 0; + public int forecastLowTemp = 0; + public int forecastHighTemp = 0; private ParcelableWeather2(Parcel in) { @@ -33,6 +35,7 @@ public class ParcelableWeather2 implements Parcelable { return; } Bundle bundle = in.readBundle(); + location = bundle.getString("weather_location"); time = bundle.getLong("weather_time"); queryTime = bundle.getLong("weather_query_time"); @@ -46,13 +49,15 @@ public class ParcelableWeather2 implements Parcelable { currentConditionType = conditionBundle.getStringArray("weather_condition_types"); currentConditionCode = weatherConditionTypesToOpenWeatherMapIds(currentConditionType[0]); - lowTemp = conditionBundle.getInt("weather_low_temp"); - highTemp = conditionBundle.getInt("weather_high_temp"); + todayLowTemp = conditionBundle.getInt("weather_low_temp"); + todayHighTemp = conditionBundle.getInt("weather_high_temp"); //fetch immediate next forecast if (--conditions > 0) { Bundle forecastBundle = in.readBundle(); forecastConditionType = forecastBundle.getStringArray("weather_condition_types"); forecastConditionCode = weatherConditionTypesToOpenWeatherMapIds(forecastConditionType[0]); + forecastLowTemp = forecastBundle.getInt("weather_low_temp"); + forecastHighTemp = forecastBundle.getInt("weather_high_temp"); } } // get the rest From e477d22c88df4bf651f99e60989b974fc5d46259 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 30 Dec 2016 20:14:13 +0100 Subject: [PATCH 072/244] Pebble: support the system weather app. - enable/disable weather app from the watchapp list - convert weather data to a format that can be displayed by the system app TODO: send the weather data periodically --- .../AbstractAppManagerFragment.java | 7 ++ .../AppManagerFragmentInstalledApps.java | 3 + .../gadgetbridge/model/Weather.java | 111 ++++++++++++++++++ .../devices/pebble/PebbleIoThread.java | 4 + .../devices/pebble/PebbleProtocol.java | 31 ++++- app/src/main/res/menu/appmanager_context.xml | 6 + app/src/main/res/values/strings.xml | 2 + 7 files changed, 161 insertions(+), 3 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 3c0de5ae..50038a9f 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 @@ -199,6 +199,9 @@ public abstract class AbstractAppManagerFragment extends Fragment { if (baseName.equals("3af858c3-16cb-4561-91e7-f1ad2df8725f")) { cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), "Kickstart (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); } + if (baseName.equals(PebbleProtocol.UUID_WEATHER.toString())) { + cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_WEATHER, "Weather (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } } } if (uuids == null) { @@ -367,8 +370,12 @@ public abstract class AbstractAppManagerFragment extends Fragment { case R.id.appmanager_hrm_activate: GBApplication.deviceService().onInstallApp(Uri.parse("fake://hrm")); return true; + case R.id.appmanager_weather_activate: + GBApplication.deviceService().onInstallApp(Uri.parse("fake://weather")); + return true; case R.id.appmanager_health_deactivate: case R.id.appmanager_hrm_deactivate: + case R.id.appmanager_weather_deactivate: GBApplication.deviceService().onAppDelete(selectedApp.getUUID()); return true; case R.id.appmanager_app_configure: 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 6cf8bfbb..fe53c32c 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 @@ -28,6 +28,9 @@ public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment if (PebbleUtils.hasHRM(mGBDevice.getModel())) { systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); } + if (PebbleUtils.getFwMajor(mGBDevice.getFirmwareVersion()) >= 4) { + systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WEATHER, "Weather (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } } return systemApps; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java index 59981199..17c8ebc5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java @@ -10,6 +10,117 @@ public class Weather { private static final Weather weather = new Weather(); public static Weather getInstance() {return weather;} + public static byte mapToPebbleCondition(int openWeatherMapCondition) { +/* deducted values: + 0 = sun + cloud + 1 = clouds + 2 = some snow + 3 = some rain + 4 = heavy rain + 5 = heavy snow + 6 = sun + cloud + rain (default icon?) + 7 = sun + 8 = rain + snow + 9 = 6 + 10, 11, ... = empty icon + */ + switch (openWeatherMapCondition) { +//Group 2xx: Thunderstorm + case 200: //thunderstorm with light rain: //11d + case 201: //thunderstorm with rain: //11d + case 202: //thunderstorm with heavy rain: //11d + case 210: //light thunderstorm:: //11d + case 211: //thunderstorm: //11d + case 230: //thunderstorm with light drizzle: //11d + case 231: //thunderstorm with drizzle: //11d + case 232: //thunderstorm with heavy drizzle: //11d + case 212: //heavy thunderstorm: //11d + case 221: //ragged thunderstorm: //11d + return 4; +//Group 3xx: Drizzle + case 300: //light intensity drizzle: //09d + case 301: //drizzle: //09d + case 302: //heavy intensity drizzle: //09d + case 310: //light intensity drizzle rain: //09d + case 311: //drizzle rain: //09d + case 312: //heavy intensity drizzle rain: //09d + case 313: //shower rain and drizzle: //09d + case 314: //heavy shower rain and drizzle: //09d + case 321: //shower drizzle: //09d + case 500: //light rain: //10d + case 501: //moderate rain: //10d + return 3; +//Group 5xx: Rain + case 502: //heavy intensity rain: //10d + case 503: //very heavy rain: //10d + case 504: //extreme rain: //10d + case 511: //freezing rain: //13d + case 520: //light intensity shower rain: //09d + case 521: //shower rain: //09d + case 522: //heavy intensity shower rain: //09d + case 531: //ragged shower rain: //09d + return 4; +//Group 6xx: Snow + case 600: //light snow: //[[file:13d.png]] + case 601: //snow: //[[file:13d.png]] + case 620: //light shower snow: //[[file:13d.png]] + return 2; + case 602: //heavy snow: //[[file:13d.png]] + case 611: //sleet: //[[file:13d.png]] + case 612: //shower sleet: //[[file:13d.png]] + case 621: //shower snow: //[[file:13d.png]] + case 622: //heavy shower snow: //[[file:13d.png]] + return 5; + case 615: //light rain and snow: //[[file:13d.png]] + case 616: //rain and snow: //[[file:13d.png]] + return 8; +//Group 7xx: Atmosphere + case 701: //mist: //[[file:50d.png]] + case 711: //smoke: //[[file:50d.png]] + case 721: //haze: //[[file:50d.png]] + case 731: //sandcase dust whirls: //[[file:50d.png]] + case 741: //fog: //[[file:50d.png]] + case 751: //sand: //[[file:50d.png]] + case 761: //dust: //[[file:50d.png]] + case 762: //volcanic ash: //[[file:50d.png]] + case 771: //squalls: //[[file:50d.png]] + case 781: //tornado: //[[file:50d.png]] + case 900: //tornado + return 6; +//Group 800: Clear + case 800: //clear sky: //[[file:01d.png]] [[file:01n.png]] + return 7; +//Group 80x: Clouds + case 801: //few clouds: //[[file:02d.png]] [[file:02n.png]] + case 802: //scattered clouds: //[[file:03d.png]] [[file:03d.png]] + case 803: //broken clouds: //[[file:04d.png]] [[file:03d.png]] + case 804: //overcast clouds: //[[file:04d.png]] [[file:04d.png]] + return 0; +//Group 90x: Extreme + case 901: //tropical storm + case 903: //cold + case 904: //hot + case 905: //windy + case 906: //hail +//Group 9xx: Additional + case 951: //calm + case 952: //light breeze + case 953: //gentle breeze + case 954: //moderate breeze + case 955: //fresh breeze + case 956: //strong breeze + case 957: //high windcase near gale + case 958: //gale + case 959: //severe gale + case 960: //storm + case 961: //violent storm + case 902: //hurricane + case 962: //hurricane + default: + return 6; + + } + } public static int mapToYahooCondition(int openWeatherMapCondition) { // openweathermap.org conditions: // http://openweathermap.org/weather-conditions 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 035fa17b..923ee95c 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 @@ -613,6 +613,10 @@ class PebbleIoThread extends GBDeviceIoThread { write(mPebbleProtocol.encodeActivateHRM(true)); return; } + if (uri.equals(Uri.parse("fake://weather"))) { + write(mPebbleProtocol.encodeActivateWeather(true)); + return; + } if (mIsInstalling) { return; 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 39bd0cee..ea954c15 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 @@ -38,7 +38,9 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; +import ru.gelin.android.weather.notification.ParcelableWeather2; public class PebbleProtocol extends GBDeviceProtocol { @@ -370,6 +372,7 @@ public class PebbleProtocol extends GBDeviceProtocol { 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 + public static final UUID UUID_WEATHER = UUID.fromString("61b22bc8-1e29-460d-a236-3fe409a439ff"); // 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"); @@ -545,7 +548,8 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeActivateWeather(true); } else { //return encodeWeatherPin(ts, "Weather", "1°/-1°", "Gadgetbridge is Sunny", "Berlin", 37); - return encodeWeatherForecast(ts, "Berlin", 0, 5, -5, 1, "Sexy", 7, 2, 1); + //return encodeWeatherForecast(ts, "Berlin", 0, 5, -5, 1, "Sexy", 7, 2, 1); + return encodeWeatherForecast(ts); } } @@ -1126,6 +1130,24 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array()); } + private byte[] encodeWeatherForecast(int timestamp) { + ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + if (weather != null) { + return encodeWeatherForecast(timestamp, + weather.location, + weather.currentTemp - 273, + weather.todayHighTemp - 273, + weather.todayLowTemp - 273, + Weather.mapToPebbleCondition(weather.currentConditionCode), + weather.currentCondition, + weather.forecastHighTemp - 273, + weather.forecastLowTemp - 273, + Weather.mapToPebbleCondition(weather.forecastConditionCode) + ); + } + return null; + } + private byte[] encodeWeatherForecast(int timestamp, String location, int tempNow, int tempHighToday, int tempLowToday, int conditionCodeToday, String conditionToday, int tempHighTomorrow, int tempLowTomorrow, int conditionCodeTomorrow) { final short WEATHER_FORECAST_LENGTH = 20; @@ -1148,10 +1170,10 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.order(ByteOrder.LITTLE_ENDIAN); buf.put((byte) 3); // unknown, always 3? buf.putShort((short) tempNow); - buf.put((byte) 1); + buf.put((byte) conditionCodeToday); buf.putShort((short) tempHighToday); buf.putShort((short) tempLowToday); - buf.put((byte) 4); + buf.put((byte) conditionCodeTomorrow); buf.putShort((short) tempHighTomorrow); buf.putShort((short) tempLowTomorrow); buf.putInt(timestamp); @@ -1392,6 +1414,9 @@ public class PebbleProtocol extends GBDeviceProtocol { if (UUID_WORKOUT.equals(uuid)) { return encodeActivateHRM(false); } + if (UUID_WEATHER.equals(uuid)) { //TODO: probably it wasn't present in firmware 3 + return encodeActivateWeather(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 43645438..766f6c5a 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -21,6 +21,12 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9d2e3e2..6430085c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,6 +30,8 @@ Deactivate Activate HRM Deactivate HRM + Activate system weather app + Deactivate system weather app Configure Move to top From 3280607cc9a587b8c9e9f07e11db7cd27b1a9fe6 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 31 Dec 2016 10:15:08 +0100 Subject: [PATCH 073/244] Pebble: hide the weather app menu entries on apps that aren't the weather app. --- .../activities/appmanager/AbstractAppManagerFragment.java | 4 ++++ 1 file changed, 4 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 50038a9f..3e68b2df 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 @@ -295,6 +295,10 @@ public abstract class AbstractAppManagerFragment extends Fragment { menu.removeItem(R.id.appmanager_hrm_activate); menu.removeItem(R.id.appmanager_hrm_deactivate); } + if (!PebbleProtocol.UUID_WEATHER.equals(selectedApp.getUUID())) { + menu.removeItem(R.id.appmanager_weather_activate); + menu.removeItem(R.id.appmanager_weather_deactivate); + } if (selectedApp.getType() == GBDeviceApp.Type.APP_SYSTEM || selectedApp.getType() == GBDeviceApp.Type.WATCHFACE_SYSTEM) { menu.removeItem(R.id.appmanager_app_delete); } From 4631e5bbafde8628533a552492084593e7cfef06 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 12:15:44 +0100 Subject: [PATCH 074/244] Pebble: restore find lost device feature abused for testing --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 ea954c15..b6447b78 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 @@ -541,16 +541,16 @@ public class PebbleProtocol extends GBDeviceProtocol { @Override public byte[] encodeFindDevice(boolean start) { - // return encodeSetCallState("Where are you?", "Gadgetbridge", start ? CallSpec.CALL_INCOMING : CallSpec.CALL_END); + return encodeSetCallState("Where are you?", "Gadgetbridge", start ? CallSpec.CALL_INCOMING : CallSpec.CALL_END); + /* int ts = (int) (System.currentTimeMillis() / 1000); if (start) { - return encodeActivateWeather(true); - } else { //return encodeWeatherPin(ts, "Weather", "1°/-1°", "Gadgetbridge is Sunny", "Berlin", 37); //return encodeWeatherForecast(ts, "Berlin", 0, 5, -5, 1, "Sexy", 7, 2, 1); return encodeWeatherForecast(ts); } + */ } private byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies) { From 95e6d2c74021ea3f643c15d7b818ef6c8908e2c5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 12:35:40 +0100 Subject: [PATCH 075/244] Pebble: delete WeatherNeat handler, it was only used for testing and the watchface seems outdated --- .../pebble/AppMessageHandlerWeatherNeat.java | 61 ------------------- .../devices/pebble/PebbleProtocol.java | 14 ++--- 2 files changed, 6 insertions(+), 69 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java deleted file mode 100644 index ac09bc7a..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerWeatherNeat.java +++ /dev/null @@ -1,61 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; - -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.util.Pair; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.UUID; - -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; - -public class AppMessageHandlerWeatherNeat extends AppMessageHandler { - - public static final int KEY_REQUEST = 0; - public static final int KEY_CITY = 1; - public static final int KEY_TEMPERATURE = 2; - public static final int KEY_CONDITION = 3; - public static final int KEY_LIGHT_TIME = 5; - - private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerWeatherNeat.class); - - public AppMessageHandlerWeatherNeat(UUID uuid, PebbleProtocol pebbleProtocol) { - super(uuid, pebbleProtocol); - } - - private byte[] encodeWeatherNeatMessage(String city, String temperature, String condition, int light_time) { - ArrayList> pairs = new ArrayList<>(4); - pairs.add(new Pair<>(KEY_CITY, (Object) city)); - pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) temperature)); - pairs.add(new Pair<>(KEY_CONDITION, (Object) condition)); - pairs.add(new Pair<>(KEY_LIGHT_TIME, (Object) light_time)); // seconds for backlight on shake - - byte[] ackMessage = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - byte[] testMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); - - ByteBuffer buf = ByteBuffer.allocate(ackMessage.length + testMessage.length); - - // encode ack and put in front of push message (hack for acknowledging the last message) - buf.put(ackMessage); - buf.put(testMessage); - - return buf.array(); - } - - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - String currentTemp = (sharedPrefs.getInt("weather_current_temp", 0) - 273) + "°C"; - String location = sharedPrefs.getString("weather_location", "unknown"); - String condition = sharedPrefs.getString("weather_current_condition", "unknown"); - GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeWeatherNeatMessage(location, currentTemp, condition, 3); - return new GBDeviceEvent[]{sendBytes}; - } -} 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 b6447b78..d656cb9c 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 @@ -68,7 +68,7 @@ public class PebbleProtocol extends GBDeviceProtocol { static final short ENDPOINT_FCTREG = 5001; static final short ENDPOINT_APPMANAGER = 6000; static final short ENDPOINT_APPFETCH = 6001; // 3.x only - public static final short ENDPOINT_DATALOG = 6778; + static final short ENDPOINT_DATALOG = 6778; static final short ENDPOINT_RUNKEEPER = 7000; static final short ENDPOINT_SCREENSHOT = 8000; static final short ENDPOINT_AUDIOSTREAM = 10000; @@ -272,18 +272,18 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final Random mRandom = new Random(); int mFwMajor = 3; - boolean mForceProtocol = false; - GBDeviceEventScreenshot mDevEventScreenshot = null; - int mScreenshotRemaining = -1; + private boolean mForceProtocol = false; + private GBDeviceEventScreenshot mDevEventScreenshot = null; + private int mScreenshotRemaining = -1; //monochrome black + white - static final byte[] clut_pebble = { + private static final byte[] clut_pebble = { 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 }; // linear BGR222 (6 bit, 64 entries) - static final byte[] clut_pebbletime = new byte[]{ + private static final byte[] clut_pebbletime = new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, (byte) 0xaa, 0x00, 0x00, 0x00, @@ -375,7 +375,6 @@ public class PebbleProtocol extends GBDeviceProtocol { public static final UUID UUID_WEATHER = UUID.fromString("61b22bc8-1e29-460d-a236-3fe409a439ff"); // 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"); private static final UUID UUID_MISFIT = UUID.fromString("0b73b76a-cd65-4dc2-9585-aaa213320858"); private static final UUID UUID_PEBBLE_TIMESTYLE = UUID.fromString("4368ffa4-f0fb-4823-90be-f754b076bdaa"); private static final UUID UUID_PEBSTYLE = UUID.fromString("da05e84d-e2a2-4020-a2dc-9cdcf265fcdd"); @@ -390,7 +389,6 @@ public class PebbleProtocol extends GBDeviceProtocol { public PebbleProtocol(GBDevice device) { super(device); mAppMessageHandlers.put(UUID_MORPHEUZ, new AppMessageHandlerMorpheuz(UUID_MORPHEUZ, PebbleProtocol.this)); - mAppMessageHandlers.put(UUID_WHETHERNEAT, new AppMessageHandlerWeatherNeat(UUID_WHETHERNEAT, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_MISFIT, new AppMessageHandlerMisfit(UUID_MISFIT, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_PEBBLE_TIMESTYLE, new AppMessageHandlerTimeStylePebble(UUID_PEBBLE_TIMESTYLE, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_PEBSTYLE, new AppMessageHandlerPebStyle(UUID_PEBSTYLE, PebbleProtocol.this)); From f12e7868378876f4a0e1474f7bce31ab0f10a926 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 13:33:50 +0100 Subject: [PATCH 076/244] Pebble: fix mario time appmessage handler (weather condition icon hardcoded to sunny for now) --- .../pebble/AppMessageHandlerMarioTime.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java index edfeac41..869053a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -14,15 +14,15 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import ru.gelin.android.weather.notification.ParcelableWeather2; -public class AppMessageHandlerMarioTime extends AppMessageHandler { +class AppMessageHandlerMarioTime extends AppMessageHandler { - public static final int KEY_WEATHER_ICON_ID = 10; - public static final int KEY_WEATHER_TEMPERATURE = 11; - public static final int KEY_WEATHER_REQUEST = 12; + private static final int KEY_WEATHER_ICON_ID = 10; + private static final int KEY_WEATHER_TEMPERATURE = 11; + private static final int KEY_WEATHER_REQUEST = 12; private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerMarioTime.class); - public AppMessageHandlerMarioTime(UUID uuid, PebbleProtocol pebbleProtocol) { + AppMessageHandlerMarioTime(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); } @@ -34,7 +34,6 @@ public class AppMessageHandlerMarioTime extends AppMessageHandler { ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); - // encode ack and put in front of push message (hack for acknowledging the last message) buf.put(weatherMessage); return buf.array(); @@ -56,14 +55,23 @@ public class AppMessageHandlerMarioTime extends AppMessageHandler { if (!weatherRequested) { return new GBDeviceEvent[]{null}; } + return pushMessage(); + } + + @Override + public GBDeviceEvent[] pushMessage() { ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + if (weather == null) { + return new GBDeviceEvent[]{null}; + } GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeWeatherMessage(weather.currentConditionCode, weather.currentTemp - 273); + sendBytes.encodedBytes = encodeWeatherMessage(weather.currentTemp - 273, 1); // FIXME: condition code GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); return new GBDeviceEvent[]{sendBytesAck, sendBytes}; + } } From 266c6b8817d32483ac892535a9155153a81ce97d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 15:56:05 +0100 Subject: [PATCH 077/244] Pebble: send weather to Pebble when we get notified by weather notification --- .../gadgetbridge/devices/EventHandler.java | 3 ++ .../WeatherNotificationReceiver.java | 23 ++++++++----- .../gadgetbridge/impl/GBDeviceService.java | 21 ++++++++++-- .../gadgetbridge/model/DeviceService.java | 13 ++++++++ .../gadgetbridge/model/WeatherSpec.java | 14 ++++++++ .../service/DeviceCommunicationService.java | 27 +++++++++++++++ .../service/ServiceDeviceSupport.java | 9 +++++ .../service/devices/hplus/HPlusSupport.java | 6 ++++ .../service/devices/miband/MiBandSupport.java | 6 ++++ .../devices/miband2/MiBand2Support.java | 6 ++++ .../devices/pebble/PebbleProtocol.java | 33 ++++++++++--------- .../service/devices/pebble/PebbleSupport.java | 8 +++++ .../vibratissimo/VibratissimoSupport.java | 6 ++++ .../serial/AbstractSerialDeviceSupport.java | 15 ++++++--- .../service/serial/GBDeviceProtocol.java | 4 +++ 15 files changed, 163 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.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 2e652788..00374072 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -12,6 +12,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; /** * Specifies all events that Gadgetbridge intends to send to the gadget device. @@ -75,4 +76,6 @@ public interface EventHandler { void onSendConfiguration(String config); void onTestNewFunction(); + + void onSendWeather(WeatherSpec weatherSpec); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index 7a8e2aa7..a02ac6c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -3,13 +3,13 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import ru.gelin.android.weather.notification.ParcelableWeather2; @@ -34,12 +34,19 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { Weather.getInstance().setWeather2(weather); LOG.info("weather in " + weather.location + " is " + weather.currentCondition + " (" + (weather.currentTemp - 273) + "°C)"); - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor edit = sharedPrefs.edit(); - edit.putString("weather_location", weather.location); - edit.putString("weather_current_condition", weather.currentCondition); - edit.putInt("weather_current_temp", weather.currentTemp); - edit.apply(); + WeatherSpec weatherSpec = new WeatherSpec(); + weatherSpec.timestamp = (int) (weather.queryTime / 1000); + weatherSpec.location = weather.location; + weatherSpec.currentTemp = weather.currentTemp; + weatherSpec.currentCondition = weather.currentCondition; + weatherSpec.currentConditionCode = weather.currentConditionCode; + weatherSpec.todayMaxTemp = weather.todayHighTemp; + weatherSpec.todayMinTemp = weather.todayLowTemp; + weatherSpec.tomorrowConditionCode = weather.forecastConditionCode; + weatherSpec.tomorrowMaxTemp = weather.forecastHighTemp; + weatherSpec.tomorrowMinTemp = weather.forecastLowTemp; + + GBApplication.deviceService().onSendWeather(weatherSpec); } } } \ No newline at end of file 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 8fd47d31..361dbe26 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -17,13 +17,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; -//import java.util.UUID; - public class GBDeviceService implements DeviceService { protected final Context mContext; - protected final Class mServiceClass; + private final Class mServiceClass; public GBDeviceService(Context context) { mContext = context; @@ -293,4 +292,20 @@ public class GBDeviceService implements DeviceService { Intent intent = createIntent().setAction(ACTION_TEST_NEW_FUNCTION); invokeService(intent); } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + Intent intent = createIntent().setAction(ACTION_SEND_WEATHER) + .putExtra(EXTRA_WEATHER_TIMESTAMP, weatherSpec.timestamp) + .putExtra(EXTRA_WEATHER_LOCATION, weatherSpec.location) + .putExtra(EXTRA_WEATHER_CURRENTTEMP, weatherSpec.currentTemp) + .putExtra(EXTRA_WEATHER_CURRENTCONDITIONCODE, weatherSpec.currentConditionCode) + .putExtra(EXTRA_WEATHER_CURRENTCONDITION, weatherSpec.currentCondition) + .putExtra(EXTRA_WEATHER_TODAYMAXTEMP, weatherSpec.todayMaxTemp) + .putExtra(EXTRA_WEATHER_TODAYMINTEMP, weatherSpec.todayMinTemp) + .putExtra(EXTRA_WEATHER_TOMORROWMAXTEMP, weatherSpec.tomorrowMaxTemp) + .putExtra(EXTRA_WEATHER_TOMORROWMINTEMP, weatherSpec.tomorrowMinTemp) + .putExtra(EXTRA_WEATHER_TOMORROWCONDITIONCODE, weatherSpec.tomorrowConditionCode); + invokeService(intent); + } } 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 c36808e5..b536fefb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -44,6 +44,7 @@ public interface DeviceService extends EventHandler { 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_SEND_WEATHER = PREFIX + ".action.send_weather"; 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,18 @@ public interface DeviceService extends EventHandler { String EXTRA_ALARMS = "alarms"; String EXTRA_PERFORM_PAIR = "perform_pair"; String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps"; + + String EXTRA_WEATHER_TIMESTAMP = "weather_timestamp"; + String EXTRA_WEATHER_LOCATION = "weather_location"; + String EXTRA_WEATHER_CURRENTTEMP = "weather_currenttemp"; + String EXTRA_WEATHER_CURRENTCONDITIONCODE = "weather_currentconditioncode"; + String EXTRA_WEATHER_CURRENTCONDITION = "currentcondition"; + String EXTRA_WEATHER_TODAYMAXTEMP = "weather_todaymaxtemp"; + String EXTRA_WEATHER_TODAYMINTEMP = "weather_todaymintemp"; + String EXTRA_WEATHER_TOMORROWMAXTEMP = "weather_tomorrowmaxtemp"; + String EXTRA_WEATHER_TOMORROWMINTEMP = "weather_tomorrowmintemp"; + String EXTRA_WEATHER_TOMORROWCONDITIONCODE = "weather_tomorrowconditioncode"; + /** * Use EXTRA_REALTIME_SAMPLE instead */ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.java new file mode 100644 index 00000000..cd30af40 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.java @@ -0,0 +1,14 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +public class WeatherSpec { + public int timestamp; + public String location; + public int currentTemp; + public int currentConditionCode; + public String currentCondition; + public int todayMaxTemp; + public int todayMinTemp; + public int tomorrowMaxTemp; + public int tomorrowMinTemp; + public int tomorrowConditionCode; +} 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 9e90bfea..f4417fb7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -47,6 +47,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; @@ -73,6 +74,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_RE 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_SEND_WEATHER; 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; @@ -123,6 +125,16 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_PERFORM_PAIR; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_VIBRATION_INTENSITY; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_CURRENTCONDITION; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_CURRENTCONDITIONCODE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_CURRENTTEMP; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_LOCATION; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_TIMESTAMP; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_TODAYMAXTEMP; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_TODAYMINTEMP; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_TOMORROWCONDITIONCODE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_TOMORROWMAXTEMP; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_TOMORROWMINTEMP; public class DeviceCommunicationService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener { private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class); @@ -501,6 +513,21 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mDeviceSupport.onTestNewFunction(); break; } + case ACTION_SEND_WEATHER: { + WeatherSpec weatherSpec = new WeatherSpec(); + weatherSpec.timestamp = intent.getIntExtra(EXTRA_WEATHER_TIMESTAMP, 0); + weatherSpec.location = intent.getStringExtra(EXTRA_WEATHER_LOCATION); + weatherSpec.currentTemp = intent.getIntExtra(EXTRA_WEATHER_CURRENTTEMP, 0); + weatherSpec.currentConditionCode = intent.getIntExtra(EXTRA_WEATHER_CURRENTCONDITIONCODE, 0); + weatherSpec.currentCondition = intent.getStringExtra(EXTRA_WEATHER_CURRENTCONDITION); + weatherSpec.todayMaxTemp = intent.getIntExtra(EXTRA_WEATHER_TODAYMAXTEMP, 0); + weatherSpec.todayMinTemp = intent.getIntExtra(EXTRA_WEATHER_TODAYMINTEMP, 0); + weatherSpec.tomorrowMaxTemp = intent.getIntExtra(EXTRA_WEATHER_TOMORROWMAXTEMP, 0); + weatherSpec.tomorrowMinTemp = intent.getIntExtra(EXTRA_WEATHER_TOMORROWMINTEMP, 0); + weatherSpec.tomorrowConditionCode = intent.getIntExtra(EXTRA_WEATHER_TOMORROWCONDITIONCODE, 0); + mDeviceSupport.onSendWeather(weatherSpec); + break; + } } return START_STICKY; 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 a90ee63c..437bf5c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -19,6 +19,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; /** * Wraps another device support instance and supports busy-checking and throttling of events. @@ -335,4 +336,12 @@ public class ServiceDeviceSupport implements DeviceSupport { } delegate.onTestNewFunction(); } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + if (checkBusy("send weather event")) { + return; + } + delegate.onSendWeather(weatherSpec); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index c3704ffe..be083b07 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; @@ -637,6 +638,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { LOG.debug("Test New Function"); } + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } + private void showIncomingCall(String name, String number){ LOG.debug("Show Incoming Call"); 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 23d7bb23..1971c073 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 @@ -50,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; @@ -1206,6 +1207,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } + private void handleSensorData(byte[] value) { int counter=0, step=0, axis1=0, axis2=0, axis3 =0; if ((value.length - 2) % 6 != 0) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index a89ff44c..b89e9e08 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -57,6 +57,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; @@ -1246,6 +1247,11 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void onTestNewFunction() { } + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } + private MiBand2Support setDateDisplay(TransactionBuilder builder) { DateTimeDisplay dateTimeDisplay = MiBand2Coordinator.getDateDisplay(getContext()); LOG.info("Setting date display to " + dateTimeDisplay); 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 d656cb9c..9e23ace4 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 @@ -39,8 +39,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; -import ru.gelin.android.weather.notification.ParcelableWeather2; public class PebbleProtocol extends GBDeviceProtocol { @@ -1128,22 +1128,23 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array()); } - private byte[] encodeWeatherForecast(int timestamp) { - ParcelableWeather2 weather = Weather.getInstance().getWeather2(); - if (weather != null) { - return encodeWeatherForecast(timestamp, - weather.location, - weather.currentTemp - 273, - weather.todayHighTemp - 273, - weather.todayLowTemp - 273, - Weather.mapToPebbleCondition(weather.currentConditionCode), - weather.currentCondition, - weather.forecastHighTemp - 273, - weather.forecastLowTemp - 273, - Weather.mapToPebbleCondition(weather.forecastConditionCode) - ); + + @Override + public byte[] encodeSendWeather(WeatherSpec weatherSpec) { + if (mFwMajor < 4) { + return null; } - return null; + return encodeWeatherForecast(weatherSpec.timestamp, + weatherSpec.location, + weatherSpec.currentTemp - 273, + weatherSpec.todayMaxTemp - 273, + weatherSpec.todayMinTemp - 273, + Weather.mapToPebbleCondition(weatherSpec.currentConditionCode), + weatherSpec.currentCondition, + weatherSpec.tomorrowMaxTemp - 273, + weatherSpec.tomorrowMinTemp - 273, + Weather.mapToPebbleCondition(weatherSpec.tomorrowConditionCode) + ); } private byte[] encodeWeatherForecast(int timestamp, String location, int tempNow, int tempHighToday, int tempLowToday, int conditionCodeToday, String conditionToday, int tempHighTomorrow, int tempLowTomorrow, int conditionCodeTomorrow) { 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 2b37852a..f88f5f77 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 @@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; @@ -170,4 +171,11 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { super.onTestNewFunction(); } } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + if (reconnect()) { + super.onSendWeather(weatherSpec); + } + } } 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 c4539a87..b19b4329 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 @@ -25,6 +25,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -280,4 +281,9 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport { public void onTestNewFunction() { } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } } 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 9c8add33..216f2c51 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 @@ -11,6 +11,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; /** @@ -27,7 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; * to create the device specific message for the respective events and sends them to the device via {@link #sendToDevice(byte[])}. */ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport { - protected GBDeviceProtocol gbDeviceProtocol; + private GBDeviceProtocol gbDeviceProtocol; protected GBDeviceIoThread gbDeviceIOThread; /** @@ -59,7 +60,7 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport /** * Lazily creates and returns the GBDeviceProtocol instance to be used. */ - public synchronized GBDeviceProtocol getDeviceProtocol() { + protected synchronized GBDeviceProtocol getDeviceProtocol() { if (gbDeviceProtocol == null) { gbDeviceProtocol = createDeviceProtocol(); } @@ -82,13 +83,13 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport * * @param bytes the message to send to the device */ - protected void sendToDevice(byte[] bytes) { + private void sendToDevice(byte[] bytes) { if (bytes != null && gbDeviceIOThread != null) { gbDeviceIOThread.write(bytes); } } - public void handleGBDeviceEvent(GBDeviceEventSendBytes sendBytes) { + private void handleGBDeviceEvent(GBDeviceEventSendBytes sendBytes) { sendToDevice(sendBytes.encodedBytes); } @@ -226,4 +227,10 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport byte[] bytes = gbDeviceProtocol.encodeTestNewFunction(); sendToDevice(bytes); } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + byte[] bytes = gbDeviceProtocol.encodeSendWeather(weatherSpec); + sendToDevice(bytes); + } } 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 c4fe1e73..77a5bc2e 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 @@ -7,6 +7,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; public abstract class GBDeviceProtocol { @@ -108,4 +109,7 @@ public abstract class GBDeviceProtocol { return mDevice; } + public byte[] encodeSendWeather(WeatherSpec weatherSpec) { + return null; + } } From 19c5cbfbb9053338561d80cbc75520b66063a0d0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 18:13:04 +0100 Subject: [PATCH 078/244] fix tests --- .../gadgetbridge/service/TestDeviceSupport.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index b4328421..6d7565de 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; public class TestDeviceSupport extends AbstractDeviceSupport { @@ -177,4 +178,9 @@ public class TestDeviceSupport extends AbstractDeviceSupport { public void onTestNewFunction() { } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } } From 82c0f35c58fcbbcc0cabe38d52f8a8da86c1626e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 18:56:24 +0100 Subject: [PATCH 079/244] Pebble: add encodeUpadteWeather() to AppMessageHandler for easier watchface support Now in Timestyle weather is in sync with what we get from weather notification --- .../devices/pebble/AppMessageHandler.java | 9 +++- .../AppMessageHandlerTimeStylePebble.java | 43 +++++++++---------- .../devices/pebble/PebbleProtocol.java | 22 +++++++++- .../DeviceCommunicationServiceTestCase.java | 4 +- .../service/TestDeviceService.java | 4 +- .../service/TestDeviceSupport.java | 4 +- 6 files changed, 54 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java index 1944f2d5..693bf9b1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java @@ -8,10 +8,11 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; public class AppMessageHandler { - protected final PebbleProtocol mPebbleProtocol; - protected final UUID mUUID; + final PebbleProtocol mPebbleProtocol; + final UUID mUUID; AppMessageHandler(UUID uuid, PebbleProtocol pebbleProtocol) { mUUID = uuid; @@ -34,6 +35,10 @@ public class AppMessageHandler { return null; } + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return null; + } + protected GBDevice getDevice() { return mPebbleProtocol.getDevice(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 784603a4..e5d49402 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -10,8 +10,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; -import nodomain.freeyourgadget.gadgetbridge.model.Weather; -import ru.gelin.android.weather.notification.ParcelableWeather2; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { private static final int MESSAGE_KEY_WeatherCondition = 10000; @@ -95,35 +94,35 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { return iconToLoad; } - private byte[] encodeTimeStylePebbleWeather() { - ArrayList> pairs = new ArrayList<>(); - ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + private byte[] encodeTimeStylePebbleWeather(WeatherSpec weatherSpec) { - boolean isNight = false; //TODO: use the night icons when night - if (weather != null) { - pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) (isNight ? 1 : 0))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherTemperature, (Object) (weather.currentTemp - 273))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) (getIconForConditionCode(weather.currentConditionCode, isNight)))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) (getIconForConditionCode(weather.forecastConditionCode, isNight)))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weather.todayHighTemp - 273))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weather.todayLowTemp - 273))); + if (weatherSpec == null) { + return null; } - return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + ArrayList> pairs = new ArrayList<>(); + boolean isNight = false; //TODO: use the night icons when night + pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) (isNight ? 1 : 0))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherTemperature, (Object) (weatherSpec.currentTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode, isNight)))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) (getIconForConditionCode(weatherSpec.tomorrowConditionCode, isNight)))); + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weatherSpec.todayMaxTemp - 273))); + + pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weatherSpec.todayMinTemp - 273))); + + return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); } @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - return pushMessage(); + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + return new GBDeviceEvent[]{sendBytesAck}; + // TODO: trigger update of weather? } @Override - public GBDeviceEvent[] pushMessage() { - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - - GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeTimeStylePebbleWeather(); - return new GBDeviceEvent[]{sendBytesAck, sendBytes}; + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeTimeStylePebbleWeather(weatherSpec); } } \ No newline at end of file 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 9e23ace4..6c4efc21 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 @@ -386,6 +386,8 @@ public class PebbleProtocol extends GBDeviceProtocol { private final Map mAppMessageHandlers = new HashMap<>(); + private UUID currentRunningApp = UUID_ZERO; + public PebbleProtocol(GBDevice device) { super(device); mAppMessageHandlers.put(UUID_MORPHEUZ, new AppMessageHandlerMorpheuz(UUID_MORPHEUZ, PebbleProtocol.this)); @@ -1134,7 +1136,8 @@ public class PebbleProtocol extends GBDeviceProtocol { if (mFwMajor < 4) { return null; } - return encodeWeatherForecast(weatherSpec.timestamp, + byte[] watchfaceProtocol = null; + byte[] forecastProtocol = encodeWeatherForecast(weatherSpec.timestamp, weatherSpec.location, weatherSpec.currentTemp - 273, weatherSpec.todayMaxTemp - 273, @@ -1145,6 +1148,19 @@ public class PebbleProtocol extends GBDeviceProtocol { weatherSpec.tomorrowMinTemp - 273, Weather.mapToPebbleCondition(weatherSpec.tomorrowConditionCode) ); + AppMessageHandler handler = mAppMessageHandlers.get(currentRunningApp); + if (handler != null) { + watchfaceProtocol = handler.encodeUpdateWeather(weatherSpec); + } + + if (watchfaceProtocol != null) { + ByteBuffer buf = ByteBuffer.allocate(forecastProtocol.length + watchfaceProtocol.length); + buf.put(forecastProtocol); + buf.put(watchfaceProtocol); + return buf.array(); + } + + return forecastProtocol; } private byte[] encodeWeatherForecast(int timestamp, String location, int tempNow, int tempHighToday, int tempLowToday, int conditionCodeToday, String conditionToday, int tempHighTomorrow, int tempLowTomorrow, int conditionCodeTomorrow) { @@ -2107,7 +2123,7 @@ public class PebbleProtocol extends GBDeviceProtocol { switch (command) { case APPRUNSTATE_START: LOG.info(ENDPOINT_NAME + ": started " + uuid); - + currentRunningApp = uuid; AppMessageHandler handler = mAppMessageHandlers.get(uuid); if (handler != null) { return handler.pushMessage(); @@ -2442,6 +2458,7 @@ public class PebbleProtocol extends GBDeviceProtocol { devEvts = handler.handleMessage(dict); } else { + currentRunningApp = uuid; devEvts = handler.pushMessage(); } } else { @@ -2453,6 +2470,7 @@ public class PebbleProtocol extends GBDeviceProtocol { devEvts = decodeDictToJSONAppMessage(uuid, buf); } else { + currentRunningApp = uuid; GBDeviceEventAppManagement gbDeviceEventAppManagement = new GBDeviceEventAppManagement(); gbDeviceEventAppManagement.uuid = uuid; gbDeviceEventAppManagement.type = GBDeviceEventAppManagement.EventType.START; diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java index 68af48ad..58dccd72 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java @@ -22,7 +22,7 @@ public class DeviceCommunicationServiceTestCase extends TestBase { * Factory that always returns the mockSupport instance */ private class TestDeviceSupportFactory extends DeviceSupportFactory { - public TestDeviceSupportFactory(Context context) { + TestDeviceSupportFactory(Context context) { super(context); } @@ -53,7 +53,7 @@ public class DeviceCommunicationServiceTestCase extends TestBase { mDeviceService = new TestDeviceService(getContext()); } - protected GBDevice getDevice() { + private GBDevice getDevice() { return realSupport.getDevice(); } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java index ba798a58..8eff6f79 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java @@ -13,11 +13,11 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService; * Extends GBDeviceServer so that communication with the service works * with Robolectric. */ -public class TestDeviceService extends GBDeviceService { +class TestDeviceService extends GBDeviceService { private final ServiceController serviceController; private final DeviceCommunicationService service; - public TestDeviceService(Context context) throws Exception { + TestDeviceService(Context context) throws Exception { super(context); serviceController = Robolectric.buildService(DeviceCommunicationService.class, createIntent()); diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 6d7565de..160b855b 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -17,9 +17,9 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; -public class TestDeviceSupport extends AbstractDeviceSupport { +class TestDeviceSupport extends AbstractDeviceSupport { - public TestDeviceSupport() { + TestDeviceSupport() { } @Override From 4e543d4b3490e26ab0813ebcc0cd7296d7ed88da Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 19:04:05 +0100 Subject: [PATCH 080/244] Pebble: rename pushMessage() to onAppStart() --- .../service/devices/pebble/AppMessageHandler.java | 2 +- .../service/devices/pebble/AppMessageHandlerMarioTime.java | 4 ++-- .../service/devices/pebble/AppMessageHandlerPebStyle.java | 2 +- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java index 693bf9b1..c0ddc21e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java @@ -31,7 +31,7 @@ public class AppMessageHandler { return null; } - public GBDeviceEvent[] pushMessage() { + public GBDeviceEvent[] onAppStart() { return null; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java index 869053a1..8ef9d762 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -55,11 +55,11 @@ class AppMessageHandlerMarioTime extends AppMessageHandler { if (!weatherRequested) { return new GBDeviceEvent[]{null}; } - return pushMessage(); + return onAppStart(); } @Override - public GBDeviceEvent[] pushMessage() { + public GBDeviceEvent[] onAppStart() { ParcelableWeather2 weather = Weather.getInstance().getWeather2(); if (weather == null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java index 20e6165f..d6402a1b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java @@ -110,7 +110,7 @@ public class AppMessageHandlerPebStyle extends AppMessageHandler { } @Override - public GBDeviceEvent[] pushMessage() { + public GBDeviceEvent[] onAppStart() { return null; /* GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); 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 6c4efc21..342bd1dd 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 @@ -2126,7 +2126,7 @@ public class PebbleProtocol extends GBDeviceProtocol { currentRunningApp = uuid; AppMessageHandler handler = mAppMessageHandlers.get(uuid); if (handler != null) { - return handler.pushMessage(); + return handler.onAppStart(); } else { GBDeviceEventAppManagement gbDeviceEventAppManagement = new GBDeviceEventAppManagement(); @@ -2459,7 +2459,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } else { currentRunningApp = uuid; - devEvts = handler.pushMessage(); + devEvts = handler.onAppStart(); } } else { devEvts = new GBDeviceEvent[]{null}; From 984e639e971ec1963061353b06393caed4b7e27e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 19:17:08 +0100 Subject: [PATCH 081/244] Pebble: push current weather to TimeStylePebble again when watchface gets started --- .../WeatherNotificationReceiver.java | 2 +- .../gadgetbridge/model/Weather.java | 19 +++++++++++++++++-- .../AppMessageHandlerTimeStylePebble.java | 11 ++++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index a02ac6c7..c6b39afd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -45,7 +45,7 @@ public class WeatherNotificationReceiver extends BroadcastReceiver { weatherSpec.tomorrowConditionCode = weather.forecastConditionCode; weatherSpec.tomorrowMaxTemp = weather.forecastHighTemp; weatherSpec.tomorrowMinTemp = weather.forecastLowTemp; - + Weather.getInstance().setWeatherSpec(weatherSpec); GBApplication.deviceService().onSendWeather(weatherSpec); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java index 17c8ebc5..979a6dc6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java @@ -4,8 +4,23 @@ import ru.gelin.android.weather.notification.ParcelableWeather2; public class Weather { private ParcelableWeather2 weather2 = null; - public ParcelableWeather2 getWeather2() {return weather2;} - public void setWeather2(ParcelableWeather2 weather2) {this.weather2 = weather2;} + private WeatherSpec weatherSpec = null; + + public ParcelableWeather2 getWeather2() { + return weather2; + } + + public void setWeather2(ParcelableWeather2 weather2) { + this.weather2 = weather2; + } + + public WeatherSpec getWeatherSpec() { + return weatherSpec; + } + + public void setWeatherSpec(WeatherSpec weatherSpec) { + this.weatherSpec = weatherSpec; + } private static final Weather weather = new Weather(); public static Weather getInstance() {return weather;} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index e5d49402..6651ed61 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -10,6 +10,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { @@ -115,10 +116,18 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + // Just ACK GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); return new GBDeviceEvent[]{sendBytesAck}; - // TODO: trigger update of weather? + } + + @Override + public GBDeviceEvent[] onAppStart() { + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeTimeStylePebbleWeather(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; } @Override From b81124770458116c95a75d3973afaf17a255eabf Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 19:27:21 +0100 Subject: [PATCH 082/244] Pebble: Adapt MarioTime logic --- .../pebble/AppMessageHandlerMarioTime.java | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java index 8ef9d762..be4e9a97 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -12,13 +12,12 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; -import ru.gelin.android.weather.notification.ParcelableWeather2; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; class AppMessageHandlerMarioTime extends AppMessageHandler { private static final int KEY_WEATHER_ICON_ID = 10; private static final int KEY_WEATHER_TEMPERATURE = 11; - private static final int KEY_WEATHER_REQUEST = 12; private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerMarioTime.class); @@ -26,10 +25,10 @@ class AppMessageHandlerMarioTime extends AppMessageHandler { super(uuid, pebbleProtocol); } - private byte[] encodeWeatherMessage(int temperature, int condition) { + private byte[] encodeMarioWeatherMessage(WeatherSpec weatherSpec) { ArrayList> pairs = new ArrayList<>(2); - pairs.add(new Pair<>(KEY_WEATHER_ICON_ID, (Object) (byte) condition)); - pairs.add(new Pair<>(KEY_WEATHER_TEMPERATURE, (Object) (byte) temperature)); + pairs.add(new Pair<>(KEY_WEATHER_ICON_ID, (Object) (byte) 1)); + pairs.add(new Pair<>(KEY_WEATHER_TEMPERATURE, (Object) (byte) (weatherSpec.currentTemp - 273))); byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); @@ -41,37 +40,22 @@ class AppMessageHandlerMarioTime extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - boolean weatherRequested = false; - for (Pair pair : pairs) { - switch (pair.first) { - case KEY_WEATHER_REQUEST: - LOG.info("got weather request"); - weatherRequested = true; - break; - default: - LOG.info("unknown key " + pair.first); - } - } - if (!weatherRequested) { - return new GBDeviceEvent[]{null}; - } - return onAppStart(); + // Just ACK + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + return new GBDeviceEvent[]{sendBytesAck}; } @Override public GBDeviceEvent[] onAppStart() { - ParcelableWeather2 weather = Weather.getInstance().getWeather2(); - - if (weather == null) { - return new GBDeviceEvent[]{null}; - } + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeWeatherMessage(weather.currentTemp - 273, 1); // FIXME: condition code - - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - - return new GBDeviceEvent[]{sendBytesAck, sendBytes}; + sendBytes.encodedBytes = encodeMarioWeatherMessage(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; + } + @Override + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeMarioWeatherMessage(weatherSpec); } } From 5a83cb1c48e56bf4017271e046c38c468b4a1cff Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 19:29:41 +0100 Subject: [PATCH 083/244] Pebble: fix npe in mario time handler I shout stop for 2016! :D --- .../service/devices/pebble/AppMessageHandlerMarioTime.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java index be4e9a97..46992628 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -26,6 +26,10 @@ class AppMessageHandlerMarioTime extends AppMessageHandler { } private byte[] encodeMarioWeatherMessage(WeatherSpec weatherSpec) { + if (weatherSpec == null) { + return null; + } + ArrayList> pairs = new ArrayList<>(2); pairs.add(new Pair<>(KEY_WEATHER_ICON_ID, (Object) (byte) 1)); pairs.add(new Pair<>(KEY_WEATHER_TEMPERATURE, (Object) (byte) (weatherSpec.currentTemp - 273))); From 7930b7da752025eeb81c0a534d332968da528f0b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 31 Dec 2016 20:08:53 +0100 Subject: [PATCH 084/244] Pebble: Support Healthify Weather --- .../pebble/AppMessageHandlerHealthify.java | 65 +++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 2 + 2 files changed, 67 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java new file mode 100644 index 00000000..41cefd78 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java @@ -0,0 +1,65 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.util.Pair; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Objects; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; + +class AppMessageHandlerHealthify extends AppMessageHandler { + private static final int KEY_TEMPERATURE = 10021; + private static final int KEY_CONDITIONS = 10022; + + private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerHealthify.class); + + AppMessageHandlerHealthify(UUID uuid, PebbleProtocol pebbleProtocol) { + super(uuid, pebbleProtocol); + } + + private byte[] encodeMarioWeatherMessage(WeatherSpec weatherSpec) { + if (weatherSpec == null) { + return null; + } + + ArrayList> pairs = new ArrayList<>(2); + pairs.add(new Pair<>(KEY_CONDITIONS, (Object) weatherSpec.currentCondition)); + pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) (weatherSpec.currentTemp - 273))); + byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + + ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); + + buf.put(weatherMessage); + + return buf.array(); + } + + @Override + public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + // Just ACK + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + return new GBDeviceEvent[]{sendBytesAck}; + } + + @Override + public GBDeviceEvent[] onAppStart() { + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeMarioWeatherMessage(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; + } + + @Override + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeMarioWeatherMessage(weatherSpec); + } +} 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 342bd1dd..d82bf5e5 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 @@ -379,6 +379,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_PEBBLE_TIMESTYLE = UUID.fromString("4368ffa4-f0fb-4823-90be-f754b076bdaa"); private static final UUID UUID_PEBSTYLE = UUID.fromString("da05e84d-e2a2-4020-a2dc-9cdcf265fcdd"); private static final UUID UUID_MARIOTIME = UUID.fromString("43caa750-2896-4f46-94dc-1adbd4bc1ff3"); + private static final UUID UUID_HELTHIFY = UUID.fromString("7ee97b2c-95e8-4720-b94e-70fccd905d98"); private static final UUID UUID_ZERO = new UUID(0, 0); @@ -395,6 +396,7 @@ public class PebbleProtocol extends GBDeviceProtocol { mAppMessageHandlers.put(UUID_PEBBLE_TIMESTYLE, new AppMessageHandlerTimeStylePebble(UUID_PEBBLE_TIMESTYLE, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_PEBSTYLE, new AppMessageHandlerPebStyle(UUID_PEBSTYLE, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_MARIOTIME, new AppMessageHandlerMarioTime(UUID_MARIOTIME, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_HELTHIFY, new AppMessageHandlerHealthify(UUID_HELTHIFY, PebbleProtocol.this)); } private final HashMap mDatalogSessions = new HashMap<>(); From 1722a6dc47aeb66fe7f1bb10c5cde931279eabb2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 1 Jan 2017 13:55:07 +0100 Subject: [PATCH 085/244] Pebble: minor code cleanup --- .../devices/pebble/PebbleProtocol.java | 336 +++++++++--------- 1 file changed, 165 insertions(+), 171 deletions(-) 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 d82bf5e5..0dcc3694 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 @@ -46,140 +46,137 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final Logger LOG = LoggerFactory.getLogger(PebbleProtocol.class); - static final short ENDPOINT_TIME = 11; - static final short ENDPOINT_FIRMWAREVERSION = 16; - public static final short ENDPOINT_PHONEVERSION = 17; - static final short ENDPOINT_SYSTEMMESSAGE = 18; - static final short ENDPOINT_MUSICCONTROL = 32; - static final short ENDPOINT_PHONECONTROL = 33; + private static final short ENDPOINT_TIME = 11; + private static final short ENDPOINT_FIRMWAREVERSION = 16; + private static final short ENDPOINT_PHONEVERSION = 17; + private static final short ENDPOINT_SYSTEMMESSAGE = 18; + private static final short ENDPOINT_MUSICCONTROL = 32; + private static final short ENDPOINT_PHONECONTROL = 33; static final short ENDPOINT_APPLICATIONMESSAGE = 48; - static final short ENDPOINT_LAUNCHER = 49; - static final short ENDPOINT_APPRUNSTATE = 52; // 3.x only - static final short ENDPOINT_LOGS = 2000; - static final short ENDPOINT_PING = 2001; - static final short ENDPOINT_LOGDUMP = 2002; - static final short ENDPOINT_RESET = 2003; - static final short ENDPOINT_APP = 2004; - static final short ENDPOINT_APPLOGS = 2006; - static final short ENDPOINT_NOTIFICATION = 3000; - static final short ENDPOINT_EXTENSIBLENOTIFS = 3010; - static final short ENDPOINT_RESOURCE = 4000; - static final short ENDPOINT_SYSREG = 5000; - static final short ENDPOINT_FCTREG = 5001; - static final short ENDPOINT_APPMANAGER = 6000; - static final short ENDPOINT_APPFETCH = 6001; // 3.x only - static final short ENDPOINT_DATALOG = 6778; - static final short ENDPOINT_RUNKEEPER = 7000; - static final short ENDPOINT_SCREENSHOT = 8000; - static final short ENDPOINT_AUDIOSTREAM = 10000; - static final short ENDPOINT_VOICECONTROL = 11000; - static final short ENDPOINT_NOTIFICATIONACTION = 11440; // 3.x only, TODO: find a better name - static final short ENDPOINT_APPREORDER = (short) 0xabcd; // 3.x only - static final short ENDPOINT_BLOBDB = (short) 45531; // 3.x only - static final short ENDPOINT_PUTBYTES = (short) 48879; + private static final short ENDPOINT_LAUNCHER = 49; + private static final short ENDPOINT_APPRUNSTATE = 52; // FW >=3.x + private static final short ENDPOINT_LOGS = 2000; + private static final short ENDPOINT_PING = 2001; + private static final short ENDPOINT_LOGDUMP = 2002; + private static final short ENDPOINT_RESET = 2003; + private static final short ENDPOINT_APP = 2004; + private static final short ENDPOINT_APPLOGS = 2006; + private static final short ENDPOINT_NOTIFICATION = 3000; // FW 1.x-2-x + private static final short ENDPOINT_EXTENSIBLENOTIFS = 3010; // FW 2.x + private static final short ENDPOINT_RESOURCE = 4000; + private static final short ENDPOINT_SYSREG = 5000; + private static final short ENDPOINT_FCTREG = 5001; + private static final short ENDPOINT_APPMANAGER = 6000; + private static final short ENDPOINT_APPFETCH = 6001; // FW >=3.x + private static final short ENDPOINT_DATALOG = 6778; + private static final short ENDPOINT_RUNKEEPER = 7000; + private static final short ENDPOINT_SCREENSHOT = 8000; + private static final short ENDPOINT_AUDIOSTREAM = 10000; + private static final short ENDPOINT_VOICECONTROL = 11000; + private static final short ENDPOINT_NOTIFICATIONACTION = 11440; // FW >=3.x, TODO: find a better name + private static final short ENDPOINT_APPREORDER = (short) 0xabcd; // FW >=3.x + private static final short ENDPOINT_BLOBDB = (short) 0xb1db; // FW >=3.x + private static final short ENDPOINT_PUTBYTES = (short) 0xbeef; - static final byte APPRUNSTATE_START = 1; - static final byte APPRUNSTATE_STOP = 2; + private static final byte APPRUNSTATE_START = 1; + private static final byte APPRUNSTATE_STOP = 2; - static final byte BLOBDB_INSERT = 1; - static final byte BLOBDB_DELETE = 4; - static final byte BLOBDB_CLEAR = 5; + private static final byte BLOBDB_INSERT = 1; + private static final byte BLOBDB_DELETE = 4; + private static final byte BLOBDB_CLEAR = 5; - static final byte BLOBDB_PIN = 1; - static final byte BLOBDB_APP = 2; - static final byte BLOBDB_REMINDER = 3; - static final byte BLOBDB_NOTIFICATION = 4; - static final byte BLOBDB_WEATHER = 5; - static final byte BLOBDB_CANNED_MESSAGES = 6; - static final byte BLOBDB_PREFERENCES = 7; - static final byte BLOBDB_APPSETTINGS = 9; - static final byte BLOBDB_APPGLANCE = 11; + private static final byte BLOBDB_PIN = 1; + private static final byte BLOBDB_APP = 2; + private static final byte BLOBDB_REMINDER = 3; + private static final byte BLOBDB_NOTIFICATION = 4; + private static final byte BLOBDB_WEATHER = 5; + private static final byte BLOBDB_CANNED_MESSAGES = 6; + private static final byte BLOBDB_PREFERENCES = 7; + private static final byte BLOBDB_APPSETTINGS = 9; + private static final byte BLOBDB_APPGLANCE = 11; - static final byte BLOBDB_SUCCESS = 1; - static final byte BLOBDB_GENERALFAILURE = 2; - static final byte BLOBDB_INVALIDOPERATION = 3; - static final byte BLOBDB_INVALIDDATABASEID = 4; - static final byte BLOBDB_INVALIDDATA = 5; - static final byte BLOBDB_KEYDOESNOTEXIST = 6; - static final byte BLOBDB_DATABASEFULL = 7; - static final byte BLOBDB_DATASTALE = 8; + private static final byte BLOBDB_SUCCESS = 1; + private static final byte BLOBDB_GENERALFAILURE = 2; + private static final byte BLOBDB_INVALIDOPERATION = 3; + private static final byte BLOBDB_INVALIDDATABASEID = 4; + private static final byte BLOBDB_INVALIDDATA = 5; + private static final byte BLOBDB_KEYDOESNOTEXIST = 6; + private static final byte BLOBDB_DATABASEFULL = 7; + private static final byte BLOBDB_DATASTALE = 8; - // This is not in the Pebble protocol - static final byte NOTIFICATION_UNDEFINED = -1; + private static final byte NOTIFICATION_EMAIL = 0; + private static final byte NOTIFICATION_SMS = 1; + private static final byte NOTIFICATION_TWITTER = 2; + private static final byte NOTIFICATION_FACEBOOK = 3; - static final byte NOTIFICATION_EMAIL = 0; - static final byte NOTIFICATION_SMS = 1; - static final byte NOTIFICATION_TWITTER = 2; - static final byte NOTIFICATION_FACEBOOK = 3; + private static final byte PHONECONTROL_ANSWER = 1; + private static final byte PHONECONTROL_HANGUP = 2; + private static final byte PHONECONTROL_GETSTATE = 3; + private static final byte PHONECONTROL_INCOMINGCALL = 4; + private static final byte PHONECONTROL_OUTGOINGCALL = 5; + private static final byte PHONECONTROL_MISSEDCALL = 6; + private static final byte PHONECONTROL_RING = 7; + private static final byte PHONECONTROL_START = 8; + private static final byte PHONECONTROL_END = 9; - static final byte PHONECONTROL_ANSWER = 1; - static final byte PHONECONTROL_HANGUP = 2; - static final byte PHONECONTROL_GETSTATE = 3; - static final byte PHONECONTROL_INCOMINGCALL = 4; - static final byte PHONECONTROL_OUTGOINGCALL = 5; - static final byte PHONECONTROL_MISSEDCALL = 6; - static final byte PHONECONTROL_RING = 7; - static final byte PHONECONTROL_START = 8; - static final byte PHONECONTROL_END = 9; + private static final byte MUSICCONTROL_SETMUSICINFO = 0x10; + private static final byte MUSICCONTROL_SETPLAYSTATE = 0x11; - static final byte MUSICCONTROL_SETMUSICINFO = 0x10; - static final byte MUSICCONTROL_SETPLAYSTATE = 0x11; + private static final byte MUSICCONTROL_PLAYPAUSE = 1; + private static final byte MUSICCONTROL_PAUSE = 2; + private static final byte MUSICCONTROL_PLAY = 3; + private static final byte MUSICCONTROL_NEXT = 4; + private static final byte MUSICCONTROL_PREVIOUS = 5; + private static final byte MUSICCONTROL_VOLUMEUP = 6; + private static final byte MUSICCONTROL_VOLUMEDOWN = 7; + private static final byte MUSICCONTROL_GETNOWPLAYING = 8; - static final byte MUSICCONTROL_PLAYPAUSE = 1; - static final byte MUSICCONTROL_PAUSE = 2; - static final byte MUSICCONTROL_PLAY = 3; - static final byte MUSICCONTROL_NEXT = 4; - static final byte MUSICCONTROL_PREVIOUS = 5; - static final byte MUSICCONTROL_VOLUMEUP = 6; - static final byte MUSICCONTROL_VOLUMEDOWN = 7; - static final byte MUSICCONTROL_GETNOWPLAYING = 8; + private static final byte MUSICCONTROL_STATE_PAUSED = 0x00; + private static final byte MUSICCONTROL_STATE_PLAYING = 0x01; + private static final byte MUSICCONTROL_STATE_REWINDING = 0x02; + private static final byte MUSICCONTROL_STATE_FASTWORWARDING = 0x03; + private static final byte MUSICCONTROL_STATE_UNKNOWN = 0x04; - static final byte MUSICCONTROL_STATE_PAUSED = 0x00; - static final byte MUSICCONTROL_STATE_PLAYING = 0x01; - static final byte MUSICCONTROL_STATE_REWINDING = 0x02; - static final byte MUSICCONTROL_STATE_FASTWORWARDING = 0x03; - static final byte MUSICCONTROL_STATE_UNKNOWN = 0x04; + private static final byte NOTIFICATIONACTION_ACK = 0; + private static final byte NOTIFICATIONACTION_NACK = 1; + private static final byte NOTIFICATIONACTION_INVOKE = 0x02; + private static final byte NOTIFICATIONACTION_RESPONSE = 0x11; - static final byte NOTIFICATIONACTION_ACK = 0; - static final byte NOTIFICATIONACTION_NACK = 1; - static final byte NOTIFICATIONACTION_INVOKE = 0x02; - static final byte NOTIFICATIONACTION_RESPONSE = 0x11; + private static final byte TIME_GETTIME = 0; + private static final byte TIME_SETTIME = 2; + private static final byte TIME_SETTIME_UTC = 3; - static final byte TIME_GETTIME = 0; - static final byte TIME_SETTIME = 2; - static final byte TIME_SETTIME_UTC = 3; + private static final byte FIRMWAREVERSION_GETVERSION = 0; - static final byte FIRMWAREVERSION_GETVERSION = 0; + private static final byte APPMANAGER_GETAPPBANKSTATUS = 1; + private static final byte APPMANAGER_REMOVEAPP = 2; + private static final byte APPMANAGER_REFRESHAPP = 3; + private static final byte APPMANAGER_GETUUIDS = 5; - static final byte APPMANAGER_GETAPPBANKSTATUS = 1; - static final byte APPMANAGER_REMOVEAPP = 2; - static final byte APPMANAGER_REFRESHAPP = 3; - static final byte APPMANAGER_GETUUIDS = 5; + private static final int APPMANAGER_RES_SUCCESS = 1; - static final int APPMANAGER_RES_SUCCESS = 1; + private static final byte APPLICATIONMESSAGE_PUSH = 1; + private static final byte APPLICATIONMESSAGE_REQUEST = 2; + private static final byte APPLICATIONMESSAGE_ACK = (byte) 0xff; + private static final byte APPLICATIONMESSAGE_NACK = (byte) 0x7f; - static final byte APPLICATIONMESSAGE_PUSH = 1; - static final byte APPLICATIONMESSAGE_REQUEST = 2; - static final byte APPLICATIONMESSAGE_ACK = (byte) 0xff; - static final byte APPLICATIONMESSAGE_NACK = (byte) 0x7f; + private static final byte DATALOG_OPENSESSION = 0x01; + private static final byte DATALOG_SENDDATA = 0x02; + private static final byte DATALOG_CLOSE = 0x03; + private static final byte DATALOG_TIMEOUT = 0x07; + private static final byte DATALOG_REPORTSESSIONS = (byte) 0x84; + private static final byte DATALOG_ACK = (byte) 0x85; + private static final byte DATALOG_NACK = (byte) 0x86; - static final byte DATALOG_OPENSESSION = 0x01; - static final byte DATALOG_SENDDATA = 0x02; - static final byte DATALOG_CLOSE = 0x03; - static final byte DATALOG_TIMEOUT = 0x07; - static final byte DATALOG_REPORTSESSIONS = (byte) 0x84; - static final byte DATALOG_ACK = (byte) 0x85; - static final byte DATALOG_NACK = (byte) 0x86; + private static final byte PING_PING = 0; + private static final byte PING_PONG = 1; - static final byte PING_PING = 0; - static final byte PING_PONG = 1; - - static final byte PUTBYTES_INIT = 1; - static final byte PUTBYTES_SEND = 2; - static final byte PUTBYTES_COMMIT = 3; - static final byte PUTBYTES_ABORT = 4; - static final byte PUTBYTES_COMPLETE = 5; + private static final byte PUTBYTES_INIT = 1; + private static final byte PUTBYTES_SEND = 2; + private static final byte PUTBYTES_COMMIT = 3; + private static final byte PUTBYTES_ABORT = 4; + private static final byte PUTBYTES_COMPLETE = 5; public static final byte PUTBYTES_TYPE_FIRMWARE = 1; public static final byte PUTBYTES_TYPE_RECOVERY = 2; @@ -189,70 +186,54 @@ public class PebbleProtocol extends GBDeviceProtocol { public static final byte PUTBYTES_TYPE_FILE = 6; public static final byte PUTBYTES_TYPE_WORKER = 7; - static final byte RESET_REBOOT = 0; + private static final byte RESET_REBOOT = 0; - static final byte SCREENSHOT_TAKE = 0; + private static final byte SCREENSHOT_TAKE = 0; - static final byte SYSTEMMESSAGE_NEWFIRMWAREAVAILABLE = 0; - static final byte SYSTEMMESSAGE_FIRMWARESTART = 1; - static final byte SYSTEMMESSAGE_FIRMWARECOMPLETE = 2; - static final byte SYSTEMMESSAGE_FIRMWAREFAIL = 3; - static final byte SYSTEMMESSAGE_FIRMWARE_UPTODATE = 4; - static final byte SYSTEMMESSAGE_FIRMWARE_OUTOFDATE = 5; - static final byte SYSTEMMESSAGE_STOPRECONNECTING = 6; - static final byte SYSTEMMESSAGE_STARTRECONNECTING = 7; + private static final byte SYSTEMMESSAGE_NEWFIRMWAREAVAILABLE = 0; + private static final byte SYSTEMMESSAGE_FIRMWARESTART = 1; + private static final byte SYSTEMMESSAGE_FIRMWARECOMPLETE = 2; + private static final byte SYSTEMMESSAGE_FIRMWAREFAIL = 3; + private static final byte SYSTEMMESSAGE_FIRMWARE_UPTODATE = 4; + private static final byte SYSTEMMESSAGE_FIRMWARE_OUTOFDATE = 5; + private static final byte SYSTEMMESSAGE_STOPRECONNECTING = 6; + private static final byte SYSTEMMESSAGE_STARTRECONNECTING = 7; - static final byte PHONEVERSION_REQUEST = 0; - static final byte PHONEVERSION_APPVERSION_MAGIC = 2; // increase this if pebble complains - static final byte PHONEVERSION_APPVERSION_MAJOR = 2; - static final byte PHONEVERSION_APPVERSION_MINOR = 3; - static final byte PHONEVERSION_APPVERSION_PATCH = 0; + private static final byte PHONEVERSION_REQUEST = 0; + private static final byte PHONEVERSION_APPVERSION_MAGIC = 2; // increase this if pebble complains + private static final byte PHONEVERSION_APPVERSION_MAJOR = 2; + private static final byte PHONEVERSION_APPVERSION_MINOR = 3; + private static final byte PHONEVERSION_APPVERSION_PATCH = 0; - static final int PHONEVERSION_SESSION_CAPS_GAMMARAY = 0x80000000; + private static final int PHONEVERSION_SESSION_CAPS_GAMMARAY = 0x80000000; - static final int PHONEVERSION_REMOTE_CAPS_TELEPHONY = 0x00000010; - static final int PHONEVERSION_REMOTE_CAPS_SMS = 0x00000020; - static final int PHONEVERSION_REMOTE_CAPS_GPS = 0x00000040; - static final int PHONEVERSION_REMOTE_CAPS_BTLE = 0x00000080; - static final int PHONEVERSION_REMOTE_CAPS_REARCAMERA = 0x00000100; - static final int PHONEVERSION_REMOTE_CAPS_ACCEL = 0x00000200; - static final int PHONEVERSION_REMOTE_CAPS_GYRO = 0x00000400; - static final int PHONEVERSION_REMOTE_CAPS_COMPASS = 0x00000800; + private static final int PHONEVERSION_REMOTE_CAPS_TELEPHONY = 0x00000010; + private static final int PHONEVERSION_REMOTE_CAPS_SMS = 0x00000020; + private static final int PHONEVERSION_REMOTE_CAPS_GPS = 0x00000040; + private static final int PHONEVERSION_REMOTE_CAPS_BTLE = 0x00000080; + private static final int PHONEVERSION_REMOTE_CAPS_REARCAMERA = 0x00000100; + private static final int PHONEVERSION_REMOTE_CAPS_ACCEL = 0x00000200; + private static final int PHONEVERSION_REMOTE_CAPS_GYRO = 0x00000400; + private static final int PHONEVERSION_REMOTE_CAPS_COMPASS = 0x00000800; - static final byte PHONEVERSION_REMOTE_OS_UNKNOWN = 0; - static final byte PHONEVERSION_REMOTE_OS_IOS = 1; - static final byte PHONEVERSION_REMOTE_OS_ANDROID = 2; - static final byte PHONEVERSION_REMOTE_OS_OSX = 3; - static final byte PHONEVERSION_REMOTE_OS_LINUX = 4; - static final byte PHONEVERSION_REMOTE_OS_WINDOWS = 5; + private static final byte PHONEVERSION_REMOTE_OS_UNKNOWN = 0; + private static final byte PHONEVERSION_REMOTE_OS_IOS = 1; + private static final byte PHONEVERSION_REMOTE_OS_ANDROID = 2; + private static final byte PHONEVERSION_REMOTE_OS_OSX = 3; + private static final byte PHONEVERSION_REMOTE_OS_LINUX = 4; + private static final byte PHONEVERSION_REMOTE_OS_WINDOWS = 5; - static final byte TYPE_BYTEARRAY = 0; - static final byte TYPE_CSTRING = 1; - static final byte TYPE_UINT = 2; - static final byte TYPE_INT = 3; + private static final byte TYPE_BYTEARRAY = 0; + private static final byte TYPE_CSTRING = 1; + private static final byte TYPE_UINT = 2; + private static final byte TYPE_INT = 3; - static final short LENGTH_PREFIX = 4; - static final short LENGTH_SIMPLEMESSAGE = 1; + private final short LENGTH_PREFIX = 4; - static final short LENGTH_APPFETCH = 2; - static final short LENGTH_APPRUNSTATE = 17; - static final short LENGTH_PING = 5; - static final short LENGTH_PHONEVERSION = 17; - static final short LENGTH_REMOVEAPP_2X = 17; - static final short LENGTH_REFRESHAPP = 5; - static final short LENGTH_SETTIME = 5; - static final short LENGTH_SYSTEMMESSAGE = 2; - static final short LENGTH_UPLOADSTART_2X = 7; - static final short LENGTH_UPLOADSTART_3X = 10; - static final short LENGTH_UPLOADCHUNK = 9; - static final short LENGTH_UPLOADCOMMIT = 9; - static final short LENGTH_UPLOADCOMPLETE = 5; - static final short LENGTH_UPLOADCANCEL = 5; + private static final byte LENGTH_UUID = 16; - static final byte LENGTH_UUID = 16; - - static final long GB_UUID_MASK = 0x4767744272646700L; + private static final long GB_UUID_MASK = 0x4767744272646700L; // base is -8 private static final String[] hwRevisions = { @@ -402,6 +383,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private final HashMap mDatalogSessions = new HashMap<>(); private byte[] encodeSimpleMessage(short endpoint, byte command) { + final short LENGTH_SIMPLEMESSAGE = 1; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_SIMPLEMESSAGE); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_SIMPLEMESSAGE); @@ -514,6 +496,7 @@ public class PebbleProtocol extends GBDeviceProtocol { @Override public byte[] encodeSetTime() { + final short LENGTH_SETTIME = 5; long ts = System.currentTimeMillis(); long ts_offset = (SimpleTimeZone.getDefault().getOffset(ts)); ByteBuffer buf; @@ -791,7 +774,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putLong(UUID_LOCATION.getMostSignificantBits()); buf.putLong(UUID_LOCATION.getLeastSignificantBits()); // disable remaining 5 possible location - buf.put(new byte[60 - 16]); + buf.put(new byte[60 - LENGTH_UUID]); return encodeBlobdb("weatherApp", BLOBDB_INSERT, BLOBDB_APPSETTINGS, buf.array()); } else { return encodeBlobdb("weatherApp", BLOBDB_DELETE, BLOBDB_APPSETTINGS, null); @@ -1257,7 +1240,8 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_APP, buf.array()); } - public byte[] encodeAppFetchAck() { + byte[] encodeAppFetchAck() { + final short LENGTH_APPFETCH = 2; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_APPFETCH); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_APPFETCH); @@ -1406,6 +1390,7 @@ public class PebbleProtocol extends GBDeviceProtocol { @Override public byte[] encodeAppStart(UUID uuid, boolean start) { if (mFwMajor >= 3) { + final short LENGTH_APPRUNSTATE = 17; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_APPRUNSTATE); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_APPRUNSTATE); @@ -1436,6 +1421,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } return encodeBlobdb(uuid, BLOBDB_DELETE, BLOBDB_APP, null); } else { + final short LENGTH_REMOVEAPP_2X = 17; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_REMOVEAPP_2X); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_REMOVEAPP_2X); @@ -1448,6 +1434,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } private byte[] encodePhoneVersion2x(byte os) { + final short LENGTH_PHONEVERSION = 17; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_PHONEVERSION); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_PHONEVERSION); @@ -1574,10 +1561,10 @@ public class PebbleProtocol extends GBDeviceProtocol { byte[] encodeUploadStart(byte type, int app_id, int size, String filename) { short length; if (mFwMajor >= 3 && (type != PUTBYTES_TYPE_FILE)) { - length = LENGTH_UPLOADSTART_3X; + length = (short) 10; type |= 0b10000000; } else { - length = LENGTH_UPLOADSTART_2X; + length = (short) 7; } if (type == PUTBYTES_TYPE_FILE && filename != null) { @@ -1608,6 +1595,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } byte[] encodeUploadChunk(int token, byte[] buffer, int size) { + final short LENGTH_UPLOADCHUNK = 9; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_UPLOADCHUNK + size); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort((short) (LENGTH_UPLOADCHUNK + size)); @@ -1620,6 +1608,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } byte[] encodeUploadCommit(int token, int crc) { + final short LENGTH_UPLOADCOMMIT = 9; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_UPLOADCOMMIT); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_UPLOADCOMMIT); @@ -1631,6 +1620,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } byte[] encodeUploadComplete(int token) { + final short LENGTH_UPLOADCOMPLETE = 5; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_UPLOADCOMPLETE); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_UPLOADCOMPLETE); @@ -1641,6 +1631,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } byte[] encodeUploadCancel(int token) { + final short LENGTH_UPLOADCANCEL = 5; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_UPLOADCANCEL); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_UPLOADCANCEL); @@ -1651,6 +1642,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } private byte[] encodeSystemMessage(byte systemMessage) { + final short LENGTH_SYSTEMMESSAGE = 2; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_SYSTEMMESSAGE); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_SYSTEMMESSAGE); @@ -1675,6 +1667,7 @@ public class PebbleProtocol extends GBDeviceProtocol { byte[] encodeAppRefresh(int index) { + final short LENGTH_REFRESHAPP = 5; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_REFRESHAPP); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_REFRESHAPP); @@ -1711,6 +1704,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } private byte[] encodePing(byte command, int cookie) { + final short LENGTH_PING = 5; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_PING); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort(LENGTH_PING); From 8b551106794c5aef6e8cceef3792bf17906c9a1d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 1 Jan 2017 14:19:35 +0100 Subject: [PATCH 086/244] Pebble: allow weather to be send to watchfaces on fw < 4.x --- .../devices/pebble/PebbleProtocol.java | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) 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 0dcc3694..9ba9c6cc 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 @@ -532,8 +532,6 @@ public class PebbleProtocol extends GBDeviceProtocol { if (start) { //return encodeWeatherPin(ts, "Weather", "1°/-1°", "Gadgetbridge is Sunny", "Berlin", 37); - //return encodeWeatherForecast(ts, "Berlin", 0, 5, -5, 1, "Sexy", 7, 2, 1); - return encodeWeatherForecast(ts); } */ } @@ -1118,40 +1116,36 @@ public class PebbleProtocol extends GBDeviceProtocol { @Override public byte[] encodeSendWeather(WeatherSpec weatherSpec) { - if (mFwMajor < 4) { - return null; - } + byte[] forecastProtocol = null; byte[] watchfaceProtocol = null; - byte[] forecastProtocol = encodeWeatherForecast(weatherSpec.timestamp, - weatherSpec.location, - weatherSpec.currentTemp - 273, - weatherSpec.todayMaxTemp - 273, - weatherSpec.todayMinTemp - 273, - Weather.mapToPebbleCondition(weatherSpec.currentConditionCode), - weatherSpec.currentCondition, - weatherSpec.tomorrowMaxTemp - 273, - weatherSpec.tomorrowMinTemp - 273, - Weather.mapToPebbleCondition(weatherSpec.tomorrowConditionCode) - ); + int length = 0; + if (mFwMajor >= 4) { + forecastProtocol = encodeWeatherForecast(weatherSpec); + length += forecastProtocol.length; + } AppMessageHandler handler = mAppMessageHandlers.get(currentRunningApp); if (handler != null) { watchfaceProtocol = handler.encodeUpdateWeather(weatherSpec); + if (watchfaceProtocol != null) { + length += watchfaceProtocol.length; + } } + ByteBuffer buf = ByteBuffer.allocate(length); - if (watchfaceProtocol != null) { - ByteBuffer buf = ByteBuffer.allocate(forecastProtocol.length + watchfaceProtocol.length); + if (forecastProtocol != null) { buf.put(forecastProtocol); + } + if (watchfaceProtocol != null) { buf.put(watchfaceProtocol); - return buf.array(); } - return forecastProtocol; + return buf.array(); } - private byte[] encodeWeatherForecast(int timestamp, String location, int tempNow, int tempHighToday, int tempLowToday, int conditionCodeToday, String conditionToday, int tempHighTomorrow, int tempLowTomorrow, int conditionCodeTomorrow) { + private byte[] encodeWeatherForecast(WeatherSpec weatherSpec) { final short WEATHER_FORECAST_LENGTH = 20; - String[] parts = {location, conditionToday}; + String[] parts = {weatherSpec.location, weatherSpec.currentCondition}; // Calculate length first short attributes_length = 0; @@ -1169,15 +1163,15 @@ public class PebbleProtocol extends GBDeviceProtocol { ByteBuffer buf = ByteBuffer.allocate(pin_length); buf.order(ByteOrder.LITTLE_ENDIAN); buf.put((byte) 3); // unknown, always 3? - buf.putShort((short) tempNow); - buf.put((byte) conditionCodeToday); - buf.putShort((short) tempHighToday); - buf.putShort((short) tempLowToday); - buf.put((byte) conditionCodeTomorrow); - buf.putShort((short) tempHighTomorrow); - buf.putShort((short) tempLowTomorrow); - buf.putInt(timestamp); - buf.put((byte) 0); // automatic location + buf.putShort((short) (weatherSpec.currentTemp - 273)); + buf.put((byte) Weather.mapToPebbleCondition(weatherSpec.currentConditionCode)); + buf.putShort((short) (weatherSpec.todayMaxTemp - 273)); + buf.putShort((short) (weatherSpec.todayMinTemp - 273)); + buf.put((byte) Weather.mapToPebbleCondition(weatherSpec.tomorrowConditionCode)); + buf.putShort((short) (weatherSpec.tomorrowMaxTemp - 273)); + buf.putShort((short) (weatherSpec.tomorrowMinTemp - 273)); + buf.putInt(weatherSpec.timestamp); + buf.put((byte) 0); // automatic location 0=manual 1=auto buf.putShort(attributes_length); // Encode Pascal-Style Strings From f25605f5a177c641f355230606ddc16406d93afd Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 1 Jan 2017 16:24:46 +0100 Subject: [PATCH 087/244] Pebble: First shot at TrekVolle support Also some cleanups --- .../devices/pebble/AppMessageHandler.java | 2 +- .../pebble/AppMessageHandlerHealthify.java | 9 +- .../pebble/AppMessageHandlerMarioTime.java | 8 +- .../pebble/AppMessageHandlerMisfit.java | 22 ++--- .../pebble/AppMessageHandlerMorpheuz.java | 58 ++++++------- .../pebble/AppMessageHandlerPebStyle.java | 2 +- .../AppMessageHandlerTimeStylePebble.java | 12 ++- .../pebble/AppMessageHandlerTrekVolle.java | 85 +++++++++++++++++++ .../pebble/DatalogSessionHealthSteps.java | 2 +- .../devices/pebble/PebbleProtocol.java | 8 +- 10 files changed, 144 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java index c0ddc21e..042c7e50 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java @@ -10,7 +10,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; -public class AppMessageHandler { +class AppMessageHandler { final PebbleProtocol mPebbleProtocol; final UUID mUUID; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java index 41cefd78..178b499e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java @@ -2,12 +2,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Objects; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; @@ -19,8 +15,6 @@ class AppMessageHandlerHealthify extends AppMessageHandler { private static final int KEY_TEMPERATURE = 10021; private static final int KEY_CONDITIONS = 10022; - private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerHealthify.class); - AppMessageHandlerHealthify(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); } @@ -53,6 +47,9 @@ class AppMessageHandlerHealthify extends AppMessageHandler { @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); sendBytes.encodedBytes = encodeMarioWeatherMessage(weatherSpec); return new GBDeviceEvent[]{sendBytes}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java index 46992628..91b105e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -2,9 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.UUID; @@ -19,8 +16,6 @@ class AppMessageHandlerMarioTime extends AppMessageHandler { private static final int KEY_WEATHER_ICON_ID = 10; private static final int KEY_WEATHER_TEMPERATURE = 11; - private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerMarioTime.class); - AppMessageHandlerMarioTime(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); } @@ -53,6 +48,9 @@ class AppMessageHandlerMarioTime extends AppMessageHandler { @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); sendBytes.encodedBytes = encodeMarioWeatherMessage(weatherSpec); return new GBDeviceEvent[]{sendBytes}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 1bb51991..381743a3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -22,21 +22,21 @@ import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class AppMessageHandlerMisfit extends AppMessageHandler { +class AppMessageHandlerMisfit extends AppMessageHandler { - public static final int KEY_SLEEPGOAL = 1; - public static final int KEY_STEP_ROGRESS = 2; - public static final int KEY_SLEEP_PROGRESS = 3; - public static final int KEY_VERSION = 4; - public static final int KEY_SYNC = 5; - public static final int KEY_INCOMING_DATA_BEGIN = 6; - public static final int KEY_INCOMING_DATA = 7; - public static final int KEY_INCOMING_DATA_END = 8; - public static final int KEY_SYNC_RESULT = 9; + private static final int KEY_SLEEPGOAL = 1; + private static final int KEY_STEP_ROGRESS = 2; + private static final int KEY_SLEEP_PROGRESS = 3; + private static final int KEY_VERSION = 4; + private static final int KEY_SYNC = 5; + private static final int KEY_INCOMING_DATA_BEGIN = 6; + private static final int KEY_INCOMING_DATA = 7; + private static final int KEY_INCOMING_DATA_END = 8; + private static final int KEY_SYNC_RESULT = 9; private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerMisfit.class); - public AppMessageHandlerMisfit(UUID uuid, PebbleProtocol pebbleProtocol) { + AppMessageHandlerMisfit(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 4f7a14a8..4b152289 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -20,37 +20,37 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleMorpheuzSampleP import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMorpheuzSample; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class AppMessageHandlerMorpheuz extends AppMessageHandler { +class AppMessageHandlerMorpheuz extends AppMessageHandler { - public static final int KEY_POINT = 1; - public static final int KEY_POINT_46 = 10000; - public static final int KEY_CTRL = 2; - public static final int KEY_CTRL_46 = 10001; - public static final int KEY_FROM = 3; - public static final int KEY_FROM_46 = 10002; - public static final int KEY_TO = 4; - public static final int KEY_TO_46 = 10003; - public static final int KEY_BASE = 5; - public static final int KEY_BASE_46 = 10004; - public static final int KEY_VERSION = 6; - public static final int KEY_VERSION_46 = 10005; - public static final int KEY_GONEOFF = 7; - public static final int KEY_GONEOFF_46 = 10006; - public static final int KEY_TRANSMIT = 8; - public static final int KEY_TRANSMIT_46 = 10007; - public static final int KEY_AUTO_RESET = 9; - public static final int KEY_AUTO_RESET_46 = 10008; - public static final int KEY_SNOOZES = 10; - public static final int KEY_SNOOZES_46 = 10009; - public static final int KEY_FAULT_46 = 10010; + private static final int KEY_POINT = 1; + private static final int KEY_POINT_46 = 10000; + private static final int KEY_CTRL = 2; + private static final int KEY_CTRL_46 = 10001; + private static final int KEY_FROM = 3; + private static final int KEY_FROM_46 = 10002; + private static final int KEY_TO = 4; + private static final int KEY_TO_46 = 10003; + private static final int KEY_BASE = 5; + private static final int KEY_BASE_46 = 10004; + private static final int KEY_VERSION = 6; + private static final int KEY_VERSION_46 = 10005; + private static final int KEY_GONEOFF = 7; + private static final int KEY_GONEOFF_46 = 10006; + private static final int KEY_TRANSMIT = 8; + private static final int KEY_TRANSMIT_46 = 10007; + private static final int KEY_AUTO_RESET = 9; + private static final int KEY_AUTO_RESET_46 = 10008; + private static final int KEY_SNOOZES = 10; + private static final int KEY_SNOOZES_46 = 10009; + private static final int KEY_FAULT_46 = 10010; - public static final int CTRL_TRANSMIT_DONE = 1; - public static final int CTRL_VERSION_DONE = 2; - public static final int CTRL_GONEOFF_DONE = 4; - public static final int CTRL_DO_NEXT = 8; - public static final int CTRL_SET_LAST_SENT = 16; - public static final int CTRL_LAZARUS = 32; - public static final int CTRL_SNOOZES_DONE = 64; + private static final int CTRL_TRANSMIT_DONE = 1; + private static final int CTRL_VERSION_DONE = 2; + private static final int CTRL_GONEOFF_DONE = 4; + private static final int CTRL_DO_NEXT = 8; + private static final int CTRL_SET_LAST_SENT = 16; + private static final int CTRL_LAZARUS = 32; + private static final int CTRL_SNOOZES_DONE = 64; // data received from Morpheuz in native format private int version = 0; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java index d6402a1b..8e916c10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java @@ -14,7 +14,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import ru.gelin.android.weather.notification.ParcelableWeather2; -public class AppMessageHandlerPebStyle extends AppMessageHandler { +class AppMessageHandlerPebStyle extends AppMessageHandler { public static final int KEY_AMPM_TEXT = 21; public static final int KEY_BLUETOOTH_ALERT = 2; public static final int KEY_BLUETOOTH_ICON = 20; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 6651ed61..16c96afc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -2,9 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.ArrayList; import java.util.UUID; @@ -13,7 +10,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; -public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { +class AppMessageHandlerTimeStylePebble extends AppMessageHandler { private static final int MESSAGE_KEY_WeatherCondition = 10000; private static final int MESSAGE_KEY_WeatherForecastCondition = 10002; private static final int MESSAGE_KEY_WeatherForecastHighTemp = 10003; @@ -35,9 +32,7 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { private static final int ICON_THUNDERSTORM = 10; private static final int ICON_WEATHER_GENERIC = 11; - private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerTimeStylePebble.class); - - public AppMessageHandlerTimeStylePebble(UUID uuid, PebbleProtocol pebbleProtocol) { + AppMessageHandlerTimeStylePebble(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); } @@ -125,6 +120,9 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); sendBytes.encodedBytes = encodeTimeStylePebbleWeather(weatherSpec); return new GBDeviceEvent[]{sendBytes}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java new file mode 100644 index 00000000..b3cfce66 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java @@ -0,0 +1,85 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.util.Pair; + +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; + +class AppMessageHandlerTrekVolle extends AppMessageHandler { + private static final int MESSAGE_KEY_WEATHER_TEMPERATURE = 10000; + private static final int MESSAGE_KEY_WEATHER_CONDITIONS = 10001; + private static final int MESSAGE_KEY_WEATHER_ICON = 10002; + private static final int MESSAGE_KEY_WEATHER_TEMPERATURE_MIN = 10024; + private static final int MESSAGE_KEY_WEATHER_TEMPERATURE_MAX = 10025; + private static final int MESSAGE_KEY_WEATHER_LOCATION = 10030; + + AppMessageHandlerTrekVolle(UUID uuid, PebbleProtocol pebbleProtocol) { + super(uuid, pebbleProtocol); + } + + private int getIconForConditionCode(int conditionCode, boolean isNight) { + /* + case 1: return RESOURCE_ID_IMAGE_WEATHER_CLEARNIGHT; + case 2: return RESOURCE_ID_IMAGE_WEATHER_CLEAR; + case 3: return RESOURCE_ID_IMAGE_WEATHER_CLOUDYNIGHT; + case 4: return RESOURCE_ID_IMAGE_WEATHER_CLOUDY; + case 5: return RESOURCE_ID_IMAGE_WEATHER_CLOUDS; + case 6: return RESOURCE_ID_IMAGE_WEATHER_THICKCLOUDS; + case 7: return RESOURCE_ID_IMAGE_WEATHER_RAIN; + case 8: return RESOURCE_ID_IMAGE_WEATHER_RAINYNIGHT; + case 9: return RESOURCE_ID_IMAGE_WEATHER_RAINY; + case 10: return RESOURCE_ID_IMAGE_WEATHER_LIGHTNING; + case 11: return RESOURCE_ID_IMAGE_WEATHER_SNOW; + case 12: return RESOURCE_ID_IMAGE_WEATHER_MIST; + */ + return 2; + } + + private byte[] encodeTrekVolleWeather(WeatherSpec weatherSpec) { + + if (weatherSpec == null) { + return null; + } + + boolean isNight = false; // FIXME + ArrayList> pairs = new ArrayList<>(); + pairs.add(new Pair<>(MESSAGE_KEY_WEATHER_TEMPERATURE, (Object) (weatherSpec.currentTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WEATHER_CONDITIONS, (Object) (weatherSpec.currentCondition))); + pairs.add(new Pair<>(MESSAGE_KEY_WEATHER_ICON, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode, isNight)))); + pairs.add(new Pair<>(MESSAGE_KEY_WEATHER_TEMPERATURE_MAX, (Object) (weatherSpec.todayMaxTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WEATHER_TEMPERATURE_MIN, (Object) (weatherSpec.todayMinTemp - 273))); + pairs.add(new Pair<>(MESSAGE_KEY_WEATHER_LOCATION, (Object) weatherSpec.location)); + + + return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + } + + @Override + public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + // Just ACK + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + return new GBDeviceEvent[]{sendBytesAck}; + } + + @Override + public GBDeviceEvent[] onAppStart() { + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeTrekVolleWeather(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; + } + + @Override + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeTrekVolleWeather(weatherSpec); + } +} \ No newline at end of file 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 4bcc6ae5..1f69dcd1 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 @@ -16,7 +16,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { +class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSteps.class); 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 9ba9c6cc..625cc734 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 @@ -361,6 +361,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_PEBSTYLE = UUID.fromString("da05e84d-e2a2-4020-a2dc-9cdcf265fcdd"); private static final UUID UUID_MARIOTIME = UUID.fromString("43caa750-2896-4f46-94dc-1adbd4bc1ff3"); private static final UUID UUID_HELTHIFY = UUID.fromString("7ee97b2c-95e8-4720-b94e-70fccd905d98"); + private static final UUID UUID_TREKVOLLE = UUID.fromString("2da02267-7a19-4e49-9ed1-439d25db14e4"); private static final UUID UUID_ZERO = new UUID(0, 0); @@ -375,9 +376,10 @@ public class PebbleProtocol extends GBDeviceProtocol { mAppMessageHandlers.put(UUID_MORPHEUZ, new AppMessageHandlerMorpheuz(UUID_MORPHEUZ, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_MISFIT, new AppMessageHandlerMisfit(UUID_MISFIT, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_PEBBLE_TIMESTYLE, new AppMessageHandlerTimeStylePebble(UUID_PEBBLE_TIMESTYLE, PebbleProtocol.this)); - mAppMessageHandlers.put(UUID_PEBSTYLE, new AppMessageHandlerPebStyle(UUID_PEBSTYLE, PebbleProtocol.this)); + //mAppMessageHandlers.put(UUID_PEBSTYLE, new AppMessageHandlerPebStyle(UUID_PEBSTYLE, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_MARIOTIME, new AppMessageHandlerMarioTime(UUID_MARIOTIME, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_HELTHIFY, new AppMessageHandlerHealthify(UUID_HELTHIFY, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_TREKVOLLE, new AppMessageHandlerTrekVolle(UUID_TREKVOLLE, PebbleProtocol.this)); } private final HashMap mDatalogSessions = new HashMap<>(); @@ -1164,10 +1166,10 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.order(ByteOrder.LITTLE_ENDIAN); buf.put((byte) 3); // unknown, always 3? buf.putShort((short) (weatherSpec.currentTemp - 273)); - buf.put((byte) Weather.mapToPebbleCondition(weatherSpec.currentConditionCode)); + buf.put(Weather.mapToPebbleCondition(weatherSpec.currentConditionCode)); buf.putShort((short) (weatherSpec.todayMaxTemp - 273)); buf.putShort((short) (weatherSpec.todayMinTemp - 273)); - buf.put((byte) Weather.mapToPebbleCondition(weatherSpec.tomorrowConditionCode)); + buf.put(Weather.mapToPebbleCondition(weatherSpec.tomorrowConditionCode)); buf.putShort((short) (weatherSpec.tomorrowMaxTemp - 273)); buf.putShort((short) (weatherSpec.tomorrowMinTemp - 273)); buf.putInt(weatherSpec.timestamp); From 507e58922fa9138945b2ef0c2d1fc028798b04ac Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 1 Jan 2017 17:58:34 +0100 Subject: [PATCH 088/244] Pebble: fix decoding of byte and short appmessage data (when a native handler is present) --- .../service/devices/pebble/PebbleProtocol.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 625cc734..407b79fe 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 @@ -1733,7 +1733,13 @@ public class PebbleProtocol extends GBDeviceProtocol { switch (type) { case TYPE_INT: case TYPE_UINT: - dict.add(new Pair(key, buf.getInt())); + if (length == 1) { + dict.add(new Pair(key, buf.get())); + } else if (length == 2) { + dict.add(new Pair(key, buf.getShort())); + } else { + dict.add(new Pair(key, buf.getInt())); + } break; case TYPE_CSTRING: case TYPE_BYTEARRAY: From 1fb4ee8a8f86eaca68c2fce76bfa02468a2a3f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Mon, 2 Jan 2017 00:58:37 +0000 Subject: [PATCH 089/244] HPlus: Basic support for data synchronization --- .../gadgetbridge/daogen/GBDaoGenerator.java | 23 +- .../devices/hplus/HPlusConstants.java | 126 +++-- .../devices/hplus/HPlusCoordinator.java | 24 +- .../hplus/HPlusHealthSampleProvider.java | 152 ++++++ .../devices/hplus/HPlusSampleProvider.java | 82 ---- .../devices/hplus/HPlusDataRecord.java | 38 ++ .../devices/hplus/HPlusDataRecordDay.java | 40 ++ .../hplus/HPlusDataRecordRealtime.java | 69 +++ .../devices/hplus/HPlusDataRecordSleep.java | 79 +++ .../devices/hplus/HPlusDataRecordSteps.java | 59 +++ .../devices/hplus/HPlusHandlerThread.java | 453 ++++++++++++++++++ .../devices/hplus/HPlusSleepRecord.java | 86 ---- .../service/devices/hplus/HPlusSupport.java | 368 ++++---------- 13 files changed, 1093 insertions(+), 506 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index e6cb8ddf..2905d45f 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -60,6 +60,7 @@ public class GBDaoGenerator { addPebbleHealthActivityKindOverlay(schema, user, device); addPebbleMisfitActivitySample(schema, user, device); addPebbleMorpheuzActivitySample(schema, user, device); + addHPlusHealthActivityKindOverlay(schema, user, device); addHPlusHealthActivitySample(schema, user, device); new DaoGenerator().generateAll(schema, "app/src/main/java"); @@ -226,15 +227,31 @@ public class GBDaoGenerator { Entity activitySample = addEntity(schema, "HPlusHealthActivitySample"); addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); activitySample.addByteArrayProperty("rawHPlusHealthData"); - activitySample.addIntProperty("rawHPlusCalories").notNull(); - activitySample.addIntProperty("rawHPlusDistance").notNull(); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().primaryKey(); activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); - activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); addHeartRateProperties(activitySample); + activitySample.addIntProperty("distance"); + activitySample.addIntProperty("calories"); + return activitySample; } + private static Entity addHPlusHealthActivityKindOverlay(Schema schema, Entity user, Entity device) { + Entity activityOverlay = addEntity(schema, "HPlusHealthActivityOverlay"); + + activityOverlay.addIntProperty(TIMESTAMP_FROM).notNull().primaryKey(); + activityOverlay.addIntProperty(TIMESTAMP_TO).notNull().primaryKey(); + activityOverlay.addIntProperty(SAMPLE_RAW_KIND).notNull().primaryKey(); + Property deviceId = activityOverlay.addLongProperty("deviceId").primaryKey().notNull().getProperty(); + activityOverlay.addToOne(device, deviceId); + + Property userId = activityOverlay.addLongProperty("userId").notNull().getProperty(); + activityOverlay.addToOne(user, userId); + activityOverlay.addByteArrayProperty("rawHPlusHealthData"); + return activityOverlay; + } + private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) { activitySample.setSuperclass(superClass); activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index d0f450fa..9f82d13d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -1,12 +1,11 @@ package nodomain.freeyourgadget.gadgetbridge.devices.hplus; +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + import java.util.UUID; -/** - * Message constants reverse-engineered by João Paulo Barraca, jpbarraca@gmail.com. - * - * @author João Paulo Barraca <jpbarraca@gmail.com> - */ public final class HPlusConstants { public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("14702856-620a-3973-7c78-9cfff0876abd"); @@ -14,66 +13,86 @@ public final class HPlusConstants { public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); - public static final byte PREF_VALUE_COUNTRY_CN = 1; - public static final byte PREF_VALUE_COUNTRY_OTHER = 2; + public static final byte ARG_COUNTRY_CN = 1; + public static final byte ARG_COUNTRY_OTHER = 2; - public static final byte PREF_VALUE_CLOCK_24H = 0; - public static final byte PREF_VALUE_CLOCK_12H = 1; + public static final byte ARG_TIMEMODE_24H = 0; + public static final byte ARG_TIMEMODE_12H = 1; - public static final byte PREF_VALUE_UNIT_METRIC = 0; - public static final byte PREF_VALUE_UNIT_IMPERIAL = 1; + public static final byte ARG_UNIT_METRIC = 0; + public static final byte ARG_UNIT_IMPERIAL = 1; - public static final byte PREF_VALUE_GENDER_MALE = 0; - public static final byte PREF_VALUE_GENDER_FEMALE = 1; + public static final byte ARG_GENDER_MALE = 0; + public static final byte ARG_GENDER_FEMALE = 1; - public static final byte PREF_VALUE_HEARTRATE_MEASURE_ON = 11; - public static final byte PREF_VALUE_HEARTRATE_MEASURE_OFF = 22; + public static final byte ARG_HEARTRATE_MEASURE_ON = 11; + public static final byte ARG_HEARTRATE_MEASURE_OFF = 22; - public static final byte PREF_VALUE_HEARTRATE_ALLDAY_ON = 10; - public static final byte PREF_VALUE_HEARTRATE_ALLDAY_OFF = -1; + public static final byte ARG_HEARTRATE_ALLDAY_ON = 10; + public static final byte ARG_HEARTRATE_ALLDAY_OFF = -1; public static final byte INCOMING_CALL_STATE_DISABLED_THRESHOLD = 0x7B; public static final byte INCOMING_CALL_STATE_ENABLED = (byte) 0xAA; - public static final byte[] COMMAND_SET_PREF_START = new byte[]{0x4f, 0x5a}; - public static final byte[] COMMAND_SET_PREF_START1 = new byte[]{0x4d}; - public static final byte COMMAND_SET_PREF_COUNTRY = 0x22; - public static final byte COMMAND_SET_PREF_TIMEMODE = 0x47; - public static final byte COMMAND_SET_PREF_UNIT = 0x48; - public static final byte COMMAND_SET_PREF_SEX = 0x2d; - public static final byte COMMAND_SET_PREF_DATE = 0x08; - public static final byte COMMAND_SET_PREF_TIME = 0x09; - public static final byte COMMAND_SET_PREF_WEEK = 0x2a; - public static final byte COMMAND_SET_PREF_SIT = 0x1e; - public static final byte COMMAND_SET_PREF_WEIGHT = 0x05; - public static final byte COMMAND_SET_PREF_HEIGHT = 0x04; - public static final byte COMMAND_SET_PREF_AGE = 0x2c; - public static final byte COMMAND_SET_PREF_GOAL = 0x26; - public static final byte COMMAND_SET_PREF_SCREENTIME = 0x0b; - public static final byte COMMAND_SET_PREF_BLOOD = 0x4e; //?? - public static final byte COMMAND_SET_PREF_FINDME = 0x0a; - public static final byte COMMAND_SET_PREF_SAVE = 0x17; - public static final byte COMMAND_SET_PREF_END = 0x4f; - public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; - public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; - public static final byte COMMAND_SET_INCOMING_CALL = 0x41; - public static final byte COMMAND_SET_CONF_SAVE = 0x17; - public static final byte COMMAND_SET_CONF_END = 0x4f; - public static final byte COMMAND_SET_PREFS = 0x50; - public static final byte COMMAND_SET_SIT_INTERVAL = 0x51; + public static final byte[] CMD_SET_PREF_START = new byte[]{0x4f, 0x5a}; + public static final byte[] CMD_SET_PREF_START1 = new byte[]{0x4d}; + public static final byte CMD_SET_LANGUAGE = 0x22; + public static final byte CMD_SET_TIMEMODE = 0x47; + public static final byte CMD_SET_UNITS = 0x48; + public static final byte CMD_SET_GENDER = 0x2d; + public static final byte CMD_SET_DATE = 0x08; + public static final byte CMD_SET_TIME = 0x09; + public static final byte CMD_SET_WEEK = 0x2a; + public static final byte CMD_SET_PREF_SIT = 0x1e; + public static final byte CMD_SET_WEIGHT = 0x05; + public static final byte CMD_HEIGHT = 0x04; + public static final byte CMD_SET_AGE = 0x2c; + public static final byte CMD_SET_GOAL = 0x26; + public static final byte CMD_SET_SCREENTIME = 0x0b; + public static final byte CMD_SET_BLOOD = 0x4e; //?? + + public static final byte CMD_SET_FINDME = 0x0a; + public static final byte ARG_FINDME_ON = 0x01; + public static final byte ARG_FINDME_OFF = 0x02; + + public static final byte CMD_GET_VERSION = 0x17; + public static final byte CMD_SET_END = 0x4f; + public static final byte CMD_SET_INCOMING_CALL_NUMBER = 0x23; + public static final byte CMD_SET_ALLDAY_HRM = 0x35; + public static final byte CMD_ACTION_INCOMING_CALL = 0x41; + public static final byte CMD_SET_CONF_END = 0x4f; + public static final byte CMD_SET_PREFS = 0x50; + public static final byte CMD_SET_SIT_INTERVAL = 0x51; public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; //Actions to device - public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; - public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; - public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; - public static final byte[] COMMAND_ACTION_INCOMING_CALL = new byte[] {6, -86}; - public static final byte COMMAND_ACTION_DISPLAY_TEXT_CENTER = 0x23; - public static final byte COMMAND_ACTION_DISPLAY_TEXT_NAME = 0x3F; - public static final byte COMMAND_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312? + public static final byte CMD_GET_ACTIVE_DAY = 0x27; + public static final byte CMD_GET_DAY_DATA = 0x15; + public static final byte CMD_GET_SLEEP = 0x19; + public static final byte CMD_GET_CURR_DATA = 0x16; + public static final byte CMD_GET_DEVICE_ID = 0x24; + public static final byte CMD_ACTION_INCOMING_SOCIAL = 0x31; + //public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; + public static final byte CMD_ACTION_DISPLAY_TEXT = 0x43; + + public static final byte CMD_ACTION_DISPLAY_TEXT_NAME = 0x3F; + public static final byte CMD_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312? + public static final byte[] CMD_ACTION_HELLO = new byte[]{0x01, 0}; + public static final byte CMD_SHUTDOWN = 91; + public static final byte ARG_SHUTDOWN_EN = 90; + + public static final byte CMD_FACTORY_RESET = -74; + public static final byte ARG_FACTORY_RESET_EN = 90; + + + + public static final byte CMD_SET_INCOMING_MESSAGE = 0x07; + public static final byte CMD_SET_INCOMING_CALL = 0x06; + public static final byte ARG_INCOMING_CALL = (byte) -86; + public static final byte ARG_INCOMING_MESSAGE = (byte) -86; //Incoming Messages public static final byte DATA_STATS = 0x33; @@ -81,9 +100,14 @@ public final class HPlusConstants { public static final byte DATA_DAY_SUMMARY = 0x38; public static final byte DATA_DAY_SUMMARY_ALT = 0x39; public static final byte DATA_SLEEP = 0x1A; - public static final byte DATA_INCOMING_CALL_STATE = 0x18; + public static final byte DATA_VERSION = 0x18; + public static final byte DB_TYPE_DAY_SLOT_SUMMARY = 1; + public static final byte DB_TYPE_DAY_SUMMARY = 2; + public static final byte DB_TYPE_INSTANT_STATS = 3; + public static final byte DB_TYPE_SLEEP_STATS = 4; + public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index b8848ebe..3de594b6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -13,6 +13,7 @@ import android.os.Build; import android.os.ParcelUuid; import android.support.annotation.NonNull; +import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; @@ -20,9 +21,9 @@ import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.miband.UserInfo; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -44,20 +45,15 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { @Override @TargetApi(Build.VERSION_CODES.LOLLIPOP) public Collection createBLEScanFilters() { - ParcelUuid mi2Service = new ParcelUuid(HPlusConstants.UUID_SERVICE_HP); - ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mi2Service).build(); + ParcelUuid hpService = new ParcelUuid(HPlusConstants.UUID_SERVICE_HP); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(hpService).build(); return Collections.singletonList(filter); } @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { - if (candidate.supportsService(HPlusConstants.UUID_SERVICE_HP)) { - return DeviceType.HPLUS; - } - String name = candidate.getDevice().getName(); - LOG.debug("Looking for: " + name); if (name != null && name.startsWith("HPLUS")) { return DeviceType.HPLUS; } @@ -97,7 +93,7 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { @Override public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { - return new HPlusSampleProvider(device, session); + return new HPlusHealthSampleProvider(device, session); } @Override @@ -137,7 +133,9 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { - // nothing to delete, yet + Long deviceId = device.getId(); + QueryBuilder qb = session.getHPlusHealthActivitySampleDao().queryBuilder(); + qb.where(HPlusHealthActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); } public static int getFitnessGoal(String address) throws IllegalArgumentException { @@ -181,9 +179,9 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { ActivityUser activityUser = new ActivityUser(); if (activityUser.getGender() == ActivityUser.GENDER_MALE) - return HPlusConstants.PREF_VALUE_GENDER_MALE; + return HPlusConstants.ARG_GENDER_MALE; - return HPlusConstants.PREF_VALUE_GENDER_FEMALE; + return HPlusConstants.ARG_GENDER_FEMALE; } public static int getGoal(String address) { @@ -197,7 +195,7 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getAllDayHR(String address) { - return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10) & 0xFF); + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, HPlusConstants.ARG_HEARTRATE_ALLDAY_ON) & 0xFF); } public static byte getSocial(String address) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java new file mode 100644 index 00000000..eec2fee8 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -0,0 +1,152 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import android.support.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import de.greenrobot.dao.query.QueryBuilder; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlay; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlayDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; + +public class HPlusHealthSampleProvider extends AbstractSampleProvider { + private static final Logger LOG = LoggerFactory.getLogger(HPlusHealthSampleProvider.class); + + + private GBDevice mDevice; + private DaoSession mSession; + + public HPlusHealthSampleProvider(GBDevice device, DaoSession session) { + super(device, session); + + mSession = session; + mDevice = device; + } + + public int getID() { + + return SampleProvider.PROVIDER_HPLUS; + } + + public int normalizeType(int rawType) { + + return rawType; + } + + public int toRawActivityKind(int activityKind) { + + return activityKind; + } + + @NonNull + @Override + protected Property getTimestampSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.Timestamp; + } + + @Override + public HPlusHealthActivitySample createActivitySample() { + return new HPlusHealthActivitySample(); + } + + @Override + protected Property getRawKindSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.RawKind; + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity; //TODO: Calculate actual value + } + + @NonNull + @Override + protected Property getDeviceIdentifierSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.DeviceId; + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getHPlusHealthActivitySampleDao(); + } + + @Override + public List getAllActivitySamples(int timestamp_from, int timestamp_to) { + List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); + + Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); + if (dbDevice == null) { + return Collections.emptyList(); + } + + QueryBuilder qb = getSession().getHPlusHealthActivityOverlayDao().queryBuilder(); + + qb.where(HPlusHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from - 24 * 60 * 60)) + .where(HPlusHealthActivityOverlayDao.Properties.TimestampTo.le(timestamp_to)); + + List overlayRecords = qb.build().list(); + + for (HPlusHealthActivityOverlay overlay : overlayRecords) { + insertVirtualItem(samples, overlay.getTimestampFrom(), overlay.getDeviceId(), overlay.getUserId()); + insertVirtualItem(samples, overlay.getTimestampTo() - 1, overlay.getDeviceId(), overlay.getUserId()); + + for (HPlusHealthActivitySample sample : samples) { + if (overlay.getTimestampFrom() <= sample.getTimestamp() && sample.getTimestamp() < overlay.getTimestampTo()) { + sample.setRawKind(overlay.getRawKind()); + } + } + } + + detachFromSession(); + + LOG.debug("Returning " + samples.size() + " samples processed by " + overlayRecords.size() + " overlays"); + + Collections.sort(samples, new Comparator() { + public int compare(HPlusHealthActivitySample one, HPlusHealthActivitySample other) { + return one.getTimestamp() - other.getTimestamp(); + } + }); + + return samples; + } + + private List insertVirtualItem(List samples, int timestamp, long deviceId, long userId){ + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + timestamp, // ts + deviceId, + userId, // User id + null, // Raw Data + ActivityKind.TYPE_UNKNOWN, + ActivitySample.NOT_MEASURED, // Intensity + ActivitySample.NOT_MEASURED, // Steps + ActivitySample.NOT_MEASURED, // HR + ActivitySample.NOT_MEASURED, // Distance + ActivitySample.NOT_MEASURED // Calories + ); + + sample.setProvider(this); + samples.add(sample); + + return samples; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java deleted file mode 100644 index a26f6be2..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.devices.hplus; - - -/* -* @author João Paulo Barraca <jpbarraca@gmail.com> -*/ - -import android.content.Context; -import android.support.annotation.NonNull; - -import java.util.Collections; -import java.util.List; - -import de.greenrobot.dao.AbstractDao; -import de.greenrobot.dao.query.QueryBuilder; -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDao; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; - -public class HPlusSampleProvider extends AbstractSampleProvider { - - - private GBDevice mDevice; - private DaoSession mSession; - - public HPlusSampleProvider(GBDevice device, DaoSession session) { - super(device, session); - - mSession = session; - mDevice = device;; - } - - public int getID() { - return SampleProvider.PROVIDER_HPLUS; - } - - public int normalizeType(int rawType) { - return rawType; - } - - public int toRawActivityKind(int activityKind) { - return activityKind; - } - - @NonNull - @Override - protected de.greenrobot.dao.Property getTimestampSampleProperty() { - return HPlusHealthActivitySampleDao.Properties.Timestamp; - } - - @Override - public HPlusHealthActivitySample createActivitySample() { - return new HPlusHealthActivitySample(); - } - - @Override - protected de.greenrobot.dao.Property getRawKindSampleProperty() { - return HPlusHealthActivitySampleDao.Properties.RawKind; - } - - @Override - public float normalizeIntensity(int rawIntensity) { - return rawIntensity; //TODO: Calculate actual value - } - - @NonNull - @Override - protected de.greenrobot.dao.Property getDeviceIdentifierSampleProperty() { - return HPlusHealthActivitySampleDao.Properties.DeviceId; - } - - @Override - public AbstractDao getSampleDao() { - return getSession().getHPlusHealthActivitySampleDao(); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java new file mode 100644 index 00000000..cc3afc5f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java @@ -0,0 +1,38 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +/** + * Created by jpbarraca on 30/12/2016. + */ + +public class HPlusDataRecord { + public final static int TYPE_SLEEP = 1; + public int activityKind = ActivityKind.TYPE_UNKNOWN; + + public int timestamp; + public byte[] rawData; + + public HPlusDataRecord(byte[] data){ + rawData = data; + } + + public byte[] getRawData() { + + return rawData; + } + + public class RecordInterval { + public int timestampFrom; + public int timestampTo; + public int activityKind; + + RecordInterval(int timestampFrom, int timestampTo, int activityKind) { + this.timestampFrom = timestampFrom; + this.timestampTo = timestampTo; + this.activityKind = activityKind; + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java new file mode 100644 index 00000000..1c42a1d8 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java @@ -0,0 +1,40 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import java.util.Calendar; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + + +public class HPlusDataRecordDay extends HPlusDataRecord { + int slot; + int steps; + int secondsInactive; + int heartRate; + + public HPlusDataRecordDay(byte[] data) { + super(data); + + int a = (data[4] & 0xFF) * 256 + (data[5] & 0xFF); + if (a >= 144) { + throw new IllegalArgumentException("Invalid Slot Number"); + } + + slot = a; // 10 minute slots as an offset from 0:00 AM + heartRate = data[1] & 0xFF; //Average Heart Rate ? + + if(heartRate == 255) + heartRate = ActivityKind.TYPE_NOT_MEASURED; + + steps = (data[2] & 0xFF) * 256 + data[3] & 0xFF; // Steps in this period + + //?? data[6]; + secondsInactive = data[7] & 0xFF; // ? + + int now = (int) (Calendar.getInstance().getTimeInMillis() / (3600 * 24 * 1000L)); + timestamp = now * 3600 * 24 + (slot / 6 * 3600 + slot % 6 * 10); + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java new file mode 100644 index 00000000..5e363d55 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java @@ -0,0 +1,69 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + + +import java.util.Calendar; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + + +public class HPlusDataRecordRealtime extends HPlusDataRecord { + int distance; + int calories; + int heartRate; + byte battery; + int activeTime; + + public HPlusDataRecordRealtime(byte[] data) { + super(data); + + if (data.length < 15) { + throw new IllegalArgumentException("Invalid data packet"); + } + + timestamp = (int) (Calendar.getInstance().getTimeInMillis() / 1000); + distance = 10 * ((data[4] & 0xFF) * 256 + (data[3] & 0xFF)); // meters + + int x = (data[6] & 0xFF) * 256 + data[5] & 0xFF; + int y = (data[8] & 0xFF) * 256 + data[7] & 0xFF; + + battery = data[9]; + + calories = x + y; // KCal + + heartRate = data[11] & 0xFF; // BPM + activeTime = (data[14] & 0xFF * 256) + (data[13] & 0xFF); + + } + + public void computeActivity(HPlusDataRecordRealtime prev){ + if(prev == null) + return; + + int deltaDistance = distance - prev.distance; + + if(deltaDistance <= 0) + return; + + int deltaTime = timestamp - prev.timestamp; + + if(deltaTime <= 0) + return; + + double speed = deltaDistance / deltaTime; + + if(speed >= 1.6) // ~6 KM/h + activityKind = ActivityKind.TYPE_ACTIVITY; + } + + public boolean same(HPlusDataRecordRealtime other){ + if(other == null) + return false; + + return distance == other.distance && calories == other.calories && heartRate == other.heartRate && battery == other.battery; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java new file mode 100644 index 00000000..839c0882 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java @@ -0,0 +1,79 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public class HPlusDataRecordSleep extends HPlusDataRecord { + private static final Logger LOG = LoggerFactory.getLogger(HPlusDataRecordSleep.class); + + int type = TYPE_SLEEP; + int bedTimeStart; + int bedTimeEnd; + int deepSleepMinutes; + int lightSleepMinutes; + int enterSleepMinutes; + int spindleMinutes; + int remSleepMinutes; + int wakeupMinutes; + int wakeupCount; + + public HPlusDataRecordSleep(byte[] data) { + super(data); + + int year = (data[2] & 0xFF) * 256 + (data[1] & 0xFF); + int month = data[3] & 0xFF; + int day = data[4] & 0xFF; + + if (year < 2000) //Attempt to recover from bug from device. + year += 1900; + + if (year < 2000 || month > 12 || day <= 0 || day > 31) { + throw new IllegalArgumentException("Invalid record date: " + year + "-" + month + "-" + day); + } + + enterSleepMinutes = ((data[6] & 0xFF) * 256 + (data[5] & 0xFF)); + spindleMinutes = ((data[8] & 0xFF) * 256 + (data[7] & 0xFF)); + deepSleepMinutes = ((data[10] & 0xFF) * 256 + (data[9] & 0xFF)); + remSleepMinutes = ((data[12] & 0xFF) * 256 + (data[11] & 0xFF)); + wakeupMinutes = ((data[14] & 0xFF) * 256 + (data[13] & 0xFF)); + wakeupCount = ((data[16] & 0xFF) * 256 + (data[15] & 0xFF)); + + int hour = data[17] & 0xFF; + int minute = data[18] & 0xFF; + + Calendar sleepStart = Calendar.getInstance(); + sleepStart.set(Calendar.YEAR, year); + sleepStart.set(Calendar.MONTH, month - 1); + sleepStart.set(Calendar.DAY_OF_MONTH, day); + sleepStart.set(Calendar.HOUR, hour); + sleepStart.set(Calendar.MINUTE, minute); + sleepStart.set(Calendar.SECOND, 0); + sleepStart.set(Calendar.MILLISECOND, 0); + + bedTimeStart = (int) (sleepStart.getTimeInMillis() / 1000); + bedTimeEnd = (enterSleepMinutes + spindleMinutes + deepSleepMinutes + remSleepMinutes + wakeupMinutes) * 60 + bedTimeStart; + lightSleepMinutes = enterSleepMinutes + spindleMinutes + remSleepMinutes; + + timestamp = bedTimeStart; + } + + public List getIntervals() { + List intervals = new ArrayList(); + + int ts = bedTimeStart + lightSleepMinutes * 60; + intervals.add(new RecordInterval(bedTimeStart, ts, ActivityKind.TYPE_LIGHT_SLEEP)); + intervals.add(new RecordInterval(ts, bedTimeEnd, ActivityKind.TYPE_DEEP_SLEEP)); + return intervals; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java new file mode 100644 index 00000000..cdc18307 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java @@ -0,0 +1,59 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + + +public class HPlusDataRecordSteps extends HPlusDataRecord{ + private static final Logger LOG = LoggerFactory.getLogger(HPlusDataRecordSteps.class); + + int steps; + int distance; + + HPlusDataRecordSteps(byte[] data) { + super(data); + + int year = (data[10] & 0xFF) * 256 + (data[9] & 0xFF); + int month = data[11] & 0xFF; + int day = data[12] & 0xFF; + + if (year < 2000 || month > 12 || day > 31) { + throw new IllegalArgumentException("Invalid record date "+year+"-"+month+"-"+day); + } + steps = (data[2] & 0xFF) * 256 + (data[1] & 0xFF); + distance = (data[4] & 0xFF) * 256 + (data[3] & 0xFF); + + /* + unknown fields + short s12 = (short)(data[5] + data[6] * 256); + short s13 = (short)(data[7] + data[8] * 256); + short s16 = (short)(data[13]) + data[14] * 256); + short s17 = data[15]; + short s18 = data[16]; + */ + + Calendar date = Calendar.getInstance(); + date.set(Calendar.YEAR, year); + date.set(Calendar.MONTH, month - 1); + date.set(Calendar.DAY_OF_MONTH, day); + date.set(Calendar.HOUR, 23); + date.set(Calendar.MINUTE, 59); + date.set(Calendar.SECOND, 59); + date.set(Calendar.MILLISECOND, 999); + + timestamp = (int) (date.getTimeInMillis() / 1000); + } + + public int getType(int ts){ + return ActivityKind.TYPE_UNKNOWN; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java new file mode 100644 index 00000000..a197bd4c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -0,0 +1,453 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + + +import android.content.Context; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusHealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlay; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlayDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; + + +public class HPlusHandlerThread extends GBDeviceIoThread { + + private int SYNC_PERIOD = 60 * 10; + private int SYNC_RETRY_PERIOD = 6; + private int SLEEP_SYNC_PERIOD = 12 * 60 * 60; + private int SLEEP_RETRY_PERIOD = 30; + + private int HELLO_INTERVAL = 30; + + private static final Logger LOG = LoggerFactory.getLogger(HPlusHandlerThread.class); + + private boolean mQuit = false; + private HPlusSupport mHPlusSupport; + private int mLastSlotReceived = 0; + private int mLastSlotRequested = 0; + + private Calendar mLastSleepDayReceived = Calendar.getInstance(); + + private Calendar mHelloTime = Calendar.getInstance(); + + private Calendar mGetDaySlotsTime = Calendar.getInstance(); + private Calendar mGetSleepTime = Calendar.getInstance(); + + private Object waitObject = new Object(); + + private HPlusDataRecordRealtime prevRealTimeRecord = null; + + public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) { + super(gbDevice, context); + + mQuit = false; + + mHPlusSupport = hplusSupport; + + mLastSleepDayReceived.setTimeInMillis(0); + mGetSleepTime.setTimeInMillis(0); + mGetDaySlotsTime.setTimeInMillis(0); + } + + + @Override + public void run() { + mQuit = false; + + sync(); + + boolean starting = true; + long waitTime = 0; + while (!mQuit) { + //LOG.debug("Waiting " + (waitTime)); + if (waitTime > 0) { + synchronized (waitObject) { + try { + waitObject.wait(waitTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + if (mQuit) { + break; + } + + Calendar now = Calendar.getInstance(); + + if (now.compareTo(mHelloTime) > 0) { + sendHello(); + } + + if (now.compareTo(mGetDaySlotsTime) > 0) { + requestNextDaySlots(); + } + + if (now.compareTo(mGetSleepTime) > 0) { + requestNextSleepData(); + } + + now = Calendar.getInstance(); + waitTime = Math.min(Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()), mHelloTime.getTimeInMillis()) - now.getTimeInMillis(); + } + + } + + @Override + public void quit() { + mQuit = true; + synchronized (waitObject) { + waitObject.notify(); + } + } + + public void sync() { + mGetSleepTime.setTimeInMillis(0); + mGetDaySlotsTime.setTimeInMillis(0); + + TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); + + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY}); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA}); + + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, HPlusConstants.ARG_HEARTRATE_ALLDAY_ON}); + + builder.queue(mHPlusSupport.getQueue()); + + synchronized (waitObject) { + waitObject.notify(); + } + } + + public void sendHello() { + mHelloTime = Calendar.getInstance(); + mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL); + + TransactionBuilder builder = new TransactionBuilder("hello"); + builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO); + + builder.queue(mHPlusSupport.getQueue()); + } + + + public void processIncomingDaySlotData(byte[] data) { + + HPlusDataRecordDay record; + try{ + record = new HPlusDataRecordDay(data); + } catch(IllegalArgumentException e){ + LOG.debug((e.getMessage())); + return; + } + + if ((record.slot == 0 && mLastSlotReceived == 0) || (record.slot == mLastSlotReceived + 1)) { + mLastSlotReceived = record.slot; + + try (DBHandler dbHandler = GBApplication.acquireDB()) { + HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + record.timestamp, // ts + deviceId, userId, // User id + record.getRawData(), // Raw Data + ActivityKind.TYPE_UNKNOWN, + ActivitySample.NOT_MEASURED, // Intensity + record.steps, // Steps + record.heartRate, // HR + ActivitySample.NOT_MEASURED, // Distance + ActivitySample.NOT_MEASURED // Calories + ); + sample.setProvider(provider); + + provider.addGBActivitySample(sample); + + } catch (GBException ex) { + LOG.debug((ex.getMessage())); + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } + + if (record.slot >= mLastSlotRequested) { + synchronized (waitObject) { + mGetDaySlotsTime.setTimeInMillis(0); + waitObject.notify(); + } + } + } + } + + private void requestNextDaySlots() { + LOG.debug("Request Next Slot: Got: " + mLastSlotReceived + " Request: " + mLastSlotRequested); + + //Sync Day Stats + byte hour = (byte) ((mLastSlotReceived) / 6); + byte nextHour = (byte) (hour + 1); + + byte nextMinute = 0; + + if (nextHour == (byte) Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) { + nextMinute = (byte) Calendar.getInstance().get(Calendar.MINUTE); + } + + byte minute = (byte) ((mLastSlotReceived % 6) * 10); + + mLastSlotRequested = (nextHour) * 6 + Math.round(nextMinute / 10); + + if (nextHour >= 24 && nextMinute > 0) { // 24 * 6 + LOG.debug("Reached End of the Day"); + mLastSlotRequested = 0; + mLastSlotReceived = 0; + mGetDaySlotsTime = Calendar.getInstance(); + mGetDaySlotsTime.add(Calendar.SECOND, SYNC_PERIOD); + + return; + } + + if (nextHour > Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) { + LOG.debug("Day data is up to date"); + mGetDaySlotsTime = Calendar.getInstance(); + mGetDaySlotsTime.add(Calendar.SECOND, SYNC_PERIOD); + return; + } + + LOG.debug("Making new Request From " + hour + ":" + minute + " to " + nextHour + ":" + nextMinute); + + byte[] msg = new byte[]{39, hour, minute, nextHour, nextMinute}; //Request the entire day + TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); + builder.write(mHPlusSupport.ctrlCharacteristic, msg); + builder.queue(mHPlusSupport.getQueue()); + + mGetDaySlotsTime = Calendar.getInstance(); + mGetDaySlotsTime.add(Calendar.SECOND, SYNC_RETRY_PERIOD); + } + + public void processIncomingSleepData(byte[] data){ + LOG.debug("Processing Sleep Data"); + + HPlusDataRecordSleep record; + + try{ + record = new HPlusDataRecordSleep(data); + } catch(IllegalArgumentException e){ + LOG.debug((e.getMessage())); + return; + } + + mLastSleepDayReceived.setTimeInMillis(record.bedTimeStart * 1000L); + + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + Long userId = DBHelper.getUser(session).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), session).getId(); + + HPlusHealthActivityOverlayDao overlayDao = session.getHPlusHealthActivityOverlayDao(); + HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + + //Insert the Overlays + List overlayList = new ArrayList<>(); + List intervals = record.getIntervals(); + for(HPlusDataRecord.RecordInterval interval : intervals){ + overlayList.add(new HPlusHealthActivityOverlay(interval.timestampFrom, interval.timestampTo, interval.activityKind, deviceId, userId, null)); + } + + overlayDao.insertOrReplaceInTx(overlayList); + + //Store the data + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + record.timestamp, // ts + deviceId, userId, // User id + record.getRawData(), // Raw Data + record.activityKind, + ActivitySample.NOT_MEASURED, // Intensity + ActivitySample.NOT_MEASURED, // Steps + ActivitySample.NOT_MEASURED, // HR + ActivitySample.NOT_MEASURED, // Distance + ActivitySample.NOT_MEASURED // Calories + ); + + sample.setProvider(provider); + + provider.addGBActivitySample(sample); + + + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } + + mGetSleepTime = Calendar.getInstance(); + mGetSleepTime.add(Calendar.SECOND, SLEEP_SYNC_PERIOD); + + } + + private void requestNextSleepData() { + LOG.debug("Request New Sleep Data"); + + mGetSleepTime = Calendar.getInstance(); + mGetSleepTime.add(Calendar.SECOND, SLEEP_RETRY_PERIOD); + + TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); + builder.queue(mHPlusSupport.getQueue()); + } + + + public void processRealtimeStats(byte[] data) { + LOG.debug("Processing Real time Stats"); + + HPlusDataRecordRealtime record; + + try{ + record = new HPlusDataRecordRealtime(data); + } catch(IllegalArgumentException e){ + LOG.debug((e.getMessage())); + return; + } + + if(record.same(prevRealTimeRecord)) + return; + + prevRealTimeRecord = record; + + getDevice().setBatteryLevel(record.battery); + getDevice().sendDeviceUpdateIntent(getContext()); + + //Skip when measuring + if(record.heartRate == 255) { + getDevice().setFirmwareVersion2("---"); + getDevice().sendDeviceUpdateIntent(getContext()); + return; + } + + getDevice().setFirmwareVersion2(""+record.heartRate); + getDevice().sendDeviceUpdateIntent(getContext()); + + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + + HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + HPlusHealthActivityOverlayDao overlayDao = session.getHPlusHealthActivityOverlayDao(); + + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + record.timestamp, // ts + deviceId, userId, // User id + record.getRawData(), // Raw Data + record.activityKind, + ActivitySample.NOT_MEASURED, // Intensity + ActivitySample.NOT_MEASURED, // Steps + record.heartRate, // HR + record.distance, // Distance + record.calories // Calories + ); + + sample.setProvider(provider); + provider.addGBActivitySample(sample); + + if(record.activeTime > 0){ + //TODO: Register ACTIVITY Time + + //Insert the Overlays + //List overlayList = new ArrayList<>(); + //overlayList.add(new HPlusHealthActivityOverlay(record.timestamp - record.activeTime * 60, record.timestamp, ActivityKind.TYPE_ACTIVITY, deviceId, userId, null)); + //overlayDao.insertOrReplaceInTx(overlayList); + } + + } catch (GBException ex) { + LOG.debug((ex.getMessage())); + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } + } + + + public void processStepStats(byte[] data) { + LOG.debug("Processing Step Stats"); + HPlusDataRecordSteps record; + + try{ + record = new HPlusDataRecordSteps(data); + } catch(IllegalArgumentException e){ + LOG.debug((e.getMessage())); + return; + } + + try (DBHandler dbHandler = GBApplication.acquireDB()) { + HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + record.timestamp, // ts + deviceId, userId, // User id + record.getRawData(), // Raw Data + ActivityKind.TYPE_UNKNOWN, + ActivitySample.NOT_MEASURED, // Intensity + record.steps, // Steps + ActivitySample.NOT_MEASURED, // HR + record.distance, // Distance + ActivitySample.NOT_MEASURED // Calories + ); + sample.setProvider(provider); + provider.addGBActivitySample(sample); + } catch (GBException ex) { + LOG.debug((ex.getMessage())); + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } + } + + public boolean processVersion(byte[] data) { + LOG.debug("Process Version"); + + int major = data[2] & 0xFF; + int minor = data[1] & 0xFF; + + getDevice().setFirmwareVersion(major + "." + minor); + + getDevice().sendDeviceUpdateIntent(getContext()); + + return true; + } + + public static HPlusHealthActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { + HPlusHealthActivitySample sample = new HPlusHealthActivitySample(); + sample.setDevice(device); + sample.setUser(user); + sample.setTimestamp(timestampInSeconds); + sample.setProvider(provider); + + return sample; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java deleted file mode 100644 index bed5d581..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java +++ /dev/null @@ -1,86 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Calendar; - - - -public class HPlusSleepRecord { - private long bedTimeStart; - private long bedTimeEnd; - private int deepSleepSeconds; - private int spindleSeconds; - private int remSleepSeconds; - private int wakeupTime; - private int wakeupCount; - private int enterSleepSeconds; - private byte[] rawData; - - HPlusSleepRecord(byte[] data) { - rawData = data; - int year = data[2] * 256 + data[1]; - int month = data[3]; - int day = data[4]; - - enterSleepSeconds = data[6] * 256 + data[5]; - spindleSeconds = data[8] * 256 + data[7]; - deepSleepSeconds = data[10] * 256 + data[9]; - remSleepSeconds = data[12] * 256 + data[11]; - wakeupTime = data[14] * 256 + data[13]; - wakeupCount = data[16] * 256 + data[15]; - int hour = data[17]; - int minute = data[18]; - - Calendar c = Calendar.getInstance(); - c.set(Calendar.YEAR, year); - c.set(Calendar.MONTH, month); - c.set(Calendar.DAY_OF_MONTH, day); - c.set(Calendar.HOUR, hour); - c.set(Calendar.MINUTE, minute); - c.set(Calendar.SECOND, 0); - c.set(Calendar.MILLISECOND, 0); - - bedTimeStart = (c.getTimeInMillis() / 1000L); - bedTimeEnd = bedTimeStart + enterSleepSeconds + spindleSeconds + deepSleepSeconds + remSleepSeconds + wakeupTime; - } - - byte[] getRawData() { - - return rawData; - } - - public long getBedTimeStart() { - return bedTimeStart; - } - - public long getBedTimeEnd() { - return bedTimeEnd; - } - - public int getDeepSleepSeconds() { - return deepSleepSeconds; - } - - public int getSpindleSeconds() { - return spindleSeconds; - } - - public int getRemSleepSeconds() { - return remSleepSeconds; - } - - public int getWakeupTime() { - return wakeupTime; - } - - public int getWakeupCount() { - return wakeupCount; - } - - public int getEnterSleepSeconds() { - return enterSleepSeconds; - } - -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index c93a4f65..65350305 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -23,22 +23,10 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.GBException; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; @@ -58,12 +46,13 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; public class HPlusSupport extends AbstractBTLEDeviceSupport { private static final Logger LOG = LoggerFactory.getLogger(HPlusSupport.class); - private BluetoothGattCharacteristic ctrlCharacteristic = null; - private BluetoothGattCharacteristic measureCharacteristic = null; + public BluetoothGattCharacteristic ctrlCharacteristic = null; + public BluetoothGattCharacteristic measureCharacteristic = null; - private byte[] lastDataStats = null; + private int[] lastDataStats = null; private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private HPlusHandlerThread syncHelper; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -85,55 +74,68 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { IntentFilter intentFilter = new IntentFilter(); broadcastManager.registerReceiver(mReceiver, intentFilter); + } @Override public void dispose() { + LOG.debug("Dispose"); LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); broadcastManager.unregisterReceiver(mReceiver); super.dispose(); + + if(syncHelper != null) + syncHelper.quit(); } @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + LOG.debug("Initializing"); + + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); measureCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE); ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + getDevice().setFirmwareVersion("N/A"); + getDevice().setFirmwareVersion2("N/A"); - builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); - - getDevice().setFirmwareVersion("0"); - getDevice().setFirmwareVersion2("0"); + syncHelper = new HPlusHandlerThread(getDevice(), getContext(), this); //Initialize device - syncPreferences(builder); //Sync preferences + sendUserInfo(builder); //Sync preferences setSIT(builder); //Sync SIT Interval setCurrentDate(builder); // Sync Current Date setCurrentTime(builder); // Sync Current Time - builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); + requestDeviceInfo(builder); + setInitialized(builder); + + + syncHelper.start(); + + builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); builder.setGattCallback(this); builder.notify(measureCharacteristic, true); - setInitialized(builder); + //LOG.debug("Initialization Done"); + return builder; } private HPlusSupport sendUserInfo(TransactionBuilder builder) { - builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START); - builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START1); + builder.write(ctrlCharacteristic, HPlusConstants.CMD_SET_PREF_START); + builder.write(ctrlCharacteristic, HPlusConstants.CMD_SET_PREF_START1); syncPreferences(builder); - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_SAVE}); - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_END}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_CONF_END}); return this; } private HPlusSupport syncPreferences(TransactionBuilder transaction) { - LOG.info("Attempting to sync preferences..."); + LOG.info("Attempting to sync preferences with: " + getDevice().getAddress()); byte gender = HPlusCoordinator.getUserGender(getDevice().getAddress()); byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); @@ -159,7 +161,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREFS, + HPlusConstants.CMD_SET_PREFS, gender, age, bodyHeight, @@ -174,20 +176,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { social, allDayHeart, wrist, + 0, alertTimeHour, alertTimeMinute, unit, timemode }); + + setAllDayHeart(transaction); + return this; } - private HPlusSupport setCountry(TransactionBuilder transaction) { - LOG.info("Attempting to set country..."); + private HPlusSupport setLanguage(TransactionBuilder transaction) { + LOG.info("Attempting to set language..."); byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_COUNTRY, + HPlusConstants.CMD_SET_LANGUAGE, value }); return this; @@ -199,7 +205,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_TIMEMODE, + HPlusConstants.CMD_SET_TIMEMODE, value }); return this; @@ -211,7 +217,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_UNIT, + HPlusConstants.CMD_SET_UNITS, value }); return this; @@ -226,7 +232,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { int day = c.get(Calendar.DAY_OF_MONTH); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_DATE, + HPlusConstants.CMD_SET_DATE, (byte) ((year / 256) & 0xff), (byte) (year % 256), (byte) (month + 1), @@ -242,7 +248,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { Calendar c = Calendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_TIME, + HPlusConstants.CMD_SET_TIME, (byte) c.get(Calendar.HOUR_OF_DAY), (byte) c.get(Calendar.MINUTE), (byte) c.get(Calendar.SECOND) @@ -258,7 +264,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { Calendar c = Calendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_WEEK, + HPlusConstants.CMD_SET_WEEK, (byte) c.get(Calendar.DAY_OF_WEEK) }); return this; @@ -274,7 +280,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { Calendar now = Calendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_SIT_INTERVAL, + HPlusConstants.CMD_SET_SIT_INTERVAL, (byte) ((startTime / 256) & 0xff), (byte) (startTime % 256), (byte) ((endTime / 256) & 0xff), @@ -302,7 +308,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_WEIGHT, + HPlusConstants.CMD_SET_WEIGHT, value }); @@ -314,7 +320,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_HEIGHT, + HPlusConstants.CMD_HEIGHT, value }); @@ -327,19 +333,19 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_AGE, + HPlusConstants.CMD_SET_AGE, value }); return this; } - private HPlusSupport setSex(TransactionBuilder transaction) { - LOG.info("Attempting to set Sex..."); + private HPlusSupport setGender(TransactionBuilder transaction) { + LOG.info("Attempting to set Gender..."); byte value = HPlusCoordinator.getUserGender(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SEX, + HPlusConstants.CMD_SET_GENDER, value }); @@ -352,7 +358,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { int value = HPlusCoordinator.getGoal(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_GOAL, + HPlusConstants.CMD_SET_GOAL, (byte) ((value / 256) & 0xff), (byte) (value % 256) @@ -366,7 +372,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SCREENTIME, + HPlusConstants.CMD_SET_SCREENTIME, value }); @@ -378,7 +384,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, + HPlusConstants.CMD_SET_ALLDAY_HRM, value }); @@ -407,8 +413,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport requestDeviceInfo(TransactionBuilder builder) { LOG.debug("Requesting Device Info!"); - BluetoothGattCharacteristic deviceName = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME); - builder.read(deviceName); + + // HPlus devices seem to report some information in an alternative manner + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); + return this; } @@ -435,7 +444,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { public void onNotification(NotificationSpec notificationSpec) { LOG.debug("Got Notification"); //TODO: Show different notifications acccording to source as Band supports this - showText(notificationSpec.body); + showText(notificationSpec.title, notificationSpec.body); } @@ -515,7 +524,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onFetchActivityData() { - + if(syncHelper != null) + syncHelper.sync(); } @Override @@ -531,7 +541,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, 0x10}); //Set Real Time... ? + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, 0x0A}); //Set Real Time... ? builder.queue(getQueue()); } @@ -545,11 +555,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte state; if (enable) - state = HPlusConstants.PREF_VALUE_HEARTRATE_ALLDAY_ON; + state = HPlusConstants.ARG_HEARTRATE_ALLDAY_ON; else - state = HPlusConstants.PREF_VALUE_HEARTRATE_ALLDAY_OFF; + state = HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF; - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, state}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state}); builder.queue(getQueue()); } @@ -561,12 +571,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = performInitialized("findMe"); byte[] msg = new byte[2]; - msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; + msg[0] = HPlusConstants.CMD_SET_FINDME; if (start) - msg[1] = 1; + msg[1] = HPlusConstants.ARG_FINDME_ON; else - msg[1] = 0; + msg[1] = HPlusConstants.ARG_FINDME_OFF; builder.write(ctrlCharacteristic, msg); builder.queue(getQueue()); @@ -586,7 +596,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = performInitialized("vibration"); byte[] msg = new byte[15]; - msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; + msg[0] = HPlusConstants.CMD_SET_INCOMING_CALL_NUMBER; for (int i = 0; i < msg.length - 1; i++) msg[i + 1] = (byte) "GadgetBridge".charAt(i); @@ -630,17 +640,17 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } - private void showIncomingCall(String name, String number){ + private void showIncomingCall(String name, String number) { LOG.debug("Show Incoming Call"); try { TransactionBuilder builder = performInitialized("incomingCallIcon"); //Enable call notifications - builder.write(ctrlCharacteristic, new byte[] {HPlusConstants.COMMAND_SET_INCOMING_CALL, 1 }); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, 1}); //Show Call Icon - builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_ACTION_INCOMING_CALL); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, HPlusConstants.ARG_INCOMING_CALL}); //builder = performInitialized("incomingCallText"); builder.queue(getQueue()); @@ -660,11 +670,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { for (int i = 0; i < msg.length; i++) msg[i] = ' '; - for(int i = 0; i < number.length() && i < (msg.length - 1); i++) + for (int i = 0; i < number.length() && i < (msg.length - 1); i++) msg[i + 1] = (byte) number.charAt(i); - msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_CENTER; + msg[0] = HPlusConstants.CMD_SET_INCOMING_CALL_NUMBER; builder.write(ctrlCharacteristic, msg); builder.queue(getQueue()); @@ -682,10 +692,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { for (int i = 0; i < msg.length; i++) msg[i] = ' '; - for(int i = 0; i < name.length() && i < (msg.length - 1); i++) + for (int i = 0; i < name.length() && i < (msg.length - 1); i++) msg[i + 1] = (byte) name.charAt(i); - msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_NAME; + msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME; builder.write(ctrlCharacteristic, msg); try { @@ -694,17 +704,18 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { e.printStackTrace(); } - msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_NAME_CN; + msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME_CN; builder.write(ctrlCharacteristic, msg); builder.queue(getQueue()); - }catch(IOException e){ + } catch (IOException e) { GB.toast(getContext(), "Error showing incoming call: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); } } private void showText(String message) { + showText(null, message); } @@ -719,7 +730,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { for (int i = 0; i < msg.length; i++) msg[i] = ' '; - msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT; + msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT; String message = ""; @@ -737,7 +748,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { int length = message.length() / 17; - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_ACTION_INCOMING_SOCIAL, (byte) 255}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_SOCIAL, (byte) 255}); int remaining; @@ -773,7 +784,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.write(ctrlCharacteristic, msg); builder.queue(getQueue()); - }catch(IOException e){ + } catch (IOException e) { GB.toast(getContext(), "Error showing device Notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); } @@ -799,17 +810,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return true; switch (data[0]) { - case HPlusConstants.DATA_STATS: - return processDataStats(data); - case HPlusConstants.DATA_SLEEP: - return processSleepStats(data); - case HPlusConstants.DATA_STEPS: - return processStepStats(data); + case HPlusConstants.DATA_VERSION: + syncHelper.processVersion(data); + case HPlusConstants.DATA_STATS: { + syncHelper.processRealtimeStats(data); + return true; + } + case HPlusConstants.DATA_SLEEP: { + syncHelper.processIncomingSleepData(data); + return true; + } + case HPlusConstants.DATA_STEPS:{ + syncHelper.processStepStats(data); + return true; + } case HPlusConstants.DATA_DAY_SUMMARY: case HPlusConstants.DATA_DAY_SUMMARY_ALT: - return processDaySummary(data); - case HPlusConstants.DATA_INCOMING_CALL_STATE: - return processIncomingCallState(data); + syncHelper.processIncomingDaySlotData(data); + return true; default: LOG.info("Unhandled characteristic changed: " + characteristicUUID); @@ -817,197 +835,5 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return false; } - private boolean processIncomingCallState(byte[] data){ - LOG.debug("Process Incoming Call State"); - //Disabled now - return true; - } - /* - Receives a message containing the status of the day. - */ - private boolean processDaySummary(byte[] data) { - LOG.debug("Process Day Summary"); - int a = data[4] * 256 + data[5]; - if (a < 144) { - int slot = a * 2; // 10 minute slots as an offset from 0:00 AM - int avgHR = data[1]; //Average Heart Rate ? - int steps = data[2] * 256 + data[3]; // Steps in this period - - //?? data[6]; - int timeInactive = data[7]; // ? - - LOG.debug("Day Stats: Slot: " + slot + " HR: " + avgHR + " Steps: " + steps + " TimeInactive: " + timeInactive); - - 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); - HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); - - - //TODO: Store Sample. How? - - //provider.addGBActivitySample(record); - - - } catch (GBException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - - } else - LOG.error("Invalid day stats"); - - return true; - } - - private boolean processStepStats(byte[] data) { - LOG.debug("Process Step Stats"); - - if (data.length < 19) { - LOG.error("Invalid Steps Message Length " + data.length); - return false; - } - /* - This is a dump of the entire day. - */ - int year = data[9] + data[10] * 256; - short month = data[11]; - short day = data[12]; - int steps = data[2] * 256 + data[1]; - - float distance = ((float) (data[3] + data[4] * 256) / 100.0f); - - /* - unknown fields - short s12 = (short)(data[5] + data[6] * 256); - short s13 = (short)(data[7] + data[8] * 256); - short s16 = (short)(data[13]) + data[14] * 256); - short s17 = data[15]; - short s18 = data[16]; - */ - - - LOG.debug("Step Stats: Year: " + year + " Month: " + month + " Day:"); - 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); - HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); - - - //TODO: Store Sample. How? - - //provider.addGBActivitySample(record); - - - } catch (GBException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - - return true; - } - - - private boolean processSleepStats(byte[] data) { - LOG.debug("Process Sleep Stats"); - - if (data.length < 19) { - LOG.error("Invalid Sleep Message Length " + data.length); - return false; - } - HPlusSleepRecord record = new HPlusSleepRecord(data); - - 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); - HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); - - //TODO: Store Sample. How? - - //provider.addGBActivitySample(record); - - - } catch (GBException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - - return true; - } - - - private boolean processDataStats(byte[] data) { - //TODO: Store Calories and Distance. How? - - LOG.debug("Process Data Stats"); - - if (data.length < 15) { - LOG.error("Invalid Stats Message Length " + data.length); - return false; - } - - //Ignore duplicate packets - if(data.equals(lastDataStats)) - return true; - - lastDataStats = data.clone(); - - double distance = ((int) data[4] * 256 + data[3]) / 100.0; - - int x = (int) data[6] * 256 + data[5]; - int y = (int) data[8] * 256 + data[7]; - int calories = x + y; - - int bpm = (data[11] == -1) ? HPlusHealthActivitySample.NOT_MEASURED : data[11]; - - 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); - HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); - - - if (bpm != HPlusHealthActivitySample.NOT_MEASURED) { - HPlusHealthActivitySample sample = createActivitySample(device, user, ts, provider); - sample.setHeartRate(bpm); - sample.setSteps(0); - sample.setRawIntensity(ActivitySample.NOT_MEASURED); - sample.setRawKind(ActivityKind.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? - provider.addGBActivitySample(sample); - } - } catch (GBException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - - - return true; - } - - - public HPlusHealthActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { - HPlusHealthActivitySample sample = new HPlusHealthActivitySample(); - sample.setDevice(device); - sample.setUser(user); - sample.setTimestamp(timestampInSeconds); - sample.setProvider(provider); - - return sample; - } } From 547736f8f7ca7060f4f9c99d6ddfca359be43d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Mon, 2 Jan 2017 10:13:34 +0000 Subject: [PATCH 090/244] HPlus: removed test values --- .../gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java index eec2fee8..61ce6a2e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -101,7 +101,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider qb = getSession().getHPlusHealthActivityOverlayDao().queryBuilder(); - qb.where(HPlusHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from - 24 * 60 * 60)) + qb.where(HPlusHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from)) .where(HPlusHealthActivityOverlayDao.Properties.TimestampTo.le(timestamp_to)); List overlayRecords = qb.build().list(); From 0ba377bb42f170bb120ead87df72a2a195f2db3e Mon Sep 17 00:00:00 2001 From: Hasan Ammar Date: Mon, 2 Jan 2017 19:03:02 -0500 Subject: [PATCH 091/244] Show K9 message body in notifications --- .../gadgetbridge/externalevents/NotificationListener.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index b033c741..130ea7d5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -252,6 +252,10 @@ public class NotificationListener extends NotificationListenerService { notificationSpec.type = AppNotificationType.getInstance().get(source); + if (source.equals("com.fsck.k9")) { + preferBigText = true; + } + if (notificationSpec.type == null) { notificationSpec.type = NotificationType.UNKNOWN; } From e70d6a1260270da695f500e23e323ec4b177be01 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 3 Jan 2017 12:46:22 +0100 Subject: [PATCH 092/244] First take at issue template --- .github/ISSUE_TEMPLATE.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..51177558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,8 @@ +####Your issue is: +*In case of a bug, do not forget to attach logs!* + +####Your wearable device is: + +*Please specify model and firmware version if possible* + +####Your android version is: From fd51f32765c40e0f125a4bbc5d2575ecbb1a66ec Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 3 Jan 2017 20:36:39 +0100 Subject: [PATCH 093/244] Add Riot as a recognized chat application (including pebble color and icon) --- .../freeyourgadget/gadgetbridge/model/AppNotificationType.java | 3 +++ .../freeyourgadget/gadgetbridge/model/NotificationType.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java index 001bb6cf..bfbe48dd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java @@ -29,6 +29,9 @@ public class AppNotificationType extends HashMap { // Conversations put("eu.siacs.conversations", NotificationType.CONVERSATIONS); + // Riot + put("im.vector.alpha", NotificationType.RIOT); + // Signal put("org.thoughtcrime.securesms", NotificationType.SIGNAL); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java index e3e32070..daae8f4c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java @@ -13,6 +13,7 @@ public enum NotificationType { GENERIC_SMS(PebbleIconID.GENERIC_SMS, PebbleColor.VividViolet), FACEBOOK(PebbleIconID.NOTIFICATION_FACEBOOK, PebbleColor.Liberty), FACEBOOK_MESSENGER(PebbleIconID.NOTIFICATION_FACEBOOK_MESSENGER, PebbleColor.VeryLightBlue), + RIOT(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.LavenderIndigo), SIGNAL(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.BlueMoon), TWITTER(PebbleIconID.NOTIFICATION_TWITTER, PebbleColor.BlueMoon), TELEGRAM(PebbleIconID.NOTIFICATION_TELEGRAM, PebbleColor.PictonBlue), @@ -46,6 +47,7 @@ public enum NotificationType { return "generic_social"; case CONVERSATIONS: case FACEBOOK_MESSENGER: + case RIOT: case SIGNAL: case TELEGRAM: case WHATSAPP: From 9d67394720e1df1951b9909a86e13f9f2a9cf8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 4 Jan 2017 01:46:24 +0000 Subject: [PATCH 094/244] HPlus: Code cleanup --- .../devices/hplus/HPlusConstants.java | 1 + .../hplus/HPlusHealthSampleProvider.java | 21 +- .../devices/hplus/HPlusDataRecord.java | 9 +- .../devices/hplus/HPlusDataRecordDay.java | 8 +- .../hplus/HPlusDataRecordRealtime.java | 23 ++- .../devices/hplus/HPlusDataRecordSleep.java | 32 ++- .../devices/hplus/HPlusDataRecordSteps.java | 19 +- .../devices/hplus/HPlusHandlerThread.java | 121 +++++------ .../service/devices/hplus/HPlusSupport.java | 194 +++++++----------- 9 files changed, 188 insertions(+), 240 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 9f82d13d..70546e87 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -36,6 +36,7 @@ public final class HPlusConstants { public static final byte[] CMD_SET_PREF_START = new byte[]{0x4f, 0x5a}; public static final byte[] CMD_SET_PREF_START1 = new byte[]{0x4d}; + public static final byte CMD_SET_ALARM = 0x4c; public static final byte CMD_SET_LANGUAGE = 0x22; public static final byte CMD_SET_TIMEMODE = 0x47; public static final byte CMD_SET_UNITS = 0x48; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java index 61ce6a2e..3fe16e41 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -6,9 +6,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices.hplus; import android.support.annotation.NonNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -30,8 +27,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; public class HPlusHealthSampleProvider extends AbstractSampleProvider { - private static final Logger LOG = LoggerFactory.getLogger(HPlusHealthSampleProvider.class); - private GBDevice mDevice; private DaoSession mSession; @@ -71,12 +66,12 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider getAllActivitySamples(int timestamp_from, int timestamp_to) { List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); @@ -107,11 +103,11 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider overlayRecords = qb.build().list(); for (HPlusHealthActivityOverlay overlay : overlayRecords) { - insertVirtualItem(samples, overlay.getTimestampFrom(), overlay.getDeviceId(), overlay.getUserId()); - insertVirtualItem(samples, overlay.getTimestampTo() - 1, overlay.getDeviceId(), overlay.getUserId()); + insertVirtualItem(samples, Math.max(overlay.getTimestampFrom(), timestamp_from), overlay.getDeviceId(), overlay.getUserId()); + insertVirtualItem(samples, Math.min(overlay.getTimestampTo() - 1, timestamp_to - 1), overlay.getDeviceId(), overlay.getUserId()); for (HPlusHealthActivitySample sample : samples) { - if (overlay.getTimestampFrom() <= sample.getTimestamp() && sample.getTimestamp() < overlay.getTimestampTo()) { + if (sample.getTimestamp() >= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) { sample.setRawKind(overlay.getRawKind()); } } @@ -119,8 +115,6 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider() { public int compare(HPlusHealthActivitySample one, HPlusHealthActivitySample other) { return one.getTimestamp() - other.getTimestamp(); @@ -137,7 +131,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider getIntervals() { - List intervals = new ArrayList(); + List intervals = new ArrayList<>(); int ts = bedTimeStart + lightSleepMinutes * 60; intervals.add(new RecordInterval(bedTimeStart, ts, ActivityKind.TYPE_LIGHT_SLEEP)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java index cdc18307..33901ddd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java @@ -5,19 +5,12 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; */ -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Calendar; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; - -public class HPlusDataRecordSteps extends HPlusDataRecord{ - private static final Logger LOG = LoggerFactory.getLogger(HPlusDataRecordSteps.class); - - int steps; - int distance; +class HPlusDataRecordSteps extends HPlusDataRecord{ + public int steps; + public int distance; HPlusDataRecordSteps(byte[] data) { super(data); @@ -45,15 +38,11 @@ public class HPlusDataRecordSteps extends HPlusDataRecord{ date.set(Calendar.YEAR, year); date.set(Calendar.MONTH, month - 1); date.set(Calendar.DAY_OF_MONTH, day); - date.set(Calendar.HOUR, 23); + date.set(Calendar.HOUR_OF_DAY, 23); date.set(Calendar.MINUTE, 59); date.set(Calendar.SECOND, 59); date.set(Calendar.MILLISECOND, 999); timestamp = (int) (date.getTimeInMillis() / 1000); } - - public int getType(int ts){ - return ActivityKind.TYPE_UNKNOWN; - } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index a197bd4c..fe8ebffc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -18,15 +18,12 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusHealthSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlay; import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -34,16 +31,15 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; -public class HPlusHandlerThread extends GBDeviceIoThread { +class HPlusHandlerThread extends GBDeviceIoThread { + private static final Logger LOG = LoggerFactory.getLogger(HPlusHandlerThread.class); private int SYNC_PERIOD = 60 * 10; private int SYNC_RETRY_PERIOD = 6; private int SLEEP_SYNC_PERIOD = 12 * 60 * 60; private int SLEEP_RETRY_PERIOD = 30; - private int HELLO_INTERVAL = 30; - - private static final Logger LOG = LoggerFactory.getLogger(HPlusHandlerThread.class); + private int HELLO_INTERVAL = 60; private boolean mQuit = false; private HPlusSupport mHPlusSupport; @@ -57,7 +53,7 @@ public class HPlusHandlerThread extends GBDeviceIoThread { private Calendar mGetDaySlotsTime = Calendar.getInstance(); private Calendar mGetSleepTime = Calendar.getInstance(); - private Object waitObject = new Object(); + private final Object waitObject = new Object(); private HPlusDataRecordRealtime prevRealTimeRecord = null; @@ -80,7 +76,6 @@ public class HPlusHandlerThread extends GBDeviceIoThread { sync(); - boolean starting = true; long waitTime = 0; while (!mQuit) { //LOG.debug("Waiting " + (waitTime)); @@ -147,25 +142,27 @@ public class HPlusHandlerThread extends GBDeviceIoThread { } } - public void sendHello() { - mHelloTime = Calendar.getInstance(); - mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL); - + private void sendHello() { TransactionBuilder builder = new TransactionBuilder("hello"); builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO); builder.queue(mHPlusSupport.getQueue()); + scheduleHello(); } + public void scheduleHello(){ + mHelloTime = Calendar.getInstance(); + mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL); + } - public void processIncomingDaySlotData(byte[] data) { + public boolean processIncomingDaySlotData(byte[] data) { HPlusDataRecordDay record; try{ record = new HPlusDataRecordDay(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); - return; + return true; } if ((record.slot == 0 && mLastSlotReceived == 0) || (record.slot == mLastSlotReceived + 1)) { @@ -181,7 +178,7 @@ public class HPlusHandlerThread extends GBDeviceIoThread { deviceId, userId, // User id record.getRawData(), // Raw Data ActivityKind.TYPE_UNKNOWN, - ActivitySample.NOT_MEASURED, // Intensity + 0, // Intensity record.steps, // Steps record.heartRate, // HR ActivitySample.NOT_MEASURED, // Distance @@ -204,11 +201,10 @@ public class HPlusHandlerThread extends GBDeviceIoThread { } } } + return true; } private void requestNextDaySlots() { - LOG.debug("Request Next Slot: Got: " + mLastSlotReceived + " Request: " + mLastSlotRequested); - //Sync Day Stats byte hour = (byte) ((mLastSlotReceived) / 6); byte nextHour = (byte) (hour + 1); @@ -240,7 +236,7 @@ public class HPlusHandlerThread extends GBDeviceIoThread { return; } - LOG.debug("Making new Request From " + hour + ":" + minute + " to " + nextHour + ":" + nextMinute); + //LOG.debug("Making new Request From " + hour + ":" + minute + " to " + nextHour + ":" + nextMinute); byte[] msg = new byte[]{39, hour, minute, nextHour, nextMinute}; //Request the entire day TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); @@ -251,16 +247,14 @@ public class HPlusHandlerThread extends GBDeviceIoThread { mGetDaySlotsTime.add(Calendar.SECOND, SYNC_RETRY_PERIOD); } - public void processIncomingSleepData(byte[] data){ - LOG.debug("Processing Sleep Data"); - + public boolean processIncomingSleepData(byte[] data){ HPlusDataRecordSleep record; try{ record = new HPlusDataRecordSleep(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); - return; + return true; } mLastSleepDayReceived.setTimeInMillis(record.bedTimeStart * 1000L); @@ -288,7 +282,7 @@ public class HPlusHandlerThread extends GBDeviceIoThread { deviceId, userId, // User id record.getRawData(), // Raw Data record.activityKind, - ActivitySample.NOT_MEASURED, // Intensity + 0, // Intensity ActivitySample.NOT_MEASURED, // Steps ActivitySample.NOT_MEASURED, // HR ActivitySample.NOT_MEASURED, // Distance @@ -307,11 +301,10 @@ public class HPlusHandlerThread extends GBDeviceIoThread { mGetSleepTime = Calendar.getInstance(); mGetSleepTime.add(Calendar.SECOND, SLEEP_SYNC_PERIOD); + return true; } private void requestNextSleepData() { - LOG.debug("Request New Sleep Data"); - mGetSleepTime = Calendar.getInstance(); mGetSleepTime.add(Calendar.SECOND, SLEEP_RETRY_PERIOD); @@ -321,20 +314,18 @@ public class HPlusHandlerThread extends GBDeviceIoThread { } - public void processRealtimeStats(byte[] data) { - LOG.debug("Processing Real time Stats"); - + public boolean processRealtimeStats(byte[] data) { HPlusDataRecordRealtime record; try{ record = new HPlusDataRecordRealtime(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); - return; + return true; } if(record.same(prevRealTimeRecord)) - return; + return true; prevRealTimeRecord = record; @@ -345,18 +336,14 @@ public class HPlusHandlerThread extends GBDeviceIoThread { if(record.heartRate == 255) { getDevice().setFirmwareVersion2("---"); getDevice().sendDeviceUpdateIntent(getContext()); - return; + return true; } - getDevice().setFirmwareVersion2(""+record.heartRate); + getDevice().setFirmwareVersion2("" + record.heartRate); getDevice().sendDeviceUpdateIntent(getContext()); try (DBHandler dbHandler = GBApplication.acquireDB()) { - DaoSession session = dbHandler.getDaoSession(); - HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); - HPlusHealthActivityOverlayDao overlayDao = session.getHPlusHealthActivityOverlayDao(); - Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); @@ -365,7 +352,7 @@ public class HPlusHandlerThread extends GBDeviceIoThread { deviceId, userId, // User id record.getRawData(), // Raw Data record.activityKind, - ActivitySample.NOT_MEASURED, // Intensity + record.intensity, // Intensity ActivitySample.NOT_MEASURED, // Steps record.heartRate, // HR record.distance, // Distance @@ -375,32 +362,25 @@ public class HPlusHandlerThread extends GBDeviceIoThread { sample.setProvider(provider); provider.addGBActivitySample(sample); - if(record.activeTime > 0){ - //TODO: Register ACTIVITY Time - - //Insert the Overlays - //List overlayList = new ArrayList<>(); - //overlayList.add(new HPlusHealthActivityOverlay(record.timestamp - record.activeTime * 60, record.timestamp, ActivityKind.TYPE_ACTIVITY, deviceId, userId, null)); - //overlayDao.insertOrReplaceInTx(overlayList); - } + //TODO: Handle Active Time. With Overlay? } catch (GBException ex) { LOG.debug((ex.getMessage())); } catch (Exception ex) { LOG.debug(ex.getMessage()); } + return true; } - public void processStepStats(byte[] data) { - LOG.debug("Processing Step Stats"); + public boolean processStepStats(byte[] data) { HPlusDataRecordSteps record; try{ record = new HPlusDataRecordSteps(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); - return; + return true; } try (DBHandler dbHandler = GBApplication.acquireDB()) { @@ -408,17 +388,40 @@ public class HPlusHandlerThread extends GBDeviceIoThread { Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + + //Hugly (?) fix. + //This message returns the day summary, but the DB already has some detailed entries with steps and distance. + //However DB data is probably incomplete as some update messages could be missing + //Proposed fix: Calculate the total steps and distance and store a new sample with the remaining data + //Existing data will reflect user activity with the issue of a potencially large number of steps at midnight. + //Steps counters by day will be OK with this + + List samples = provider.getActivitySamples(record.timestamp - 3600 * 24 + 1, record.timestamp); + + int missingDistance = record.distance; + int missingSteps = record.steps; + + for(HPlusHealthActivitySample sample : samples){ + if(sample.getSteps() > 0) { + missingSteps -= sample.getSteps(); + } + if(sample.getDistance() > 0){ + missingDistance -= sample.getDistance(); + } + } + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( record.timestamp, // ts deviceId, userId, // User id record.getRawData(), // Raw Data ActivityKind.TYPE_UNKNOWN, - ActivitySample.NOT_MEASURED, // Intensity - record.steps, // Steps + 0, // Intensity + Math.max( missingSteps, 0), // Steps ActivitySample.NOT_MEASURED, // HR - record.distance, // Distance + Math.max( missingDistance, 0), // Distance ActivitySample.NOT_MEASURED // Calories ); + sample.setProvider(provider); provider.addGBActivitySample(sample); } catch (GBException ex) { @@ -426,11 +429,11 @@ public class HPlusHandlerThread extends GBDeviceIoThread { } catch (Exception ex) { LOG.debug(ex.getMessage()); } + + return true; } public boolean processVersion(byte[] data) { - LOG.debug("Process Version"); - int major = data[2] & 0xFF; int minor = data[1] & 0xFF; @@ -440,14 +443,4 @@ public class HPlusHandlerThread extends GBDeviceIoThread { return true; } - - public static HPlusHealthActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { - HPlusHealthActivitySample sample = new HPlusHealthActivitySample(); - sample.setDevice(device); - sample.setUser(user); - sample.setTimestamp(timestampInSeconds); - sample.setProvider(provider); - - return sample; - } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 65350305..1deda41d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -4,7 +4,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; * @author João Paulo Barraca <jpbarraca@gmail.com> */ -import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.BroadcastReceiver; @@ -23,7 +22,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -35,10 +33,10 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; -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.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -49,9 +47,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { public BluetoothGattCharacteristic ctrlCharacteristic = null; public BluetoothGattCharacteristic measureCharacteristic = null; - private int[] lastDataStats = null; - - private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private HPlusHandlerThread syncHelper; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -82,10 +77,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { LOG.debug("Dispose"); LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); broadcastManager.unregisterReceiver(mReceiver); - super.dispose(); - if(syncHelper != null) - syncHelper.quit(); + close(); + + super.dispose(); } @Override @@ -112,15 +107,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { setInitialized(builder); - syncHelper.start(); builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); builder.setGattCallback(this); builder.notify(measureCharacteristic, true); - //LOG.debug("Initialization Done"); - return builder; } @@ -135,8 +127,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport syncPreferences(TransactionBuilder transaction) { - LOG.info("Attempting to sync preferences with: " + getDevice().getAddress()); - byte gender = HPlusCoordinator.getUserGender(getDevice().getAddress()); byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); @@ -189,8 +179,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setLanguage(TransactionBuilder transaction) { - LOG.info("Attempting to set language..."); - byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_LANGUAGE, @@ -201,8 +189,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setTimeMode(TransactionBuilder transaction) { - LOG.info("Attempting to set Time Mode..."); - byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_TIMEMODE, @@ -212,9 +198,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setUnit(TransactionBuilder transaction) { - LOG.info("Attempting to set Units..."); - - byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_UNITS, @@ -224,8 +207,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setCurrentDate(TransactionBuilder transaction) { - LOG.info("Attempting to set Current Date..."); - Calendar c = Calendar.getInstance(); int year = c.get(Calendar.YEAR) - 1900; int month = c.get(Calendar.MONTH); @@ -243,8 +224,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setCurrentTime(TransactionBuilder transaction) { - LOG.info("Attempting to set Current Time..."); - Calendar c = Calendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ @@ -259,8 +238,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setDayOfWeek(TransactionBuilder transaction) { - LOG.info("Attempting to set Day Of Week..."); - Calendar c = Calendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ @@ -272,8 +249,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setSIT(TransactionBuilder transaction) { - LOG.info("Attempting to set SIT..."); - int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); @@ -291,7 +266,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { (byte) (now.get(Calendar.YEAR) % 256), (byte) (now.get(Calendar.MONTH) + 1), (byte) (now.get(Calendar.DAY_OF_MONTH)), - (byte) (now.get(Calendar.HOUR)), + (byte) (now.get(Calendar.HOUR_OF_DAY)), (byte) (now.get(Calendar.MINUTE)), (byte) (now.get(Calendar.SECOND)), 0, @@ -304,8 +279,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setWeight(TransactionBuilder transaction) { - LOG.info("Attempting to set Weight..."); - byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_WEIGHT, @@ -316,8 +289,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setHeight(TransactionBuilder transaction) { - LOG.info("Attempting to set Height..."); - byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_HEIGHT, @@ -329,8 +300,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAge(TransactionBuilder transaction) { - LOG.info("Attempting to set Age..."); - byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_AGE, @@ -341,8 +310,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setGender(TransactionBuilder transaction) { - LOG.info("Attempting to set Gender..."); - byte value = HPlusCoordinator.getUserGender(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_GENDER, @@ -354,8 +321,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setGoal(TransactionBuilder transaction) { - LOG.info("Attempting to set Sex..."); - int value = HPlusCoordinator.getGoal(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_GOAL, @@ -368,8 +333,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setScreenTime(TransactionBuilder transaction) { - LOG.info("Attempting to set Screentime..."); - byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_SCREENTIME, @@ -392,28 +355,35 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } - private HPlusSupport setAlarm(TransactionBuilder transaction) { - LOG.info("Attempting to set Alarm..."); - //TODO: Find how to set alarms + private HPlusSupport setAlarm(TransactionBuilder transaction, Calendar t) { + + transaction.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALARM, + (byte) (t.get(Calendar.YEAR) / 256), + (byte) (t.get(Calendar.YEAR) % 256), + (byte) (t.get(Calendar.MONTH) + 1), + (byte) t.get(Calendar.HOUR_OF_DAY), + (byte) t.get(Calendar.MINUTE), + (byte) t.get(Calendar.SECOND)}); + return this; } - private HPlusSupport setBlood(TransactionBuilder transaction) { - LOG.info("Attempting to set Blood..."); - //TODO: Find what blood means for the band - return this; - } - - - private HPlusSupport setFindMe(TransactionBuilder transaction) { - LOG.info("Attempting to set Findme..."); + private HPlusSupport setFindMe(TransactionBuilder transaction, boolean state) { //TODO: Find how this works + + byte[] msg = new byte[2]; + msg[0] = HPlusConstants.CMD_SET_FINDME; + + if (state) + msg[1] = HPlusConstants.ARG_FINDME_ON; + else + msg[1] = HPlusConstants.ARG_FINDME_OFF; + + transaction.write(ctrlCharacteristic, msg); return this; } private HPlusSupport requestDeviceInfo(TransactionBuilder builder) { - LOG.debug("Requesting Device Info!"); - // HPlus devices seem to report some information in an alternative manner builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); @@ -433,31 +403,50 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void pair() { + LOG.debug("Pair"); } - private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { + private void handleDeviceInfo(DeviceInfo info) { LOG.warn("Device info: " + info); } @Override public void onNotification(NotificationSpec notificationSpec) { - LOG.debug("Got Notification"); - //TODO: Show different notifications acccording to source as Band supports this + //TODO: Show different notifications according to source as Band supports this showText(notificationSpec.title, notificationSpec.body); } - @Override public void onSetTime() { TransactionBuilder builder = new TransactionBuilder("time"); setCurrentDate(builder); setCurrentTime(builder); + + builder.queue(getQueue()); } @Override public void onSetAlarms(ArrayList alarms) { + if (alarms.size() == 0) + return; + + for (Alarm alarm : alarms) { + + if (!alarm.isEnabled()) + continue; + + if (alarm.isSmartWakeup()) //Not available + continue; + + Calendar t = alarm.getAlarmCal(); + TransactionBuilder builder = new TransactionBuilder("alarm"); + setAlarm(builder, t); + builder.queue(getQueue()); + + return; //Only first alarm + } } @@ -524,19 +513,22 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onFetchActivityData() { - if(syncHelper != null) + if (syncHelper != null) syncHelper.sync(); } @Override public void onReboot() { + getQueue().clear(); + + TransactionBuilder builder = new TransactionBuilder("Shutdown"); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SHUTDOWN, HPlusConstants.ARG_SHUTDOWN_EN}); + builder.queue(getQueue()); } @Override public void onHeartRateTest() { - LOG.debug("On HeartRateTest"); - getQueue().clear(); TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); @@ -547,8 +539,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onEnableRealtimeHeartRateMeasurement(boolean enable) { - LOG.debug("Set Real Time HR Measurement: " + enable); - getQueue().clear(); TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); @@ -561,35 +551,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state}); builder.queue(getQueue()); + } @Override public void onFindDevice(boolean start) { - LOG.debug("Find Me"); - try { TransactionBuilder builder = performInitialized("findMe"); - byte[] msg = new byte[2]; - msg[0] = HPlusConstants.CMD_SET_FINDME; - - if (start) - msg[1] = HPlusConstants.ARG_FINDME_ON; - else - msg[1] = HPlusConstants.ARG_FINDME_OFF; - - builder.write(ctrlCharacteristic, msg); + setFindMe(builder, start); builder.queue(getQueue()); } catch (IOException e) { - GB.toast(getContext(), "Error toogling Find Me: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + GB.toast(getContext(), "Error toggling Find Me: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); } } @Override public void onSetConstantVibration(int intensity) { - LOG.debug("Vibration Trigger"); - getQueue().clear(); try { @@ -615,6 +594,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onEnableHeartRateSleepSupport(boolean enable) { + onEnableRealtimeHeartRateMeasurement(enable); } @@ -641,8 +621,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private void showIncomingCall(String name, String number) { - LOG.debug("Show Incoming Call"); - try { TransactionBuilder builder = performInitialized("incomingCallIcon"); @@ -652,7 +630,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //Show Call Icon builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, HPlusConstants.ARG_INCOMING_CALL}); - //builder = performInitialized("incomingCallText"); builder.queue(getQueue()); //TODO: Use WaitAction @@ -714,18 +691,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } } - private void showText(String message) { - - showText(null, message); - } - private void showText(String title, String body) { - LOG.debug("Show Notification"); - try { TransactionBuilder builder = performInitialized("notification"); - byte[] msg = new byte[20]; for (int i = 0; i < msg.length; i++) msg[i] = ' '; @@ -790,11 +759,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } } - public boolean isExpectedDevice(BluetoothDevice device) { - return true; - } - - public void close() { + private void close() { + if (syncHelper != null) { + syncHelper.quit(); + syncHelper = null; + } } @Override @@ -811,29 +780,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { switch (data[0]) { case HPlusConstants.DATA_VERSION: - syncHelper.processVersion(data); - case HPlusConstants.DATA_STATS: { - syncHelper.processRealtimeStats(data); - return true; - } - case HPlusConstants.DATA_SLEEP: { - syncHelper.processIncomingSleepData(data); - return true; - } - case HPlusConstants.DATA_STEPS:{ - syncHelper.processStepStats(data); - return true; - } + return syncHelper.processVersion(data); + + case HPlusConstants.DATA_STATS: + return syncHelper.processRealtimeStats(data); + + case HPlusConstants.DATA_SLEEP: + return syncHelper.processIncomingSleepData(data); + + case HPlusConstants.DATA_STEPS: + return syncHelper.processStepStats(data); + case HPlusConstants.DATA_DAY_SUMMARY: case HPlusConstants.DATA_DAY_SUMMARY_ALT: - syncHelper.processIncomingDaySlotData(data); - return true; + return syncHelper.processIncomingDaySlotData(data); + default: - LOG.info("Unhandled characteristic changed: " + characteristicUUID); - + LOG.debug("Unhandled characteristic changed: " + characteristicUUID); + return true; } - return false; } - - } From 91b346b23de79089d73f7c6e950c7a3c73ff56b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 4 Jan 2017 23:24:17 +0000 Subject: [PATCH 095/244] HPlus: Enabled decoding of additional fields in day summary message --- .../devices/hplus/HPlusDataRecordSteps.java | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java index 33901ddd..a1bd3e4e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java @@ -6,33 +6,44 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; import java.util.Calendar; +import java.util.Locale; class HPlusDataRecordSteps extends HPlusDataRecord{ + public int year; + public int month; + public int day; + public int steps; public int distance; + public int activeTime; + public int maxHeartRate; + public int minHeartRate; + public int calories; + HPlusDataRecordSteps(byte[] data) { super(data); - int year = (data[10] & 0xFF) * 256 + (data[9] & 0xFF); - int month = data[11] & 0xFF; - int day = data[12] & 0xFF; + year = (data[10] & 0xFF) * 256 + (data[9] & 0xFF); + month = data[11] & 0xFF; + day = data[12] & 0xFF; + + //Recover from bug in firmware where year is corrupted + if(year < 1900) + year += 1900; if (year < 2000 || month > 12 || day > 31) { throw new IllegalArgumentException("Invalid record date "+year+"-"+month+"-"+day); } steps = (data[2] & 0xFF) * 256 + (data[1] & 0xFF); distance = (data[4] & 0xFF) * 256 + (data[3] & 0xFF); + activeTime = (data[14] & 0xFF) * 256 + (data[13] & 0xFF); + calories = (data[6] & 0xFF) * 256 + (data[5] & 0xFF); + calories += (data[8] & 0xFF) * 256 + (data[7] & 0xFF); - /* - unknown fields - short s12 = (short)(data[5] + data[6] * 256); - short s13 = (short)(data[7] + data[8] * 256); - short s16 = (short)(data[13]) + data[14] * 256); - short s17 = data[15]; - short s18 = data[16]; - */ + maxHeartRate = data[15] & 0xFF; + minHeartRate = data[16] & 0xFF; Calendar date = Calendar.getInstance(); date.set(Calendar.YEAR, year); @@ -45,4 +56,8 @@ class HPlusDataRecordSteps extends HPlusDataRecord{ timestamp = (int) (date.getTimeInMillis() / 1000); } + + public String toString(){ + return String.format(Locale.US, "%s-%s-%s steps:%d distance:%d minHR:%d maxHR:%d calories:%d activeTime:%d", year, month, day, steps, distance,minHeartRate, maxHeartRate, calories, activeTime); + } } \ No newline at end of file From 510427e30be1f1ca436132b038dc35ae508effcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 4 Jan 2017 23:41:35 +0000 Subject: [PATCH 096/244] HPlus: Refactoring. Calendar -> GregorianCalendar --- .../devices/hplus/HPlusDataRecordDay.java | 5 ++- .../hplus/HPlusDataRecordRealtime.java | 4 +- .../devices/hplus/HPlusDataRecordSleep.java | 6 +-- .../devices/hplus/HPlusDataRecordSteps.java | 3 +- .../devices/hplus/HPlusHandlerThread.java | 41 +++++++++---------- .../service/devices/hplus/HPlusSupport.java | 9 ++-- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java index b0481101..c6a8603f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java @@ -4,7 +4,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; * @author João Paulo Barraca <jpbarraca@gmail.com> */ -import java.util.Calendar; +import java.util.GregorianCalendar; + import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -33,7 +34,7 @@ public class HPlusDataRecordDay extends HPlusDataRecord { //?? data[6]; secondsInactive = data[7] & 0xFF; // ? - int now = (int) (Calendar.getInstance().getTimeInMillis() / (3600 * 24 * 1000L)); + int now = (int) (GregorianCalendar.getInstance().getTimeInMillis() / (3600 * 24 * 1000L)); timestamp = now * 3600 * 24 + (slot / 6 * 3600 + slot % 6 * 10); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java index 4c9eb9ee..8a3750ec 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java @@ -5,7 +5,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; */ -import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -26,7 +26,7 @@ class HPlusDataRecordRealtime extends HPlusDataRecord { throw new IllegalArgumentException("Invalid data packet"); } - timestamp = (int) (Calendar.getInstance().getTimeInMillis() / 1000); + timestamp = (int) (GregorianCalendar.getInstance().getTimeInMillis() / 1000); distance = 10 * ((data[4] & 0xFF) * 256 + (data[3] & 0xFF)); // meters int x = (data[6] & 0xFF) * 256 + data[5] & 0xFF; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java index 9a898f6f..b7dbdd42 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java @@ -7,6 +7,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; import java.util.ArrayList; import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -47,7 +48,7 @@ public class HPlusDataRecordSleep extends HPlusDataRecord { int hour = data[17] & 0xFF; int minute = data[18] & 0xFF; - Calendar sleepStart = Calendar.getInstance(); + Calendar sleepStart = GregorianCalendar.getInstance(); sleepStart.set(Calendar.YEAR, year); sleepStart.set(Calendar.MONTH, month - 1); sleepStart.set(Calendar.DAY_OF_MONTH, day); @@ -61,9 +62,6 @@ public class HPlusDataRecordSleep extends HPlusDataRecord { lightSleepMinutes = enterSleepMinutes + spindleMinutes + remSleepMinutes; timestamp = bedTimeStart; - - Calendar sleepEnd = Calendar.getInstance(); - sleepEnd.setTimeInMillis(bedTimeEnd * 1000L); } public List getIntervals() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java index a1bd3e4e..997a09ed 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java @@ -6,6 +6,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.Locale; @@ -45,7 +46,7 @@ class HPlusDataRecordSteps extends HPlusDataRecord{ maxHeartRate = data[15] & 0xFF; minHeartRate = data[16] & 0xFF; - Calendar date = Calendar.getInstance(); + Calendar date = GregorianCalendar.getInstance(); date.set(Calendar.YEAR, year); date.set(Calendar.MONTH, month - 1); date.set(Calendar.DAY_OF_MONTH, day); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index fe8ebffc..fcb7f6a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -46,17 +47,15 @@ class HPlusHandlerThread extends GBDeviceIoThread { private int mLastSlotReceived = 0; private int mLastSlotRequested = 0; - private Calendar mLastSleepDayReceived = Calendar.getInstance(); - - private Calendar mHelloTime = Calendar.getInstance(); - - private Calendar mGetDaySlotsTime = Calendar.getInstance(); - private Calendar mGetSleepTime = Calendar.getInstance(); - - private final Object waitObject = new Object(); + private Calendar mLastSleepDayReceived = GregorianCalendar.getInstance(); + private Calendar mHelloTime = GregorianCalendar.getInstance(); + private Calendar mGetDaySlotsTime = GregorianCalendar.getInstance(); + private Calendar mGetSleepTime = GregorianCalendar.getInstance(); private HPlusDataRecordRealtime prevRealTimeRecord = null; + private final Object waitObject = new Object(); + public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) { super(gbDevice, context); @@ -92,7 +91,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { break; } - Calendar now = Calendar.getInstance(); + Calendar now = GregorianCalendar.getInstance(); if (now.compareTo(mHelloTime) > 0) { sendHello(); @@ -106,7 +105,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { requestNextSleepData(); } - now = Calendar.getInstance(); + now = GregorianCalendar.getInstance(); waitTime = Math.min(Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()), mHelloTime.getTimeInMillis()) - now.getTimeInMillis(); } @@ -151,7 +150,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { } public void scheduleHello(){ - mHelloTime = Calendar.getInstance(); + mHelloTime = GregorianCalendar.getInstance(); mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL); } @@ -211,8 +210,8 @@ class HPlusHandlerThread extends GBDeviceIoThread { byte nextMinute = 0; - if (nextHour == (byte) Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) { - nextMinute = (byte) Calendar.getInstance().get(Calendar.MINUTE); + if (nextHour == (byte) GregorianCalendar.getInstance().get(Calendar.HOUR_OF_DAY)) { + nextMinute = (byte) GregorianCalendar.getInstance().get(Calendar.MINUTE); } byte minute = (byte) ((mLastSlotReceived % 6) * 10); @@ -223,15 +222,15 @@ class HPlusHandlerThread extends GBDeviceIoThread { LOG.debug("Reached End of the Day"); mLastSlotRequested = 0; mLastSlotReceived = 0; - mGetDaySlotsTime = Calendar.getInstance(); + mGetDaySlotsTime = GregorianCalendar.getInstance(); mGetDaySlotsTime.add(Calendar.SECOND, SYNC_PERIOD); return; } - if (nextHour > Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) { + if (nextHour > GregorianCalendar.getInstance().get(GregorianCalendar.HOUR_OF_DAY)) { LOG.debug("Day data is up to date"); - mGetDaySlotsTime = Calendar.getInstance(); + mGetDaySlotsTime = GregorianCalendar.getInstance(); mGetDaySlotsTime.add(Calendar.SECOND, SYNC_PERIOD); return; } @@ -243,7 +242,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { builder.write(mHPlusSupport.ctrlCharacteristic, msg); builder.queue(mHPlusSupport.getQueue()); - mGetDaySlotsTime = Calendar.getInstance(); + mGetDaySlotsTime = GregorianCalendar.getInstance(); mGetDaySlotsTime.add(Calendar.SECOND, SYNC_RETRY_PERIOD); } @@ -298,15 +297,15 @@ class HPlusHandlerThread extends GBDeviceIoThread { LOG.debug(ex.getMessage()); } - mGetSleepTime = Calendar.getInstance(); - mGetSleepTime.add(Calendar.SECOND, SLEEP_SYNC_PERIOD); + mGetSleepTime = GregorianCalendar.getInstance(); + mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_SYNC_PERIOD); return true; } private void requestNextSleepData() { - mGetSleepTime = Calendar.getInstance(); - mGetSleepTime.add(Calendar.SECOND, SLEEP_RETRY_PERIOD); + mGetSleepTime = GregorianCalendar.getInstance(); + mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_RETRY_PERIOD); TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 1deda41d..54fd18c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; @@ -207,7 +208,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setCurrentDate(TransactionBuilder transaction) { - Calendar c = Calendar.getInstance(); + Calendar c = GregorianGregorianCalendar.getInstance(); int year = c.get(Calendar.YEAR) - 1900; int month = c.get(Calendar.MONTH); int day = c.get(Calendar.DAY_OF_MONTH); @@ -224,7 +225,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setCurrentTime(TransactionBuilder transaction) { - Calendar c = Calendar.getInstance(); + Calendar c = GregorianCalendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_TIME, @@ -238,7 +239,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setDayOfWeek(TransactionBuilder transaction) { - Calendar c = Calendar.getInstance(); + Calendar c = GregorianCalendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_WEEK, @@ -252,7 +253,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); - Calendar now = Calendar.getInstance(); + Calendar now = GregorianCalendar.getInstance(); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_SIT_INTERVAL, From ae0718c398529fda1c70f14211da6e81c406563d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Thu, 5 Jan 2017 00:03:54 +0000 Subject: [PATCH 097/244] HPlus: Refactoring and added comments to the message decoders members --- .../devices/hplus/HPlusDataRecord.java | 18 ++++++++ ...rdDay.java => HPlusDataRecordDaySlot.java} | 28 ++++++++++--- .../hplus/HPlusDataRecordRealtime.java | 25 +++++++++++ .../devices/hplus/HPlusDataRecordSleep.java | 42 +++++++++++++++++++ .../devices/hplus/HPlusDataRecordSteps.java | 37 ++++++++++++++++ .../devices/hplus/HPlusHandlerThread.java | 4 +- 6 files changed, 147 insertions(+), 7 deletions(-) rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/{HPlusDataRecordDay.java => HPlusDataRecordDaySlot.java} (59%) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java index 7e854d68..d2c96471 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java @@ -11,7 +11,14 @@ public class HPlusDataRecord { public final static int TYPE_SLEEP = 1; public int activityKind = ActivityKind.TYPE_UNKNOWN; + /** + * Time of this record in seconds + */ public int timestamp; + + /** + * Raw data as sent from the device + */ public byte[] rawData; public HPlusDataRecord(byte[] data){ @@ -24,8 +31,19 @@ public class HPlusDataRecord { } public class RecordInterval { + /** + * Start time of this interval in seconds + */ public int timestampFrom; + + /** + * End time of this interval in seconds + */ public int timestampTo; + + /** + * Type of activity {@link ActivityKind} + */ public int activityKind; RecordInterval(int timestampFrom, int timestampTo, int activityKind) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java similarity index 59% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java index c6a8603f..3e3b9ad1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDay.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java @@ -9,13 +9,31 @@ import java.util.GregorianCalendar; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class HPlusDataRecordDay extends HPlusDataRecord { +public class HPlusDataRecordDaySlot extends HPlusDataRecord { + + /** + * The device reports data aggregated in slots. + * There are 144 slots in a given day, summarizing 10 minutes of data + * Integer with the slot number from 0 to 143 + */ public int slot; + + /** + * Number of steps + */ public int steps; + + /** + * Number of seconds without activity (TBC) + */ public int secondsInactive; + + /** + * Average Heart Rate in Beats Per Minute + */ public int heartRate; - public HPlusDataRecordDay(byte[] data) { + public HPlusDataRecordDaySlot(byte[] data) { super(data); int a = (data[4] & 0xFF) * 256 + (data[5] & 0xFF); @@ -23,13 +41,13 @@ public class HPlusDataRecordDay extends HPlusDataRecord { throw new IllegalArgumentException("Invalid Slot Number"); } - slot = a; // 10 minute slots as an offset from 0:00 AM - heartRate = data[1] & 0xFF; //Average Heart Rate ? + slot = a; + heartRate = data[1] & 0xFF; if(heartRate == 255) heartRate = ActivityKind.TYPE_NOT_MEASURED; - steps = (data[2] & 0xFF) * 256 + data[3] & 0xFF; // Steps in this period + steps = (data[2] & 0xFF) * 256 + data[3] & 0xFF; //?? data[6]; secondsInactive = data[7] & 0xFF; // ? diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java index 8a3750ec..d827976d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java @@ -12,11 +12,36 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; class HPlusDataRecordRealtime extends HPlusDataRecord { + + /** + * Distance accumulated during the day in meters + */ public int distance; + + /** + * Calories consumed during the day in KCalories + */ public int calories; + + /** + * Instantaneous Heart Rate measured in Beats Per Minute + */ public int heartRate; + + /** + * Battery level from 0 to 100 + */ public byte battery; + + /** + * Time active (To be determined how it works) + */ public int activeTime; + + /** + * Computing intensity + * To be calculated appropriately + */ public int intensity; public HPlusDataRecordRealtime(byte[] data) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java index b7dbdd42..97ea0294 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java @@ -14,14 +14,56 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class HPlusDataRecordSleep extends HPlusDataRecord { + /** + * Time which the device determined to be the bed time in seconds + */ public int bedTimeStart; + + /** + * Time which the device determined to be the end of this sleep period in seconds + */ public int bedTimeEnd; + + /** + * Number of minutes in Deep Sleep + */ public int deepSleepMinutes; + + /** + * Number of minutes in Light Sleep + * This is considered as Light Sleep + + */ public int lightSleepMinutes; + + /** + * Number of minutes to start sleeping (??) + * This is considered as Light Sleep + */ public int enterSleepMinutes; + + /** + * Number of minutes with Sleep Spindles (??) + * This is considered as Light Sleep + */ public int spindleMinutes; + + /** + * Number of minutes in REM sleep + * This is considered as Light Sleep + + */ public int remSleepMinutes; + + /** + * Number of wake up minutes during the sleep period + * This is not considered as a sleep activity + */ public int wakeupMinutes; + + /** + * Number of times the user woke up + */ public int wakeupCount; public HPlusDataRecordSleep(byte[] data) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java index 997a09ed..0d8e6c34 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java @@ -11,16 +11,52 @@ import java.util.Locale; class HPlusDataRecordSteps extends HPlusDataRecord{ + + /** + * Year of the record reported by the device + * Sometimes the device will report a low number (e.g, 116) which will be "corrected" + * by adding 1900 + */ public int year; + + /** + * Month of the record reported by the device from 1 to 12 + */ public int month; + + /** + * Day of the record reported by the device + */ public int day; + /** + * Number of steps accumulated in the day reported + */ public int steps; + + /** + * Distance in meters accumulated in the day reported + */ public int distance; + /** + * Amount of time active in the day (Units are To Be Determined) + */ public int activeTime; + + /** + * Max Heart Rate recorded in Beats Per Minute + */ public int maxHeartRate; + + /** + * Min Heart Rate recorded in Beats Per Minute + */ public int minHeartRate; + + /** + * Amount of estimated calories consumed during the day in KCalories + */ public int calories; HPlusDataRecordSteps(byte[] data) { @@ -31,6 +67,7 @@ class HPlusDataRecordSteps extends HPlusDataRecord{ day = data[12] & 0xFF; //Recover from bug in firmware where year is corrupted + //data[10] will be set to 0, effectively offsetting values by minus 1900 years if(year < 1900) year += 1900; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index fcb7f6a4..5f960392 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -156,9 +156,9 @@ class HPlusHandlerThread extends GBDeviceIoThread { public boolean processIncomingDaySlotData(byte[] data) { - HPlusDataRecordDay record; + HPlusDataRecordDaySlot record; try{ - record = new HPlusDataRecordDay(data); + record = new HPlusDataRecordDaySlot(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); return true; From 051d1e739094bddde285f0c306bc9f230be39005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Thu, 5 Jan 2017 00:17:28 +0000 Subject: [PATCH 098/244] HPlus: fix typo --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 54fd18c5..da76d7c9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -208,7 +208,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setCurrentDate(TransactionBuilder transaction) { - Calendar c = GregorianGregorianCalendar.getInstance(); + Calendar c = GregorianCalendar.getInstance(); int year = c.get(Calendar.YEAR) - 1900; int month = c.get(Calendar.MONTH); int day = c.get(Calendar.DAY_OF_MONTH); From 93ae57bd605aa5b738a59469d0651dabfae882a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Thu, 5 Jan 2017 10:33:37 +0000 Subject: [PATCH 099/244] Refactoring class HPlusDataRecordDaySummary --- ...ps.java => HPlusDataRecordDaySummary.java} | 4 +- .../devices/hplus/HPlusHandlerThread.java | 40 +++++++++++++------ 2 files changed, 29 insertions(+), 15 deletions(-) rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/{HPlusDataRecordSteps.java => HPlusDataRecordDaySummary.java} (96%) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java similarity index 96% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java index 0d8e6c34..0bb6609c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java @@ -10,7 +10,7 @@ import java.util.GregorianCalendar; import java.util.Locale; -class HPlusDataRecordSteps extends HPlusDataRecord{ +class HPlusDataRecordDaySummary extends HPlusDataRecord{ /** * Year of the record reported by the device @@ -59,7 +59,7 @@ class HPlusDataRecordSteps extends HPlusDataRecord{ */ public int calories; - HPlusDataRecordSteps(byte[] data) { + HPlusDataRecordDaySummary(byte[] data) { super(data); year = (data[10] & 0xFF) * 256 + (data[9] & 0xFF); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index 5f960392..caf9435f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -35,10 +35,12 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; class HPlusHandlerThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(HPlusHandlerThread.class); - private int SYNC_PERIOD = 60 * 10; - private int SYNC_RETRY_PERIOD = 6; + private int CURRENT_DAY_SYNC_PERIOD = 60 * 10; + private int CURRENT_DAY_SYNC_RETRY_PERIOD = 6; private int SLEEP_SYNC_PERIOD = 12 * 60 * 60; - private int SLEEP_RETRY_PERIOD = 30; + private int SLEEP_SYNC_RETRY_PERIOD = 30; + + private int DAY_SUMMARY_SYNC_PERIOD = 24 * 60 * 60; private int HELLO_INTERVAL = 60; @@ -51,6 +53,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { private Calendar mHelloTime = GregorianCalendar.getInstance(); private Calendar mGetDaySlotsTime = GregorianCalendar.getInstance(); private Calendar mGetSleepTime = GregorianCalendar.getInstance(); + private Calendar mGetDaySummaryTime = GregorianCalendar.getInstance(); private HPlusDataRecordRealtime prevRealTimeRecord = null; @@ -62,10 +65,6 @@ class HPlusHandlerThread extends GBDeviceIoThread { mQuit = false; mHPlusSupport = hplusSupport; - - mLastSleepDayReceived.setTimeInMillis(0); - mGetSleepTime.setTimeInMillis(0); - mGetDaySlotsTime.setTimeInMillis(0); } @@ -105,6 +104,10 @@ class HPlusHandlerThread extends GBDeviceIoThread { requestNextSleepData(); } + if(now.compareTo(mGetDaySummaryTime) > 0) { + requestDaySummaryData(); + } + now = GregorianCalendar.getInstance(); waitTime = Math.min(Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()), mHelloTime.getTimeInMillis()) - now.getTimeInMillis(); } @@ -122,6 +125,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { public void sync() { mGetSleepTime.setTimeInMillis(0); mGetDaySlotsTime.setTimeInMillis(0); + mGetDaySummaryTime.setTimeInMillis(0); TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); @@ -154,6 +158,14 @@ class HPlusHandlerThread extends GBDeviceIoThread { mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL); } + public void requestDaySummaryData(){ + TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary"); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); + builder.queue(mHPlusSupport.getQueue()); + + mGetDaySummaryTime.add(Calendar.SECOND, DAY_SUMMARY_SYNC_PERIOD); + } + public boolean processIncomingDaySlotData(byte[] data) { HPlusDataRecordDaySlot record; @@ -223,7 +235,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { mLastSlotRequested = 0; mLastSlotReceived = 0; mGetDaySlotsTime = GregorianCalendar.getInstance(); - mGetDaySlotsTime.add(Calendar.SECOND, SYNC_PERIOD); + mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); return; } @@ -231,7 +243,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { if (nextHour > GregorianCalendar.getInstance().get(GregorianCalendar.HOUR_OF_DAY)) { LOG.debug("Day data is up to date"); mGetDaySlotsTime = GregorianCalendar.getInstance(); - mGetDaySlotsTime.add(Calendar.SECOND, SYNC_PERIOD); + mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); return; } @@ -243,7 +255,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { builder.queue(mHPlusSupport.getQueue()); mGetDaySlotsTime = GregorianCalendar.getInstance(); - mGetDaySlotsTime.add(Calendar.SECOND, SYNC_RETRY_PERIOD); + mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_RETRY_PERIOD); } public boolean processIncomingSleepData(byte[] data){ @@ -305,7 +317,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { private void requestNextSleepData() { mGetSleepTime = GregorianCalendar.getInstance(); - mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_RETRY_PERIOD); + mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_SYNC_RETRY_PERIOD); TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); @@ -373,14 +385,16 @@ class HPlusHandlerThread extends GBDeviceIoThread { public boolean processStepStats(byte[] data) { - HPlusDataRecordSteps record; + LOG.debug("Process Step Stats"); + HPlusDataRecordDaySummary record; try{ - record = new HPlusDataRecordSteps(data); + record = new HPlusDataRecordDaySummary(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); return true; } + LOG.debug("Received: " + record); try (DBHandler dbHandler = GBApplication.acquireDB()) { HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); From d491921b1c89b899e0d588c7ca56a4147fd917fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Thu, 5 Jan 2017 10:36:22 +0000 Subject: [PATCH 100/244] HPlus: Rename HPlusHandlerThread method --- .../service/devices/hplus/HPlusHandlerThread.java | 4 ++-- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index caf9435f..e564b4b1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -384,8 +384,8 @@ class HPlusHandlerThread extends GBDeviceIoThread { } - public boolean processStepStats(byte[] data) { - LOG.debug("Process Step Stats"); + public boolean processDaySummary(byte[] data) { + LOG.debug("Process Day Summary"); HPlusDataRecordDaySummary record; try{ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index da76d7c9..17e9e666 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -790,7 +790,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return syncHelper.processIncomingSleepData(data); case HPlusConstants.DATA_STEPS: - return syncHelper.processStepStats(data); + return syncHelper.processDaySummary(data); case HPlusConstants.DATA_DAY_SUMMARY: case HPlusConstants.DATA_DAY_SUMMARY_ALT: From ade7161c4d21fbabe82298e380c6b88ab323d7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Thu, 5 Jan 2017 14:03:49 +0000 Subject: [PATCH 101/244] HPlus: Buffer Day Slot data before commit to DB --- .../devices/hplus/HPlusDataRecordDaySlot.java | 4 + .../devices/hplus/HPlusHandlerThread.java | 129 ++++++++++-------- 2 files changed, 75 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java index 3e3b9ad1..5f9e310d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java @@ -5,6 +5,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; */ import java.util.GregorianCalendar; +import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -56,4 +57,7 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord { timestamp = now * 3600 * 24 + (slot / 6 * 3600 + slot % 6 * 10); } + public String toString(){ + return String.format(Locale.US, "Slot: %d, Steps: %d, InactiveSeconds: %d, HeartRate: %d", slot, steps, secondsInactive, heartRate); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index e564b4b1..c40abfae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -36,7 +36,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(HPlusHandlerThread.class); private int CURRENT_DAY_SYNC_PERIOD = 60 * 10; - private int CURRENT_DAY_SYNC_RETRY_PERIOD = 6; + private int CURRENT_DAY_SYNC_RETRY_PERIOD = 10; private int SLEEP_SYNC_PERIOD = 12 * 60 * 60; private int SLEEP_SYNC_RETRY_PERIOD = 30; @@ -48,6 +48,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { private HPlusSupport mHPlusSupport; private int mLastSlotReceived = 0; private int mLastSlotRequested = 0; + private int mSlotsToRequest = 6; private Calendar mLastSleepDayReceived = GregorianCalendar.getInstance(); private Calendar mHelloTime = GregorianCalendar.getInstance(); @@ -58,6 +59,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { private HPlusDataRecordRealtime prevRealTimeRecord = null; private final Object waitObject = new Object(); + List mDaySlotSamples = new ArrayList<>(); public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) { super(gbDevice, context); @@ -163,6 +165,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); builder.queue(mHPlusSupport.getQueue()); + mGetDaySummaryTime = GregorianCalendar.getInstance(); mGetDaySummaryTime.add(Calendar.SECOND, DAY_SUMMARY_SYNC_PERIOD); } @@ -175,87 +178,97 @@ class HPlusHandlerThread extends GBDeviceIoThread { LOG.debug((e.getMessage())); return true; } + mLastSlotReceived = record.slot; - if ((record.slot == 0 && mLastSlotReceived == 0) || (record.slot == mLastSlotReceived + 1)) { - mLastSlotReceived = record.slot; + try (DBHandler dbHandler = GBApplication.acquireDB()) { + HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); - try (DBHandler dbHandler = GBApplication.acquireDB()) { - HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + record.timestamp, // ts + deviceId, userId, // User id + record.getRawData(), // Raw Data + ActivityKind.TYPE_UNKNOWN, + 0, // Intensity + record.steps, // Steps + record.heartRate, // HR + ActivitySample.NOT_MEASURED, // Distance + ActivitySample.NOT_MEASURED // Calories + ); + sample.setProvider(provider); + mDaySlotSamples.add(sample); - Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); - Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); - HPlusHealthActivitySample sample = new HPlusHealthActivitySample( - record.timestamp, // ts - deviceId, userId, // User id - record.getRawData(), // Raw Data - ActivityKind.TYPE_UNKNOWN, - 0, // Intensity - record.steps, // Steps - record.heartRate, // HR - ActivitySample.NOT_MEASURED, // Distance - ActivitySample.NOT_MEASURED // Calories - ); - sample.setProvider(provider); + Calendar now = GregorianCalendar.getInstance(); - provider.addGBActivitySample(sample); + //Dump buffered samples to DB + if ((record.timestamp + (60*100) >= (now.getTimeInMillis() / 1000L) )) { - } catch (GBException ex) { - LOG.debug((ex.getMessage())); - } catch (Exception ex) { - LOG.debug(ex.getMessage()); + provider.getSampleDao().insertOrReplaceInTx(mDaySlotSamples); + + mGetDaySlotsTime.setTimeInMillis(0); + mDaySlotSamples.clear(); + mSlotsToRequest = 144 - mLastSlotReceived; } + } catch (GBException ex) { + LOG.debug((ex.getMessage())); + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } - if (record.slot >= mLastSlotRequested) { - synchronized (waitObject) { - mGetDaySlotsTime.setTimeInMillis(0); - waitObject.notify(); - } + //Request next slot + if(mLastSlotReceived == mLastSlotRequested){ + synchronized (waitObject) { + mGetDaySlotsTime.setTimeInMillis(0); + waitObject.notify(); } } + + return true; } private void requestNextDaySlots() { - //Sync Day Stats - byte hour = (byte) ((mLastSlotReceived) / 6); - byte nextHour = (byte) (hour + 1); - byte nextMinute = 0; + Calendar now = GregorianCalendar.getInstance(); - if (nextHour == (byte) GregorianCalendar.getInstance().get(Calendar.HOUR_OF_DAY)) { - nextMinute = (byte) GregorianCalendar.getInstance().get(Calendar.MINUTE); + if (mLastSlotReceived >= 144 + 6) { // 24 * 6 + 6 + LOG.debug("Reached End of the Day"); + mLastSlotReceived = 0; + mSlotsToRequest = 6; // 1h + mGetDaySlotsTime = now; + mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); + mLastSlotRequested = 0; + return; } + //Sync Day Stats + mLastSlotRequested = Math.min(mLastSlotReceived + mSlotsToRequest, 144); + + LOG.debug("Requesting slot " + mLastSlotRequested); + + byte nextHour = (byte) (mLastSlotRequested / 6); + byte nextMinute = (byte) ((mLastSlotRequested % 6) * 10); + + if (nextHour == (byte) now.get(Calendar.HOUR_OF_DAY)) { + nextMinute = (byte) now.get(Calendar.MINUTE); + } + + byte hour = (byte) (mLastSlotReceived / 6); byte minute = (byte) ((mLastSlotReceived % 6) * 10); - mLastSlotRequested = (nextHour) * 6 + Math.round(nextMinute / 10); + byte[] msg = new byte[]{39, hour, minute, nextHour, nextMinute}; - if (nextHour >= 24 && nextMinute > 0) { // 24 * 6 - LOG.debug("Reached End of the Day"); - mLastSlotRequested = 0; - mLastSlotReceived = 0; - mGetDaySlotsTime = GregorianCalendar.getInstance(); - mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); - - return; - } - - if (nextHour > GregorianCalendar.getInstance().get(GregorianCalendar.HOUR_OF_DAY)) { - LOG.debug("Day data is up to date"); - mGetDaySlotsTime = GregorianCalendar.getInstance(); - mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); - return; - } - - //LOG.debug("Making new Request From " + hour + ":" + minute + " to " + nextHour + ":" + nextMinute); - - byte[] msg = new byte[]{39, hour, minute, nextHour, nextMinute}; //Request the entire day TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); builder.write(mHPlusSupport.ctrlCharacteristic, msg); builder.queue(mHPlusSupport.getQueue()); - mGetDaySlotsTime = GregorianCalendar.getInstance(); - mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_RETRY_PERIOD); + mGetDaySlotsTime = now; + if(mSlotsToRequest < 144) { + mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_RETRY_PERIOD); + }else{ + mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); + } } public boolean processIncomingSleepData(byte[] data){ From 970c6960ea41f3937e79d39039e22ce34ad5d7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Thu, 5 Jan 2017 14:24:39 +0000 Subject: [PATCH 102/244] HPlus: delay day slot fetch --- .../service/devices/hplus/HPlusHandlerThread.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index c40abfae..93715fce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -264,11 +264,13 @@ class HPlusHandlerThread extends GBDeviceIoThread { builder.queue(mHPlusSupport.getQueue()); mGetDaySlotsTime = now; - if(mSlotsToRequest < 144) { + if(mSlotsToRequest == 6) { mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_RETRY_PERIOD); }else{ mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); } + LOG.debug("Requesting next slot " + mLastSlotRequested+ " at " + mGetDaySlotsTime.getTime()); + } public boolean processIncomingSleepData(byte[] data){ From 7f50e0d2b7a472f8e1bc85e03287209f646de537 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 7 Jan 2017 22:41:10 +0100 Subject: [PATCH 103/244] Pebble: add support for weather in square watchface So far celsius are forced for temperature #482 --- .../pebble/AppMessageHandlerSquare.java | 78 +++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 2 + 2 files changed, 80 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java new file mode 100644 index 00000000..d58aaf04 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java @@ -0,0 +1,78 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.util.Pair; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; + +class AppMessageHandlerSquare extends AppMessageHandler { + // "CfgKeyCelsiusTemperature":10001, + // CfgKeyConditions":10002, + //"CfgKeyWeatherError":10003, + // "CfgKeyWeatherMode":10004, + // "CfgKeyUseCelsius":10005," + // CfgKeyWeatherLocation":10006," + // "CfgKeyTemperature":10000, + // + // + private static final int KEY_TEMP = 10001; //celsius + private static final int KEY_WEATHER = 10002; + private static final int KEY_WEATHER_MODE = 10004; + private static final int KEY_USE_CELSIUS = 10005; //celsius + private static final int KEY_LOCATION = 10006; + private static final int KEY_TEMP_F = 10000; //fahrenheit + + AppMessageHandlerSquare(UUID uuid, PebbleProtocol pebbleProtocol) { + super(uuid, pebbleProtocol); + } + + private byte[] encodeSquareWeatherMessage(WeatherSpec weatherSpec) { + if (weatherSpec == null) { + return null; + } + + ArrayList> pairs = new ArrayList<>(2); + pairs.add(new Pair<>(KEY_WEATHER_MODE, (Object) 1)); + pairs.add(new Pair<>(KEY_WEATHER, (Object) weatherSpec.currentCondition)); + pairs.add(new Pair<>(KEY_USE_CELSIUS, (Object) 1)); + pairs.add(new Pair<>(KEY_TEMP, (Object) (weatherSpec.currentTemp - 273))); + pairs.add(new Pair<>(KEY_LOCATION, (Object) (weatherSpec.location))); + byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + + ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); + + buf.put(weatherMessage); + + return buf.array(); + } + + @Override + public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + // Just ACK + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + return new GBDeviceEvent[]{sendBytesAck}; + } + + @Override + public GBDeviceEvent[] onAppStart() { + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeSquareWeatherMessage(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; + } + + @Override + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeSquareWeatherMessage(weatherSpec); + } +} 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 407b79fe..8223657d 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 @@ -362,6 +362,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_MARIOTIME = UUID.fromString("43caa750-2896-4f46-94dc-1adbd4bc1ff3"); private static final UUID UUID_HELTHIFY = UUID.fromString("7ee97b2c-95e8-4720-b94e-70fccd905d98"); private static final UUID UUID_TREKVOLLE = UUID.fromString("2da02267-7a19-4e49-9ed1-439d25db14e4"); + private static final UUID UUID_SQUARE = UUID.fromString("cb332373-4ee5-4c5c-8912-4f62af2d756c"); private static final UUID UUID_ZERO = new UUID(0, 0); @@ -380,6 +381,7 @@ public class PebbleProtocol extends GBDeviceProtocol { mAppMessageHandlers.put(UUID_MARIOTIME, new AppMessageHandlerMarioTime(UUID_MARIOTIME, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_HELTHIFY, new AppMessageHandlerHealthify(UUID_HELTHIFY, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_TREKVOLLE, new AppMessageHandlerTrekVolle(UUID_TREKVOLLE, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_SQUARE, new AppMessageHandlerSquare(UUID_SQUARE, PebbleProtocol.this)); } private final HashMap mDatalogSessions = new HashMap<>(); From 09cc0134dbc3b0ae874f6f9bcb3a930006ea2f0b Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 8 Jan 2017 15:27:01 +0100 Subject: [PATCH 104/244] Pebble: add support for weather in some watchfaces by gh/zalewszczak See https://github.com/zalewszczak/pebble for a list of watchfaces #482 --- .../pebble/AppMessageHandlerZalewszczak.java | 90 +++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 6 ++ 2 files changed, 96 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java new file mode 100644 index 00000000..756c7e52 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java @@ -0,0 +1,90 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.util.Pair; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; + +class AppMessageHandlerZalewszczak extends AppMessageHandler { + private static final int KEY_ICON = 0; + private static final int KEY_TEMP = 1; //celsius + + AppMessageHandlerZalewszczak(UUID uuid, PebbleProtocol pebbleProtocol) { + super(uuid, pebbleProtocol); + } + + /* + * converted to JAVA from original JS + */ + private int getIconForConditionCode(int conditionCode) { + if (conditionCode < 300) { + return 7; + } else if (conditionCode < 400) { + return 6; + } else if (conditionCode == 511) { + return 8; + } else if (conditionCode < 600) { + return 6; + } else if (conditionCode < 700) { + return 8; + } else if (conditionCode < 800) { + return 10; + } else if (conditionCode == 800) { + return 1; + } else if (conditionCode == 801) { + return 2; + } else if (conditionCode < 900) { + return 5; + } else { + return 0; + } + } + + + private byte[] encodeWeatherMessage(WeatherSpec weatherSpec) { + if (weatherSpec == null) { + return null; + } + + ArrayList> pairs = new ArrayList<>(2); + pairs.add(new Pair<>(KEY_TEMP, (Object) (Math.round(weatherSpec.currentTemp - 273) + "C"))); + pairs.add(new Pair<>(KEY_ICON, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode)))); + byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + + ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); + + buf.put(weatherMessage); + + return buf.array(); + } + + @Override + public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + // Just ACK + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + return new GBDeviceEvent[]{sendBytesAck}; + } + + @Override + public GBDeviceEvent[] onAppStart() { + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeWeatherMessage(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; + } + + @Override + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeWeatherMessage(weatherSpec); + } +} 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 8223657d..e8c16908 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 @@ -363,6 +363,9 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_HELTHIFY = UUID.fromString("7ee97b2c-95e8-4720-b94e-70fccd905d98"); private static final UUID UUID_TREKVOLLE = UUID.fromString("2da02267-7a19-4e49-9ed1-439d25db14e4"); private static final UUID UUID_SQUARE = UUID.fromString("cb332373-4ee5-4c5c-8912-4f62af2d756c"); + private static final UUID UUID_ZALEWSZCZAK_CROWEX = UUID.fromString("a88b3151-2426-43c6-b1d0-9b288b3ec47e"); + private static final UUID UUID_ZALEWSZCZAK_FANCY = UUID.fromString("014e17bf-5878-4781-8be1-8ef998cee1ba"); + private static final UUID UUID_ZALEWSZCZAK_TALLY = UUID.fromString("abb51965-52e2-440a-b93c-843eeacb697d"); private static final UUID UUID_ZERO = new UUID(0, 0); @@ -382,6 +385,9 @@ public class PebbleProtocol extends GBDeviceProtocol { mAppMessageHandlers.put(UUID_HELTHIFY, new AppMessageHandlerHealthify(UUID_HELTHIFY, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_TREKVOLLE, new AppMessageHandlerTrekVolle(UUID_TREKVOLLE, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_SQUARE, new AppMessageHandlerSquare(UUID_SQUARE, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_ZALEWSZCZAK_CROWEX, new AppMessageHandlerZalewszczak(UUID_ZALEWSZCZAK_CROWEX, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_ZALEWSZCZAK_FANCY, new AppMessageHandlerZalewszczak(UUID_ZALEWSZCZAK_FANCY, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_ZALEWSZCZAK_TALLY, new AppMessageHandlerZalewszczak(UUID_ZALEWSZCZAK_TALLY, PebbleProtocol.this)); } private final HashMap mDatalogSessions = new HashMap<>(); From 5c8f02d054446d95cb69693aa235fa774500fb36 Mon Sep 17 00:00:00 2001 From: Yar Date: Thu, 15 Dec 2016 22:58:56 +0300 Subject: [PATCH 105/244] Set notification to minimal priority Pebble app is not showing in status bar and on lockscreen. This change is for GadgetBridge to have the same behavior. --- .../main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java index a349a59d..8da545b7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -61,6 +61,7 @@ public class GB { .setContentText(text) .setSmallIcon(connected ? R.drawable.ic_notification : R.drawable.ic_notification_disconnected) .setContentIntent(pendingIntent) + .setPriority(Notification.PRIORITY_MIN) .setOngoing(true); if (GBApplication.isRunningLollipopOrLater()) { builder.setVisibility(Notification.VISIBILITY_PUBLIC); From c1abaaa4e0b54b321f35526047caef79d83aa4e2 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 8 Jan 2017 15:51:56 +0100 Subject: [PATCH 106/244] Add support for hiding the icon in the status bar and the notification on the lockscreen. This adds proper settings to toggle GB behavior and closes #460. --- .../nodomain/freeyourgadget/gadgetbridge/GBApplication.java | 4 ++++ .../java/nodomain/freeyourgadget/gadgetbridge/util/GB.java | 4 +++- app/src/main/res/values/strings.xml | 4 ++++ app/src/main/res/xml/preferences.xml | 6 ++++++ 4 files changed, 17 insertions(+), 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 b0659383..ea3abd19 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -171,6 +171,10 @@ public class GBApplication extends Application { return prefs.getBoolean("log_to_file", false); } + public static boolean minimizeNotification() { + return prefs.getBoolean("minimize_priority", false); + } + static void setupDatabase(Context context) { DBOpenHelper helper = new DBOpenHelper(context, DATABASE_NAME, null); SQLiteDatabase db = helper.getWritableDatabase(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java index 8da545b7..51e821aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -61,11 +61,13 @@ public class GB { .setContentText(text) .setSmallIcon(connected ? R.drawable.ic_notification : R.drawable.ic_notification_disconnected) .setContentIntent(pendingIntent) - .setPriority(Notification.PRIORITY_MIN) .setOngoing(true); if (GBApplication.isRunningLollipopOrLater()) { builder.setVisibility(Notification.VISIBILITY_PUBLIC); } + if (GBApplication.minimizeNotification()) { + builder.setPriority(Notification.PRIORITY_MIN); + } return builder.build(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6430085c..522d0bad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -65,6 +65,10 @@ Language + Hide the gadgetbridge notification + The icon in the status bar and the notification in the lockscreen are shown + The icon in the status bar and the notification in the lockscreen are hidden + Notifications Repetitions Phone Calls diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index b721a88f..92a17e12 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -30,6 +30,12 @@ android:entryValues="@array/pref_language_values" android:defaultValue="default" android:summary="%s" /> + Date: Sun, 8 Jan 2017 16:48:50 +0100 Subject: [PATCH 107/244] Pebble: try to get rid of the sleep and rely on countdownlatch instead. Could help with #494 --- .../service/devices/pebble/ble/PebbleGATTServer.java | 5 +++++ .../service/devices/pebble/ble/PebbleLESupport.java | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) 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 d3f54ee7..fd8637bb 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 @@ -111,6 +111,11 @@ class PebbleGATTServer extends BluetoothGattServerCallback { int serial = header >> 3; if (command == 0x01) { LOG.info("got ACK for serial = " + serial); + if (mPebbleLESupport.mPPAck != null) { + mPebbleLESupport.mPPAck.countDown(); + } else { + LOG.warn("mPPAck countdownlatch is not present but it probably should"); + } } if (command == 0x02) { // some request? LOG.info("got command 0x02"); 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 883cc13d..e13552d9 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 @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import java.util.concurrent.CountDownLatch; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -23,6 +24,7 @@ public class PebbleLESupport { private int mMTU = 20; private int mMTULimit = Integer.MAX_VALUE; boolean mIsConnected = false; + public CountDownLatch mPPAck; public PebbleLESupport(Context context, final BluetoothDevice btDevice, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) throws IOException { mBtDevice = btDevice; @@ -135,6 +137,7 @@ public class PebbleLESupport { int payloadToSend = bytesRead + 4; int srcPos = 0; + mPPAck = new CountDownLatch(1); while (payloadToSend > 0) { int chunkSize = (payloadToSend < (mMTU - 4)) ? payloadToSend : mMTU - 4; byte[] outBuf = new byte[chunkSize + 1]; @@ -145,7 +148,9 @@ public class PebbleLESupport { payloadToSend -= chunkSize; } - Thread.sleep(500); // FIXME ugly wait 0.5s after each pebble package send to the pebble (we do not wait for the GATT chunks) + mPPAck.await(); + mPPAck = null; + } catch (IOException | InterruptedException e) { LOG.info(e.getMessage()); Thread.currentThread().interrupt(); From 855a141ec4d9de15c650a868600d9fb0fab4e79f Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 8 Jan 2017 17:17:29 +0100 Subject: [PATCH 108/244] Pebble: in AppMessageHandler provide a default implementation of handleMessage which just ACKs --- .../service/devices/pebble/AppMessageHandler.java | 6 +++++- .../devices/pebble/AppMessageHandlerHealthify.java | 8 -------- .../devices/pebble/AppMessageHandlerMarioTime.java | 8 -------- .../service/devices/pebble/AppMessageHandlerSquare.java | 8 -------- .../devices/pebble/AppMessageHandlerTimeStylePebble.java | 8 -------- .../devices/pebble/AppMessageHandlerTrekVolle.java | 8 -------- .../devices/pebble/AppMessageHandlerZalewszczak.java | 8 -------- 7 files changed, 5 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java index 042c7e50..0e0ae701 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; @@ -28,7 +29,10 @@ class AppMessageHandler { } public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - return null; + // Just ACK + GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); + return new GBDeviceEvent[]{sendBytesAck}; } public GBDeviceEvent[] onAppStart() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java index 178b499e..29dad593 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java @@ -36,14 +36,6 @@ class AppMessageHandlerHealthify extends AppMessageHandler { return buf.array(); } - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - // Just ACK - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - return new GBDeviceEvent[]{sendBytesAck}; - } - @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java index 91b105e4..118e56e0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -37,14 +37,6 @@ class AppMessageHandlerMarioTime extends AppMessageHandler { return buf.array(); } - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - // Just ACK - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - return new GBDeviceEvent[]{sendBytesAck}; - } - @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java index d58aaf04..bcb77679 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java @@ -52,14 +52,6 @@ class AppMessageHandlerSquare extends AppMessageHandler { return buf.array(); } - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - // Just ACK - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - return new GBDeviceEvent[]{sendBytesAck}; - } - @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 16c96afc..25d995ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -109,14 +109,6 @@ class AppMessageHandlerTimeStylePebble extends AppMessageHandler { return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); } - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - // Just ACK - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - return new GBDeviceEvent[]{sendBytesAck}; - } - @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java index b3cfce66..d926e8d8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java @@ -59,14 +59,6 @@ class AppMessageHandlerTrekVolle extends AppMessageHandler { return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); } - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - // Just ACK - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - return new GBDeviceEvent[]{sendBytesAck}; - } - @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java index 756c7e52..f26b0704 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java @@ -64,14 +64,6 @@ class AppMessageHandlerZalewszczak extends AppMessageHandler { return buf.array(); } - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - // Just ACK - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - return new GBDeviceEvent[]{sendBytesAck}; - } - @Override public GBDeviceEvent[] onAppStart() { WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); From 6c269aa08952de556952d85eb185140adb3dfede Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 7 Jan 2017 23:03:58 +0100 Subject: [PATCH 109/244] Generic characteristic names from field names --- .../service/btle/GattCharacteristic.java | 101 ++++++++---------- 1 file changed, 44 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java index 812454a4..1767c15e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java @@ -1,7 +1,10 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle; import android.bluetooth.BluetoothGattCharacteristic; +import android.util.Log; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -178,64 +181,12 @@ public class GattCharacteristic { public static final UUID UUID_CHARACTERISTIC_WIND_CHILL = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A79"))); - //do we need this? + private static Map GATTCHARACTERISTIC_DEBUG; - private static final Map GATTCHARACTERISTIC_DEBUG; - - static { - GATTCHARACTERISTIC_DEBUG = new HashMap<>(); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_ALERT_CATEGORY_ID, "Alert AlertCategory ID"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_ALERT_CATEGORY_ID_BIT_MASK, "Alert AlertCategory ID Bit Mask"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_ALERT_LEVEL, "Alert Level"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_ALERT_NOTIFICATION_CONTROL_POINT, "Alert Notification Control Point"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_ALERT_STATUS, "Alert Status"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_GAP_APPEARANCE, "Appearance"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_BLOOD_PRESSURE_FEATURE, "Blood Pressure Feature"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_BLOOD_PRESSURE_MEASUREMENT, "Blood Pressure Measurement"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_BODY_SENSOR_LOCATION, "Body Sensor Location"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_CURRENT_TIME, "Current Time"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_DATE_TIME, "Date Time"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_DAY_DATE_TIME, "Day Date Time"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_DAY_OF_WEEK, "Day of Week"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_GAP_DEVICE_NAME, "Device Name"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_DST_OFFSET, "DST Offset"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_EXACT_TIME_256, "Exact Time 256"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_FIRMWARE_REVISION_STRING, "Firmware Revision String"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_HARDWARE_REVISION_STRING, "Hardware Revision String"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT, "Heart Rate Control Point"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT, "Heart Rate Measurement"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST, "IEEE 11073-20601 Regulatory"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_INTERMEDIATE_BLOOD_PRESSURE, "Intermediate Cuff Pressure"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_INTERMEDIATE_TEMPERATURE, "Intermediate Temperature"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_LOCAL_TIME_INFORMATION, "Local Time Information"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_MANUFACTURER_NAME_STRING, "Manufacturer Name String"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_MEASUREMENT_INTERVAL, "Measurement Interval"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_MODEL_NUMBER_STRING, "Model Number String"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_NEW_ALERT, "New Alert"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS, "Peripheral Preferred Connection Parameters"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_GAP_PERIPHERAL_PRIVACY_FLAG, "Peripheral Privacy Flag"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_GAP_RECONNECTION_ADDRESS, "Reconnection Address"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_REFERENCE_TIME_INFORMATION, "Reference Time Information"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_RINGER_CONTROL_POINT, "Ringer Control Point"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_RINGER_SETTING, "Ringer Setting"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_SERIAL_NUMBER_STRING, "Serial Number String"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_GATT_SERVICE_CHANGED, "Service Changed"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_SOFTWARE_REVISION_STRING, "Software Revision String"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_SUPPORTED_NEW_ALERT_CATEGORY, "Supported New Alert AlertCategory"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_SUPPORTED_UNREAD_ALERT_CATEGORY, "Supported Unread Alert AlertCategory"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_SYSTEM_ID, "System ID"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TEMPERATURE_MEASUREMENT, "Temperature Measurement"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TEMPERATURE_TYPE, "Temperature DeviceType"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TIME_ACCURACY, "Time Accuracy"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TIME_SOURCE, "Time Source"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TIME_UPDATE_CONTROL_POINT, "Time Update Control Point"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TIME_UPDATE_STATE, "Time Update State"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TIME_WITH_DST, "Time with DST"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TIME_ZONE, "Time Zone"); - GATTCHARACTERISTIC_DEBUG.put(UUID_CHARACTERISTIC_TX_POWER_LEVEL, "Tx Power Level"); - } - - public static String lookup(UUID uuid, String fallback) { + public static synchronized String lookup(UUID uuid, String fallback) { + if (GATTCHARACTERISTIC_DEBUG == null) { + GATTCHARACTERISTIC_DEBUG = initDebugMap(); + } String name = GATTCHARACTERISTIC_DEBUG.get(uuid); if (name == null) { name = fallback; @@ -243,6 +194,42 @@ public class GattCharacteristic { return name; } + private static Map initDebugMap() { + Map map = new HashMap<>(); + + try { + for (Field field : GattCharacteristic.class.getDeclaredFields()) { + if ((field.getModifiers() & Modifier.STATIC) != 0 && field.getType() == UUID.class) { + UUID uuid = (UUID) field.get(null); + if (uuid != null) { + map.put(uuid, toPrettyName(field.getName())); + } + } + } + } catch (Exception ex) { + Log.w(GattCharacteristic.class.getName(), "Error reading UUID fields by reflection: " + ex.getMessage(), ex); + } + return map; + } + + private static String toPrettyName(String fieldName) { + String[] words = fieldName.split("_"); + if (words.length <= 1) { + return fieldName.toLowerCase(); + } + StringBuilder builder = new StringBuilder(fieldName.length()); + for (String word : words) { + if (word.length() == 0 || "UUID".equals(word) || "CHARACTERISTIC".equals(word)) { + continue; + } + if (builder.length() > 0) { + builder.append(" "); + } + builder.append(word.toLowerCase()); + } + return builder.toString(); + } + public static String toString(BluetoothGattCharacteristic characteristic) { return characteristic.getUuid() + " (" + lookup(characteristic.getUuid(), "unknown") + ")"; } From 96203f574fb2f1b5704b449f9ef146b6223f777a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 8 Jan 2017 21:36:26 +0100 Subject: [PATCH 110/244] Tiny test for GattCharacteristic class --- .../freeyourgadget/gadgetbridge/test/MiscTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MiscTest.java diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MiscTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MiscTest.java new file mode 100644 index 00000000..9c499760 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MiscTest.java @@ -0,0 +1,14 @@ +package nodomain.freeyourgadget.gadgetbridge.test; + +import org.junit.Assert; +import org.junit.Test; + +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; + +public class MiscTest extends TestBase { + @Test + public void testGattCharacteristic() { + String desc = GattCharacteristic.lookup(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT, "xxx"); + Assert.assertEquals("heart rate control point", desc); + } +} From 7b1ea68b62a81c5bb9e21ba820e4bd7383ee918a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 8 Jan 2017 21:45:27 +0100 Subject: [PATCH 111/244] Remove obsolete layout params Closes #495 (thanks!) --- app/src/main/res/layout/activity_configure_alarms.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/res/layout/activity_configure_alarms.xml b/app/src/main/res/layout/activity_configure_alarms.xml index 5d5fe957..a22fcc18 100644 --- a/app/src/main/res/layout/activity_configure_alarms.xml +++ b/app/src/main/res/layout/activity_configure_alarms.xml @@ -10,7 +10,5 @@ android:descendantFocusability="blocksDescendants" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:id="@+id/alarm_list" - android:layout_alignParentTop="true" - android:layout_centerHorizontal="true" /> + android:id="@+id/alarm_list" /> From 98dc1e127e62c54f340386e3e2c809f58d0d1250 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 14:11:13 +0100 Subject: [PATCH 112/244] update changelogs --- CHANGELOG.md | 6 ++++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 7 ++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa1ae6d9..b076d0e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ###Changelog +####Version 0.17.0 (next) +* Add weather support through "Weather Notification" app +* Pebble: Support for build-in weather system app (FW 4.x) +* Pebble: Add weather support for various watchfaces +* Pebble 2/LE: Improve reliablitly and transfer speed + ####Version 0.16.0 * New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca * ZeBand: Initial support: notifications, heart rate, sleep monitoring, user configuration, date+time diff --git a/app/build.gradle b/app/build.gradle index e985816d..b6b68af6 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.16.0" - versionCode 80 + versionName "0.17.0" + versionCode 81 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index d9e4591a..51089ee0 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,11 @@ + + Add weather support through "Weather Notification" app + Pebble: Support for build-in weather system app (FW 4.x) + Pebble: Add weather support for various watchfaces + Pebble 2/LE: Improve reliablitly and transfer speed + New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca ZeBand: Initial support: notifications, heart rate, sleep monitoring, user configuration, date+time @@ -10,7 +16,6 @@ Mi Band 2: Fix activity data missing after doing manual hr measurements or live activity Support sharing firmwares/watchapps/watchfaces to Gadgetbridge Support for the "Subsonic" music player (#474) - Mi Band: Fix crash with unknown notification sources From 6619a16b63d5b3bf4019bf410925f7fc677c2010 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 14:18:58 +0100 Subject: [PATCH 113/244] fix xml --- 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 51089ee0..ee4bc2cb 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,6 +1,6 @@ - + Add weather support through "Weather Notification" app Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces From f56a4d878eb7a32169d70dba034558a48307294d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 14:19:45 +0100 Subject: [PATCH 114/244] fix xml for real (maybe) --- app/src/main/res/xml/changelog_master.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index ee4bc2cb..5f0774fc 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -16,6 +16,7 @@ Mi Band 2: Fix activity data missing after doing manual hr measurements or live activity Support sharing firmwares/watchapps/watchfaces to Gadgetbridge Support for the "Subsonic" music player (#474) + Mi Band: Fix crash with unknown notification sources From a2c052090b83579d90f7775366b6cba42260e1f0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 14:22:54 +0100 Subject: [PATCH 115/244] update Japanese and Spanish from transifex (ignore French, sorry guys, but I dont know what is going on with CA/FR fights) --- app/src/main/res/values-es/strings.xml | 44 ++++++++++++++------------ app/src/main/res/values-ja/strings.xml | 2 ++ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index b0a40f60..1c613394 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -12,7 +12,7 @@ Desconectar Borrar Dispositivo Borrar %1$s - ¡Esta acción borrará el dispositivo y toda la información asociada a él! + ¡Esta acción borrará el dispositivo y toda su información asociada! Depuración Gestor de app @@ -27,6 +27,8 @@ Desactivar Activar Monitor de Ritmo Cardíaco Desactivar Monitor de Ritmo Cardíaco + Activar la aplicación del tiempo del sistema + Desactivar la aplicación del tiempo del sistema Configurar Mover a la parte de arriba @@ -36,14 +38,14 @@ Estás a punto de instalar el firmware %s en lugar del que está en tu MiBand. Estás a punto de instalar los firmwares %1$s y %2$s en lugar de los que están en tu MiBand. Este firmware ha sido probado y se sabe que es compatible con Gadgetbridge. - Este firmware no ha sido probado y puede que no sea compatible con Gadgetbridge.\n\nNO se recomienda la instalación en tu MiBand!. - Si aún así quieres seguir y las cosas continúan funcionando correctamente después de esto, por favor indícales a los desarrolladores de Gadgetbridge que la versión del firmware: %s funciona bien. + Este firmware no ha sido probado y puede que no sea compatible con Gadgetbridge.\n\n¡NO se recomienda la instalación en tu MiBand!. + Si aun así quieres seguir y las cosas continúan funcionando correctamente, por favor indícales a los desarrolladores de Gadgetbridge que esta versión del firmware funciona bien: %s . Ajustes Ajustes generales Conectarse al dispositivo cuando el Bluetooth esté activado Reconectar automáticamente - Reproductor de audio preferido + Reproductor de audio favorito Predeterminado Fecha y hora Sincronizar hora @@ -62,7 +64,7 @@ Soporte para notificaciones genéricas … también con pantalla encendida No Molestar - Dejar de enviar Notificaciones no deseadas basándose en el modo No Molestar + Dejar de enviar notificaciones no deseadas basándose en el modo No Molestar siempre cuando la pantalla está apagada nunca @@ -99,7 +101,7 @@ Preferir siempre BLE Usar el soporte experimental de Pebble LE para todos los Pebble en lugar del bluetooth clásico. Requiere vincular \"Pebble LE\" si un Pebble no-LE ha sido vinculado antes. Pebble 2/LE límite de GATT MTU - Si su Pebble 2/Pebble LE no funciona correctamente, pruebe esta opción para limitar el MTU (rango válido 20–512) + Si tu Pebble 2/Pebble LE no funciona correctamente, prueba esta opción para limitar el MTU (rango válido 20–512) Activar crear registros de la App del Reloj Producirá registros de las apps del reloj que Gadgetbridge guardará (necesita reconexión) Intentos de reconexión @@ -170,7 +172,7 @@ Medio Largo Muy largo - Ring + Timbre Alarma Vibración Probar @@ -208,7 +210,7 @@ No se ha podido instalar el fichero: %1$s No se puede instalar este firmware: no coincide con la revision hardware de tu Pebble. Por favor, espera mientras se determina el estado de la instalación... - Batería baja del Gadget! + ¡Batería baja del Gadget! A %1$s le queda: %2$s%% batería Última carga: %s \n Número de cargas: %s @@ -219,13 +221,13 @@ El archivo no puede ser instalado, el dispositivo no está listo. Miband firmware %1$s Versión compatible - Versión no probada! + ¡Versión no probada! Conexión al dispositivo: %1$s Pebble firmware %1$s Revisión de hardware correcta - La revisión de hardware es incorrecta! + ¡La versión de hardware es incorrecta! %1$s (%2$s) - Hubo un problema con la transferencia de firmware. NO REINICIES tu Mi Band! + Hubo un problema con la transferencia de firmware. ¡NO REINICIES tu Mi Band! Hubo un problema con la transferencia de metadatos del firmware Instalación del firmware completa Instalación del firmware completa, reiniciando dispositivo... @@ -284,7 +286,7 @@ Importar Base de Datos Importar datos de actividad antiguos Desde Gadgetbridge 0.12.0 usamos un nuevo formato de base de datos. -Se pueden importar los antiguos datos de actividad y asociarlos con el dispoditivo al que se está conectando (%1$s).\n +Se pueden importar los antiguos datos de actividad y asociarlos con el dispositivo al que se está conectando (%1$s).\n \n Si no importas los antiguos datos de actividad ahora, siempre lo podrás hacer después seleccionando \"MERGE OLD ACTIVITY DATA\" en el apartado de gestión de base de datos de actividad.\n \n @@ -303,17 +305,17 @@ Por favor, ten en cuenta que puedes importar datos desde Mi Band, Pebble Health ¿Quiere sobreescribir la base de datos actual? Todos sus datos actuales (si los hay) se borrarán. Importado con éxito. Error importando DB: %1$s - No se ha encontrado una Base de Datos con actividad antigua, no se importará nada. - No hay ningún dispositivo conectado al que asociar la Base de Datos antigua. - Uniendo los datos de actividad + No se ha encontrado una base de datos con actividad antigua, no se importará nada. + No hay ningún dispositivo conectado al que asociar la base de datos antigua. + Fusionando los datos de actividad Por favor, espere mientras se unen las bases de datos. - Error importando los datos antiguos a la nueva Base de Datos. + Error importando los datos antiguos a la nueva base de datos. Asociar los datos antiguos al dispositivo - ¿Quiere borrar los datos de actividad? - ¿Quiere borrar la base de datos? Todos sus datos de actividad y la información sobre sus dispositivos se borrarán. + ¿Quieres borrar los datos de actividad? + ¿Quieres borrar la base de datos? Todos tus datos de actividad y la información sobre tus dispositivos se borrarán. Datos borrados. - El borrado de la Base de Datos ha fallado. - ¿Quieres borrar la antigua Base de Datos de Actividades? + El borrado de la base de datos ha fallado. + ¿Quieres borrar la antigua base de datos de actividades? ¿Quiere borrar la base de datos antigua? Si los datos no se han importado, se perderán. Los datos antiguos han sido borrados. El borrado de la Base de datos antiguos ha fallado. @@ -324,5 +326,5 @@ Por favor, ten en cuenta que puedes importar datos desde Mi Band, Pebble Health Vibración Emparejando con Pebble - En su dispositivo Android va a aparecer un mensaje para emparejarse. Si no apareciera, mire en el cajón de notificaciones y acepte la propuesta de emparejamiento. Después acepte también en su Pebble. + En su dispositivo Android va a aparecer un mensaje para emparejarse. Si no apareciera, mira en el cajón de notificaciones y acepta la propuesta de emparejamiento. Después acepta también en tu Pebble. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ddddedfa..09222a91 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -27,6 +27,8 @@ 非アクティベート HRM をアクティベート HRM を非アクティベート + システムの天気アプリを有効にする + システムの天気アプリを無効にする 設定 先頭に移動 From f05b51fd832eef184076cad23cd8c0de1816f3c9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 14:41:02 +0100 Subject: [PATCH 116/244] Pebble: Add option to disable call display Closes #494 --- CHANGELOG.md | 1 + .../gadgetbridge/service/devices/pebble/PebbleSupport.java | 7 ++++++- app/src/main/res/values/strings.xml | 3 +++ app/src/main/res/xml/changelog_master.xml | 1 + app/src/main/res/xml/preferences.xml | 5 +++++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b076d0e8..8285da4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Add weather support through "Weather Notification" app * Pebble: Support for build-in weather system app (FW 4.x) * Pebble: Add weather support for various watchfaces +* Pebble: Add option to disable call display * Pebble 2/LE: Improve reliablitly and transfer speed ####Version 0.16.0 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 f88f5f77..56087f23 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 @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; @@ -120,7 +121,11 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { @Override public void onSetCallState(CallSpec callSpec) { if (reconnect()) { - super.onSetCallState(callSpec); + if (callSpec.command == CallSpec.CALL_OUTGOING) { + if (GBApplication.getPrefs().getBoolean("pebble_enable_outgoing_call",true)) { + super.onSetCallState(callSpec); + } + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 522d0bad..522883d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,6 +104,9 @@ Sync Misfit Sync Morpheuz + Support outgoing calls + Disabling this will also stop the Pebble 2/LE to vibrate on outgoing calls + Allow 3rd Party Android App Access Enable experimental support for Android Apps using PebbleKit diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 5f0774fc..ee5f87fd 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -4,6 +4,7 @@ Add weather support through "Weather Notification" app Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces + Pebble: Add option to disable call display Pebble 2/LE: Improve reliablitly and transfer speed diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 92a17e12..43985431 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -152,6 +152,11 @@ android:title="@string/pref_title_pebble_settings"> + Date: Mon, 9 Jan 2017 15:11:50 +0100 Subject: [PATCH 117/244] Pebble: use Notifications system app as parent UUID for notifications --- .../appmanager/AppManagerFragmentInstalledApps.java | 2 +- .../service/devices/pebble/PebbleProtocol.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) 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 fe53c32c..c5919fe9 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 @@ -16,7 +16,7 @@ public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment //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(PebbleProtocol.UUID_NOTIFICATIONS, "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)); 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 e8c16908..fb0016d7 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 @@ -354,6 +354,8 @@ public class PebbleProtocol extends GBDeviceProtocol { 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 public static final UUID UUID_WEATHER = UUID.fromString("61b22bc8-1e29-460d-a236-3fe409a439ff"); // FIXME: store somewhere else, this is also accessed by other code + public static final UUID UUID_NOTIFICATIONS = UUID.fromString("b2cae818-10f8-46df-ad2b-98ad2254a3c1"); + 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_MISFIT = UUID.fromString("0b73b76a-cd65-4dc2-9585-aaa213320858"); @@ -925,9 +927,8 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putLong(uuid.getMostSignificantBits()); buf.putInt((int) (uuid.getLeastSignificantBits() >>> 32)); buf.putInt(id); - buf.putLong(uuid.getMostSignificantBits()); - buf.putInt((int) (uuid.getLeastSignificantBits() >>> 32)); - buf.putInt(id); + buf.putLong(UUID_NOTIFICATIONS.getMostSignificantBits()); + buf.putLong(UUID_NOTIFICATIONS.getLeastSignificantBits()); buf.order(ByteOrder.LITTLE_ENDIAN); buf.putInt(timestamp); // 32-bit timestamp buf.putShort((short) 0); // duration From 3644d5e7a64fa38879277998b568757636078cb5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 16:33:00 +0100 Subject: [PATCH 118/244] Pebble: remove notifications when dismissed on the phone (#326) Most of the code is generic, so it could be implemented by other devices. I dont know what happens if multiple messages arrive in the same notification. So, this is experimental. --- .../gadgetbridge/devices/EventHandler.java | 2 ++ .../externalevents/NotificationListener.java | 3 ++- .../gadgetbridge/impl/GBDeviceService.java | 8 ++++++++ .../gadgetbridge/model/DeviceService.java | 1 + .../service/DeviceCommunicationService.java | 5 +++++ .../gadgetbridge/service/ServiceDeviceSupport.java | 5 +++++ .../service/devices/hplus/HPlusSupport.java | 5 +++++ .../service/devices/miband/MiBandSupport.java | 5 +++++ .../service/devices/miband2/MiBand2Support.java | 5 +++++ .../service/devices/pebble/PebbleProtocol.java | 11 +++++++---- .../devices/vibratissimo/VibratissimoSupport.java | 5 +++++ .../service/serial/AbstractSerialDeviceSupport.java | 6 ++++++ .../gadgetbridge/service/serial/GBDeviceProtocol.java | 4 ++++ .../gadgetbridge/service/TestDeviceSupport.java | 5 +++++ 14 files changed, 65 insertions(+), 5 deletions(-) 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 00374072..6cfaa1ef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -22,6 +22,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; public interface EventHandler { void onNotification(NotificationSpec notificationSpec); + void onDeleteNotification(int id); + void onSetTime(); void onSetAlarms(ArrayList alarms); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 130ea7d5..4ad5fa48 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -392,7 +392,8 @@ public class NotificationListener extends NotificationListenerService { @Override public void onNotificationRemoved(StatusBarNotification sbn) { - + LOG.info("notification removed, will ask device to delete it"); + GBApplication.deviceService().onDeleteNotification((int) sbn.getPostTime()); //FIMXE: a truly unique id would be better } private void dumpExtras(Bundle bundle) { 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 361dbe26..949791c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -105,6 +105,14 @@ public class GBDeviceService implements DeviceService { invokeService(intent); } + @Override + public void onDeleteNotification(int id) { + Intent intent = createIntent().setAction(ACTION_DELETE_NOTIFICATION) + .putExtra(EXTRA_NOTIFICATION_ID, id); + invokeService(intent); + + } + @Override public void onSetTime() { Intent intent = createIntent().setAction(ACTION_SETTIME); 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 b536fefb..f03a58d5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -16,6 +16,7 @@ public interface DeviceService extends EventHandler { String ACTION_START = PREFIX + ".action.start"; String ACTION_CONNECT = PREFIX + ".action.connect"; String ACTION_NOTIFICATION = PREFIX + ".action.notification"; + String ACTION_DELETE_NOTIFICATION = PREFIX + ".action.delete_notification"; String ACTION_CALLSTATE = PREFIX + ".action.callstate"; String ACTION_SETCANNEDMESSAGES = PREFIX + ".action.setcannedmessages"; String ACTION_SETTIME = PREFIX + ".action.settime"; 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 f4417fb7..0edf9171 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -60,6 +60,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CA import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETE_CALENDAREVENT; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETE_NOTIFICATION; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT; @@ -355,6 +356,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mDeviceSupport.onNotification(notificationSpec); break; } + case ACTION_DELETE_NOTIFICATION: { + mDeviceSupport.onDeleteNotification(intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)); + break; + } case ACTION_ADD_CALENDAREVENT: { CalendarEventSpec calendarEventSpec = new CalendarEventSpec(); calendarEventSpec.id = intent.getLongExtra(EXTRA_CALENDAREVENT_ID, -1); 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 437bf5c7..e46d03db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -135,6 +135,11 @@ public class ServiceDeviceSupport implements DeviceSupport { delegate.onNotification(notificationSpec); } + @Override + public void onDeleteNotification(int id) { + delegate.onDeleteNotification(id); + } + @Override public void onSetTime() { if (checkBusy("set time") || checkThrottle("set time")) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index be083b07..c92c8736 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -447,6 +447,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { showText(notificationSpec.body); } + @Override + public void onDeleteNotification(int id) { + + } + @Override public void onSetTime() { 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 1971c073..be1e32fb 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 @@ -545,6 +545,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { performPreferredNotification(origin + " received", origin, null); } + @Override + public void onDeleteNotification(int id) { + + } + @Override public void onSetTime() { try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index b89e9e08..d068c2c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -594,6 +594,11 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { performPreferredNotification(origin + " received", origin, alertLevel, null); } + @Override + public void onDeleteNotification(int id) { + + } + @Override public void onSetTime() { try { 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 fb0016d7..ede3da7a 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 @@ -483,6 +483,11 @@ public class PebbleProtocol extends GBDeviceProtocol { } } + @Override + public byte[] encodeDeleteNotification(int id) { + return encodeBlobdb(new UUID(GB_UUID_MASK, id), BLOBDB_DELETE, BLOBDB_NOTIFICATION, null); + } + @Override public byte[] encodeAddCalendarEvent(CalendarEventSpec calendarEventSpec) { long id = calendarEventSpec.id != -1 ? calendarEventSpec.id : mRandom.nextLong(); @@ -917,16 +922,14 @@ public class PebbleProtocol extends GBDeviceProtocol { } } - UUID uuid = UUID.randomUUID(); short pin_length = (short) (NOTIFICATION_PIN_LENGTH + attributes_length); ByteBuffer buf = ByteBuffer.allocate(pin_length); // pin - 46 bytes buf.order(ByteOrder.BIG_ENDIAN); - buf.putLong(uuid.getMostSignificantBits()); - buf.putInt((int) (uuid.getLeastSignificantBits() >>> 32)); - buf.putInt(id); + buf.putLong(GB_UUID_MASK); + buf.putLong(id); buf.putLong(UUID_NOTIFICATIONS.getMostSignificantBits()); buf.putLong(UUID_NOTIFICATIONS.getLeastSignificantBits()); buf.order(ByteOrder.LITTLE_ENDIAN); 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 b19b4329..cca5825d 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 @@ -124,6 +124,11 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport { public void onNotification(NotificationSpec notificationSpec) { } + @Override + public void onDeleteNotification(int id) { + + } + @Override public void onSetTime() { 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 216f2c51..d3df5c13 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 @@ -108,6 +108,12 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport sendToDevice(bytes); } + @Override + public void onDeleteNotification(int id) { + byte[] bytes = gbDeviceProtocol.encodeDeleteNotification(id); + sendToDevice(bytes); + } + @Override public void onSetTime() { byte[] bytes = gbDeviceProtocol.encodeSetTime(); 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 77a5bc2e..ca3e3ee9 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 @@ -21,6 +21,10 @@ public abstract class GBDeviceProtocol { return null; } + public byte[] encodeDeleteNotification(int id) { + return null; + } + public byte[] encodeSetTime() { return null; } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 160b855b..45ba1217 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -54,6 +54,11 @@ class TestDeviceSupport extends AbstractDeviceSupport { } + @Override + public void onDeleteNotification(int id) { + + } + @Override public void onSetTime() { From 8c0f5599a1d33920801d783f3936731d7e452449 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 16:49:11 +0100 Subject: [PATCH 119/244] Do not try to remove notifications from the device in some obvious cases --- .../externalevents/NotificationListener.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 4ad5fa48..70c026c1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -392,7 +392,22 @@ public class NotificationListener extends NotificationListenerService { @Override public void onNotificationRemoved(StatusBarNotification sbn) { + //FIXME: deduplicate code + String source = sbn.getPackageName(); + Notification notification = sbn.getNotification(); + if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) { + return; + } + + if (source.equals("android") || + source.equals("com.android.systemui") || + source.equals("com.android.dialer") || + source.equals("com.cyanogenmod.eleven")) { + return; + } + LOG.info("notification removed, will ask device to delete it"); + GBApplication.deviceService().onDeleteNotification((int) sbn.getPostTime()); //FIMXE: a truly unique id would be better } From fda317671ab3efaf244d6509a354a163b98c9722 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 18:34:22 +0100 Subject: [PATCH 120/244] Ignore summary information for k9 mail (#373) --- .../gadgetbridge/externalevents/NotificationListener.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 70c026c1..80942dd9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -253,6 +253,10 @@ public class NotificationListener extends NotificationListenerService { notificationSpec.type = AppNotificationType.getInstance().get(source); if (source.equals("com.fsck.k9")) { + // we dont want group summaries at all for k9 + if ((notification.flags & Notification.FLAG_GROUP_SUMMARY) == Notification.FLAG_GROUP_SUMMARY) { + return; + } preferBigText = true; } From 8f988de49d1a7f963874ec6b55b46c4ca770bf7e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 9 Jan 2017 18:39:37 +0100 Subject: [PATCH 121/244] update changelogs --- 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 8285da4d..9980af04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ * Pebble: Support for build-in weather system app (FW 4.x) * Pebble: Add weather support for various watchfaces * Pebble: Add option to disable call display +* Pebble: Delete notifications that got dismissed on the phone * Pebble 2/LE: Improve reliablitly and transfer speed +* Various fixes for K9 mail when using the generic notification receiver ####Version 0.16.0 * New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index ee5f87fd..8895eea3 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -4,8 +4,10 @@ Add weather support through "Weather Notification" app Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces + Pebble: Delete notifications that got dismissed on the phone Pebble: Add option to disable call display Pebble 2/LE: Improve reliablitly and transfer speed + Various fixes for K9 mail when using the generic notification receiver New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca From 4cf872664cb3bc3954c7f88d3459b3aff3a128bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Tue, 10 Jan 2017 13:08:45 +0000 Subject: [PATCH 122/244] HPlus: Improved support for storing and displaying data. --- .../devices/hplus/HPlusConstants.java | 29 +- .../devices/hplus/HPlusCoordinator.java | 4 + .../hplus/HPlusHealthSampleProvider.java | 76 ++- .../devices/hplus/HPlusDataRecord.java | 17 +- .../devices/hplus/HPlusDataRecordDaySlot.java | 31 +- .../hplus/HPlusDataRecordDaySummary.java | 4 +- .../hplus/HPlusDataRecordRealtime.java | 21 +- .../devices/hplus/HPlusDataRecordSleep.java | 14 +- .../devices/hplus/HPlusHandlerThread.java | 484 +++++++++++------- .../service/devices/hplus/HPlusSupport.java | 44 +- 10 files changed, 466 insertions(+), 258 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 70546e87..5feaf6d7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -28,8 +28,8 @@ public final class HPlusConstants { public static final byte ARG_HEARTRATE_MEASURE_ON = 11; public static final byte ARG_HEARTRATE_MEASURE_OFF = 22; - public static final byte ARG_HEARTRATE_ALLDAY_ON = 10; - public static final byte ARG_HEARTRATE_ALLDAY_OFF = -1; + public static final byte ARG_HEARTRATE_ALLDAY_ON = 0x0A; + public static final byte ARG_HEARTRATE_ALLDAY_OFF = (byte) 0xff; public static final byte INCOMING_CALL_STATE_DISABLED_THRESHOLD = 0x7B; public static final byte INCOMING_CALL_STATE_ENABLED = (byte) 0xAA; @@ -64,9 +64,7 @@ public final class HPlusConstants { public static final byte CMD_SET_CONF_END = 0x4f; public static final byte CMD_SET_PREFS = 0x50; public static final byte CMD_SET_SIT_INTERVAL = 0x51; - - public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; - + public static final byte CMD_SET_HEARTRATE_STATE = 0x32; //Actions to device public static final byte CMD_GET_ACTIVE_DAY = 0x27; @@ -76,19 +74,17 @@ public final class HPlusConstants { public static final byte CMD_GET_DEVICE_ID = 0x24; public static final byte CMD_ACTION_INCOMING_SOCIAL = 0x31; - //public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; + //public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; //Unknown public static final byte CMD_ACTION_DISPLAY_TEXT = 0x43; public static final byte CMD_ACTION_DISPLAY_TEXT_NAME = 0x3F; public static final byte CMD_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312? - public static final byte[] CMD_ACTION_HELLO = new byte[]{0x01, 0}; - public static final byte CMD_SHUTDOWN = 91; - public static final byte ARG_SHUTDOWN_EN = 90; + public static final byte[] CMD_ACTION_HELLO = new byte[]{0x01, 0x00}; + public static final byte CMD_SHUTDOWN = 0x5B; + public static final byte ARG_SHUTDOWN_EN = 0x5A; public static final byte CMD_FACTORY_RESET = -74; - public static final byte ARG_FACTORY_RESET_EN = 90; - - + public static final byte ARG_FACTORY_RESET_EN = 0x5A; public static final byte CMD_SET_INCOMING_MESSAGE = 0x07; public static final byte CMD_SET_INCOMING_CALL = 0x06; @@ -103,15 +99,9 @@ public final class HPlusConstants { public static final byte DATA_SLEEP = 0x1A; public static final byte DATA_VERSION = 0x18; - - public static final byte DB_TYPE_DAY_SLOT_SUMMARY = 1; - public static final byte DB_TYPE_DAY_SUMMARY = 2; - public static final byte DB_TYPE_INSTANT_STATS = 3; - public static final byte DB_TYPE_SLEEP_STATS = 4; - - public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; + public static final String PREF_HPLUS_HR = "hplus_hr_enable"; public static final String PREF_HPLUS_UNIT = "hplus_unit"; public static final String PREF_HPLUS_TIMEMODE = "hplus_timemode"; public static final String PREF_HPLUS_WRIST = "hplus_wrist"; @@ -120,5 +110,4 @@ public final class HPlusConstants { public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time"; public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time"; public static final String PREF_HPLUS_COUNTRY = "hplus_country"; - } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 3de594b6..8a596d4c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -198,6 +198,10 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, HPlusConstants.ARG_HEARTRATE_ALLDAY_ON) & 0xFF); } + public static byte getHRState(String address) { + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_HR + "_" + address, HPlusConstants.ARG_HEARTRATE_MEASURE_ON) & 0xFF); + } + public static byte getSocial(String address) { //TODO: Figure what this is. Returning the default value diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java index 3fe16e41..9a7fa8c1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -5,9 +5,12 @@ package nodomain.freeyourgadget.gadgetbridge.devices.hplus; */ import android.support.annotation.NonNull; +import android.util.Log; +import java.util.Calendar; import java.util.Collections; import java.util.Comparator; +import java.util.GregorianCalendar; import java.util.List; import de.greenrobot.dao.AbstractDao; @@ -25,6 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDa import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusDataRecord; public class HPlusHealthSampleProvider extends AbstractSampleProvider { @@ -44,13 +48,28 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider getActivityamples(int timestamp_from, int timestamp_to) { + return getAllActivitySamples(timestamp_from, timestamp_to); + } + + public List getSleepSamples(int timestamp_from, int timestamp_to) { + return getAllActivitySamples(timestamp_from, timestamp_to); + } + @NonNull @Override public List getAllActivitySamples(int timestamp_from, int timestamp_to) { @@ -97,16 +125,48 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider qb = getSession().getHPlusHealthActivityOverlayDao().queryBuilder(); - qb.where(HPlusHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from)) - .where(HPlusHealthActivityOverlayDao.Properties.TimestampTo.le(timestamp_to)); + qb.where(HPlusHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()), + HPlusHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from - 3600 * 24), + HPlusHealthActivityOverlayDao.Properties.TimestampTo.le(timestamp_to), + HPlusHealthActivityOverlayDao.Properties.TimestampTo.ge(timestamp_from)); List overlayRecords = qb.build().list(); + //Todays sample steps will come from the Day Slots messages + //Historical steps will be provided by Day Summaries messages + //This will allow both week and current day results to be consistent + Calendar today = GregorianCalendar.getInstance(); + today.set(Calendar.HOUR_OF_DAY, 0); + today.set(Calendar.MINUTE, 0); + today.set(Calendar.SECOND, 0); + today.set(Calendar.MILLISECOND, 0); + + int stepsToday = 0; + for(HPlusHealthActivitySample sample: samples){ + if(sample.getTimestamp() >= today.getTimeInMillis() / 1000){ + //Only consider these for the current day as a single message is enough for steps + //HR and Overlays will still benefit from the full set of samples + if(sample.getRawKind() == HPlusDataRecord.TYPE_REALTIME) { + int aux = sample.getSteps(); + sample.setSteps(sample.getSteps() - stepsToday); + stepsToday = aux; + }else + sample.setSteps(ActivitySample.NOT_MEASURED); + }else{ + if (sample.getRawKind() != HPlusDataRecord.TYPE_DAY_SUMMARY) { + sample.setSteps(ActivityKind.TYPE_NOT_MEASURED); + } + } + } + for (HPlusHealthActivityOverlay overlay : overlayRecords) { + + //Create fake events to improve activity counters if there are no events around the overlay + //timestamp boundaries insertVirtualItem(samples, Math.max(overlay.getTimestampFrom(), timestamp_from), overlay.getDeviceId(), overlay.getUserId()); insertVirtualItem(samples, Math.min(overlay.getTimestampTo() - 1, timestamp_to - 1), overlay.getDeviceId(), overlay.getUserId()); - for (HPlusHealthActivitySample sample : samples) { + if (sample.getTimestamp() >= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) { sample.setRawKind(overlay.getRawKind()); } @@ -124,7 +184,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider insertVirtualItem(List samples, int timestamp, long deviceId, long userId){ + private List insertVirtualItem(List samples, int timestamp, long deviceId, long userId) { HPlusHealthActivitySample sample = new HPlusHealthActivitySample( timestamp, // ts deviceId, @@ -143,5 +203,5 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider= 144) { @@ -50,14 +51,34 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord { steps = (data[2] & 0xFF) * 256 + data[3] & 0xFF; - //?? data[6]; + //?? data[6]; atemp?? always 0 secondsInactive = data[7] & 0xFF; // ? - int now = (int) (GregorianCalendar.getInstance().getTimeInMillis() / (3600 * 24 * 1000L)); - timestamp = now * 3600 * 24 + (slot / 6 * 3600 + slot % 6 * 10); + Calendar slotTime = GregorianCalendar.getInstance(); + + slotTime.set(Calendar.MINUTE, (slot % 6) * 10); + slotTime.set(Calendar.HOUR_OF_DAY, slot / 6); + slotTime.set(Calendar.SECOND, 0); + + timestamp = (int) (slotTime.getTimeInMillis() / 1000L); } public String toString(){ - return String.format(Locale.US, "Slot: %d, Steps: %d, InactiveSeconds: %d, HeartRate: %d", slot, steps, secondsInactive, heartRate); + Calendar slotTime = GregorianCalendar.getInstance(); + slotTime.setTimeInMillis(timestamp * 1000L); + return String.format(Locale.US, "Slot: %d, Time: %s, Steps: %d, InactiveSeconds: %d, HeartRate: %d", slot, slotTime.getTime(), steps, secondsInactive, heartRate); + } + + public void add(HPlusDataRecordDaySlot other){ + if(other == null) + return; + + steps += other.steps; + secondsInactive += other.secondsInactive; + if(heartRate == -1) + heartRate = other.heartRate; + else if(other.heartRate != -1) { + heartRate = (heartRate + other.heartRate) / 2; + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java index 0bb6609c..e28960f9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java @@ -60,7 +60,7 @@ class HPlusDataRecordDaySummary extends HPlusDataRecord{ public int calories; HPlusDataRecordDaySummary(byte[] data) { - super(data); + super(data, TYPE_DAY_SUMMARY); year = (data[10] & 0xFF) * 256 + (data[9] & 0xFF); month = data[11] & 0xFF; @@ -75,7 +75,7 @@ class HPlusDataRecordDaySummary extends HPlusDataRecord{ throw new IllegalArgumentException("Invalid record date "+year+"-"+month+"-"+day); } steps = (data[2] & 0xFF) * 256 + (data[1] & 0xFF); - distance = (data[4] & 0xFF) * 256 + (data[3] & 0xFF); + distance = ((data[4] & 0xFF) * 256 + (data[3] & 0xFF)) * 10; activeTime = (data[14] & 0xFF) * 256 + (data[13] & 0xFF); calories = (data[6] & 0xFF) * 256 + (data[5] & 0xFF); calories += (data[8] & 0xFF) * 256 + (data[7] & 0xFF); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java index d827976d..8b39f84f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java @@ -33,6 +33,11 @@ class HPlusDataRecordRealtime extends HPlusDataRecord { */ public byte battery; + /** + * Number of steps today + */ + public int steps; + /** * Time active (To be determined how it works) */ @@ -45,7 +50,7 @@ class HPlusDataRecordRealtime extends HPlusDataRecord { public int intensity; public HPlusDataRecordRealtime(byte[] data) { - super(data); + super(data, TYPE_REALTIME); if (data.length < 15) { throw new IllegalArgumentException("Invalid data packet"); @@ -53,7 +58,7 @@ class HPlusDataRecordRealtime extends HPlusDataRecord { timestamp = (int) (GregorianCalendar.getInstance().getTimeInMillis() / 1000); distance = 10 * ((data[4] & 0xFF) * 256 + (data[3] & 0xFF)); // meters - + steps = (data[2] & 0xFF) * 256 + (data[1] & 0xFF); int x = (data[6] & 0xFF) * 256 + data[5] & 0xFF; int y = (data[8] & 0xFF) * 256 + data[7] & 0xFF; @@ -63,10 +68,14 @@ class HPlusDataRecordRealtime extends HPlusDataRecord { heartRate = data[11] & 0xFF; // BPM activeTime = (data[14] & 0xFF * 256) + (data[13] & 0xFF); - if(heartRate == 255) + if(heartRate == 255) { intensity = 0; - else + activityKind = ActivityKind.TYPE_NOT_MEASURED; + } + else { intensity = (int) (100 * Math.max(0, Math.min((heartRate - 60) / 120.0, 1))); // TODO: Calculate a proper value + activityKind = ActivityKind.TYPE_UNKNOWN; + } } public void computeActivity(HPlusDataRecordRealtime prev){ @@ -93,11 +102,11 @@ class HPlusDataRecordRealtime extends HPlusDataRecord { if(other == null) return false; - return distance == other.distance && calories == other.calories && heartRate == other.heartRate && battery == other.battery; + return steps == other.steps && distance == other.distance && calories == other.calories && heartRate == other.heartRate && battery == other.battery; } public String toString(){ - return String.format(Locale.US, "Distance: %d Calories: %d HeartRate: %d Battery: %d ActiveTime: %d Intensity: %d", distance, calories, heartRate, battery, activeTime, intensity); + return String.format(Locale.US, "Distance: %d Steps: %d Calories: %d HeartRate: %d Battery: %d ActiveTime: %d Intensity: %d", distance, steps, calories, heartRate, battery, activeTime, intensity); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java index 97ea0294..f74db8fb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; +import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -67,7 +68,7 @@ public class HPlusDataRecordSleep extends HPlusDataRecord { public int wakeupCount; public HPlusDataRecordSleep(byte[] data) { - super(data); + super(data, TYPE_SLEEP); int year = (data[2] & 0xFF) * 256 + (data[1] & 0xFF); int month = data[3] & 0xFF; @@ -91,6 +92,7 @@ public class HPlusDataRecordSleep extends HPlusDataRecord { int minute = data[18] & 0xFF; Calendar sleepStart = GregorianCalendar.getInstance(); + sleepStart.clear(); sleepStart.set(Calendar.YEAR, year); sleepStart.set(Calendar.MONTH, month - 1); sleepStart.set(Calendar.DAY_OF_MONTH, day); @@ -114,4 +116,14 @@ public class HPlusDataRecordSleep extends HPlusDataRecord { intervals.add(new RecordInterval(ts, bedTimeEnd, ActivityKind.TYPE_DEEP_SLEEP)); return intervals; } + + public String toString(){ + Calendar s = GregorianCalendar.getInstance(); + s.setTimeInMillis(bedTimeStart * 1000L); + + Calendar end = GregorianCalendar.getInstance(); + end.setTimeInMillis(bedTimeEnd * 1000L); + + return String.format(Locale.US, "Sleep start: %s end: %s enter: %d spindles: %d rem: %d deep: %d wake: %d-%d", s.getTime(), end.getTime(), enterSleepMinutes, spindleMinutes, remSleepMinutes, deepSleepMinutes, wakeupMinutes, wakeupCount); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index 93715fce..d8e5de5a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -6,14 +6,21 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; import android.content.Context; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; import java.util.GregorianCalendar; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; @@ -28,6 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; @@ -35,20 +43,22 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; class HPlusHandlerThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(HPlusHandlerThread.class); - private int CURRENT_DAY_SYNC_PERIOD = 60 * 10; + private int CURRENT_DAY_SYNC_PERIOD = 24 * 60 * 60 * 365; //Never private int CURRENT_DAY_SYNC_RETRY_PERIOD = 10; + private int SLEEP_SYNC_PERIOD = 12 * 60 * 60; private int SLEEP_SYNC_RETRY_PERIOD = 30; private int DAY_SUMMARY_SYNC_PERIOD = 24 * 60 * 60; + private int DAY_SUMMARY_SYNC_RETRY_PERIOD = 30; private int HELLO_INTERVAL = 60; private boolean mQuit = false; private HPlusSupport mHPlusSupport; - private int mLastSlotReceived = 0; + + private int mLastSlotReceived = -1; private int mLastSlotRequested = 0; - private int mSlotsToRequest = 6; private Calendar mLastSleepDayReceived = GregorianCalendar.getInstance(); private Calendar mHelloTime = GregorianCalendar.getInstance(); @@ -56,10 +66,13 @@ class HPlusHandlerThread extends GBDeviceIoThread { private Calendar mGetSleepTime = GregorianCalendar.getInstance(); private Calendar mGetDaySummaryTime = GregorianCalendar.getInstance(); + private boolean mSlotsInitialSync = true; + private HPlusDataRecordRealtime prevRealTimeRecord = null; private final Object waitObject = new Object(); - List mDaySlotSamples = new ArrayList<>(); + + List mDaySlotSamples = new ArrayList<>(); public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) { super(gbDevice, context); @@ -78,7 +91,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { long waitTime = 0; while (!mQuit) { - //LOG.debug("Waiting " + (waitTime)); + if (waitTime > 0) { synchronized (waitObject) { try { @@ -88,10 +101,16 @@ class HPlusHandlerThread extends GBDeviceIoThread { } } } + if (mQuit) { break; } + if(!mHPlusSupport.getDevice().isConnected()){ + quit(); + break; + } + Calendar now = GregorianCalendar.getInstance(); if (now.compareTo(mHelloTime) > 0) { @@ -111,7 +130,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { } now = GregorianCalendar.getInstance(); - waitTime = Math.min(Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()), mHelloTime.getTimeInMillis()) - now.getTimeInMillis(); + waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()), mHelloTime.getTimeInMillis())) - now.getTimeInMillis(); } } @@ -128,151 +147,153 @@ class HPlusHandlerThread extends GBDeviceIoThread { mGetSleepTime.setTimeInMillis(0); mGetDaySlotsTime.setTimeInMillis(0); mGetDaySummaryTime.setTimeInMillis(0); + mLastSleepDayReceived.setTimeInMillis(0); + + mSlotsInitialSync = true; + mLastSlotReceived = -1; + mLastSlotRequested = 0; TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY}); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); + builder.wait(400); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); + builder.wait(400); + + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); + builder.wait(400); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); + builder.wait(400); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY}); + builder.wait(400); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA}); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, HPlusConstants.ARG_HEARTRATE_ALLDAY_ON}); - builder.queue(mHPlusSupport.getQueue()); + scheduleHello(); synchronized (waitObject) { waitObject.notify(); } } + /** + * Send an Hello/Null Packet to keep connection + */ private void sendHello() { TransactionBuilder builder = new TransactionBuilder("hello"); - builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO); + builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO); builder.queue(mHPlusSupport.getQueue()); + scheduleHello(); + + synchronized (waitObject) { + waitObject.notify(); + } } + /** + * Schedule an Hello Packet in the future + */ public void scheduleHello(){ mHelloTime = GregorianCalendar.getInstance(); mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL); } - public void requestDaySummaryData(){ - TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary"); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); - builder.queue(mHPlusSupport.getQueue()); - - mGetDaySummaryTime = GregorianCalendar.getInstance(); - mGetDaySummaryTime.add(Calendar.SECOND, DAY_SUMMARY_SYNC_PERIOD); - } - + /** + * Process a message containing information regarding a day slot + * A slot summarizes 10 minutes of data + * + * @param data the message from the device + * @return boolean indicating success or fail + */ public boolean processIncomingDaySlotData(byte[] data) { HPlusDataRecordDaySlot record; + try{ record = new HPlusDataRecordDaySlot(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); + return false; + } + + //Ignore real time messages as they are still not understood + if(!mSlotsInitialSync){ + mGetDaySlotsTime.set(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); return true; } - mLastSlotReceived = record.slot; - try (DBHandler dbHandler = GBApplication.acquireDB()) { - HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + Calendar now = GregorianCalendar.getInstance(); + int nowSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + (now.get(Calendar.MINUTE) / 10); - Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); - Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); - HPlusHealthActivitySample sample = new HPlusHealthActivitySample( - record.timestamp, // ts - deviceId, userId, // User id - record.getRawData(), // Raw Data - ActivityKind.TYPE_UNKNOWN, - 0, // Intensity - record.steps, // Steps - record.heartRate, // HR - ActivitySample.NOT_MEASURED, // Distance - ActivitySample.NOT_MEASURED // Calories - ); - sample.setProvider(provider); - mDaySlotSamples.add(sample); - - Calendar now = GregorianCalendar.getInstance(); - - //Dump buffered samples to DB - if ((record.timestamp + (60*100) >= (now.getTimeInMillis() / 1000L) )) { - - provider.getSampleDao().insertOrReplaceInTx(mDaySlotSamples); - - mGetDaySlotsTime.setTimeInMillis(0); - mDaySlotSamples.clear(); - mSlotsToRequest = 144 - mLastSlotReceived; - } - } catch (GBException ex) { - LOG.debug((ex.getMessage())); - } catch (Exception ex) { - LOG.debug(ex.getMessage()); + //If the slot is in the future, actually it is from the previous day + //Subtract a day of seconds + if(record.slot >= nowSlot){ + record.timestamp -= 3600 * 24; } - //Request next slot - if(mLastSlotReceived == mLastSlotRequested){ + //Ignore out of order messages + if(record.slot == mLastSlotReceived + 1) { + mLastSlotReceived = record.slot; + } + + if(record.slot < 143){ + mDaySlotSamples.add(record); + }else { + + //Sort the samples + Collections.sort(mDaySlotSamples, new Comparator() { + public int compare(HPlusDataRecordDaySlot one, HPlusDataRecordDaySlot other) { + return one.timestamp - other.timestamp; + } + }); + + try (DBHandler dbHandler = GBApplication.acquireDB()) { + HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + List samples = new ArrayList<>(); + + for(HPlusDataRecordDaySlot storedRecord : mDaySlotSamples) { + HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp); + + sample.setRawHPlusHealthData(record.getRawData()); + sample.setSteps(record.steps); + sample.setHeartRate(record.heartRate); + sample.setRawKind(record.type); + + sample.setProvider(provider); + samples.add(sample); + } + + provider.getSampleDao().insertOrReplaceInTx(samples); + mDaySlotSamples.clear(); + + } catch (GBException ex) { + LOG.debug((ex.getMessage())); + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } + } + //Still fetching ring buffer. Request the next slots + if (record.slot == mLastSlotRequested) { + mGetDaySlotsTime.clear(); synchronized (waitObject) { - mGetDaySlotsTime.setTimeInMillis(0); waitObject.notify(); } } - return true; } - private void requestNextDaySlots() { - - Calendar now = GregorianCalendar.getInstance(); - - if (mLastSlotReceived >= 144 + 6) { // 24 * 6 + 6 - LOG.debug("Reached End of the Day"); - mLastSlotReceived = 0; - mSlotsToRequest = 6; // 1h - mGetDaySlotsTime = now; - mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); - mLastSlotRequested = 0; - return; - } - - //Sync Day Stats - mLastSlotRequested = Math.min(mLastSlotReceived + mSlotsToRequest, 144); - - LOG.debug("Requesting slot " + mLastSlotRequested); - - byte nextHour = (byte) (mLastSlotRequested / 6); - byte nextMinute = (byte) ((mLastSlotRequested % 6) * 10); - - if (nextHour == (byte) now.get(Calendar.HOUR_OF_DAY)) { - nextMinute = (byte) now.get(Calendar.MINUTE); - } - - byte hour = (byte) (mLastSlotReceived / 6); - byte minute = (byte) ((mLastSlotReceived % 6) * 10); - - byte[] msg = new byte[]{39, hour, minute, nextHour, nextMinute}; - - TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); - builder.write(mHPlusSupport.ctrlCharacteristic, msg); - builder.queue(mHPlusSupport.getQueue()); - - mGetDaySlotsTime = now; - if(mSlotsToRequest == 6) { - mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_RETRY_PERIOD); - }else{ - mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); - } - LOG.debug("Requesting next slot " + mLastSlotRequested+ " at " + mGetDaySlotsTime.getTime()); - - } + /** + * Process sleep data from the device + * Devices send a single sleep message for each sleep period + * This message contains the duration of the sub-intervals (rem, deep, etc...) + * + * @param data the message from the device + * @return boolean indicating success or fail + */ public boolean processIncomingSleepData(byte[] data){ HPlusDataRecordSleep record; @@ -280,7 +301,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { record = new HPlusDataRecordSleep(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); - return true; + return false; } mLastSleepDayReceived.setTimeInMillis(record.bedTimeStart * 1000L); @@ -293,9 +314,10 @@ class HPlusHandlerThread extends GBDeviceIoThread { HPlusHealthActivityOverlayDao overlayDao = session.getHPlusHealthActivityOverlayDao(); HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); - //Insert the Overlays + //Get the individual Sleep overlays and insert them List overlayList = new ArrayList<>(); List intervals = record.getIntervals(); + for(HPlusDataRecord.RecordInterval interval : intervals){ overlayList.add(new HPlusHealthActivityOverlay(interval.timestampFrom, interval.timestampTo, interval.activityKind, deviceId, userId, null)); } @@ -303,23 +325,13 @@ class HPlusHandlerThread extends GBDeviceIoThread { overlayDao.insertOrReplaceInTx(overlayList); //Store the data - HPlusHealthActivitySample sample = new HPlusHealthActivitySample( - record.timestamp, // ts - deviceId, userId, // User id - record.getRawData(), // Raw Data - record.activityKind, - 0, // Intensity - ActivitySample.NOT_MEASURED, // Steps - ActivitySample.NOT_MEASURED, // HR - ActivitySample.NOT_MEASURED, // Distance - ActivitySample.NOT_MEASURED // Calories - ); + HPlusHealthActivitySample sample = createSample(dbHandler, record.timestamp); + sample.setRawHPlusHealthData(record.getRawData()); + sample.setRawKind(record.activityKind); sample.setProvider(provider); provider.addGBActivitySample(sample); - - } catch (Exception ex) { LOG.debug(ex.getMessage()); } @@ -330,16 +342,12 @@ class HPlusHandlerThread extends GBDeviceIoThread { return true; } - private void requestNextSleepData() { - mGetSleepTime = GregorianCalendar.getInstance(); - mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_SYNC_RETRY_PERIOD); - - TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); - builder.queue(mHPlusSupport.getQueue()); - } - - + /** + * Process a message containing real time information + * + * @param data the message from the device + * @return boolean indicating success or fail + */ public boolean processRealtimeStats(byte[] data) { HPlusDataRecordRealtime record; @@ -347,49 +355,59 @@ class HPlusHandlerThread extends GBDeviceIoThread { record = new HPlusDataRecordRealtime(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); - return true; + return false; } - - if(record.same(prevRealTimeRecord)) + //Skip duplicated messages as the device seems to send the same record multiple times + //This can be used to detect the user is moving (not sleeping) + if(prevRealTimeRecord != null && record.same(prevRealTimeRecord)) return true; prevRealTimeRecord = record; getDevice().setBatteryLevel(record.battery); - getDevice().sendDeviceUpdateIntent(getContext()); - //Skip when measuring - if(record.heartRate == 255) { + //Skip when measuring heart rate + //Calories and Distance are updated and these values will be lost. + //Because a message with a valid Heart Rate will be provided, this loss very limited + if(record.heartRate == ActivityKind.TYPE_NOT_MEASURED) { getDevice().setFirmwareVersion2("---"); getDevice().sendDeviceUpdateIntent(getContext()); - return true; + }else { + getDevice().setFirmwareVersion2("" + record.heartRate); + getDevice().sendDeviceUpdateIntent(getContext()); } - getDevice().setFirmwareVersion2("" + record.heartRate); - getDevice().sendDeviceUpdateIntent(getContext()); - try (DBHandler dbHandler = GBApplication.acquireDB()) { HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); - Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); - Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); - HPlusHealthActivitySample sample = new HPlusHealthActivitySample( - record.timestamp, // ts - deviceId, userId, // User id - record.getRawData(), // Raw Data - record.activityKind, - record.intensity, // Intensity - ActivitySample.NOT_MEASURED, // Steps - record.heartRate, // HR - record.distance, // Distance - record.calories // Calories - ); + HPlusHealthActivitySample sample = createSample(dbHandler, record.timestamp); + sample.setRawKind(record.type); + sample.setRawIntensity(record.intensity); + sample.setHeartRate(record.heartRate); + sample.setDistance(record.distance); + sample.setCalories(record.calories); + sample.setSteps(record.steps); + sample.setRawHPlusHealthData(record.getRawData()); sample.setProvider(provider); + + if (sample.getSteps() != ActivitySample.NOT_MEASURED && sample.getSteps() - prevRealTimeRecord.steps > 0) { + Intent intent = new Intent(DeviceService.ACTION_REALTIME_STEPS) + .putExtra(DeviceService.EXTRA_REALTIME_STEPS, sample.getSteps() - prevRealTimeRecord.steps) + .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); + } + provider.addGBActivitySample(sample); //TODO: Handle Active Time. With Overlay? - } catch (GBException ex) { LOG.debug((ex.getMessage())); } catch (Exception ex) { @@ -398,57 +416,35 @@ class HPlusHandlerThread extends GBDeviceIoThread { return true; } - + /** + * Process a day summary message + * This message includes aggregates regarding an entire day + * + * @param data the message from the device + * @return boolean indicating success or fail + */ public boolean processDaySummary(byte[] data) { - LOG.debug("Process Day Summary"); HPlusDataRecordDaySummary record; try{ record = new HPlusDataRecordDaySummary(data); } catch(IllegalArgumentException e){ LOG.debug((e.getMessage())); - return true; + return false; } - LOG.debug("Received: " + record); try (DBHandler dbHandler = GBApplication.acquireDB()) { HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); - Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); - Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + HPlusHealthActivitySample sample = createSample(dbHandler, record.timestamp); - //Hugly (?) fix. - //This message returns the day summary, but the DB already has some detailed entries with steps and distance. - //However DB data is probably incomplete as some update messages could be missing - //Proposed fix: Calculate the total steps and distance and store a new sample with the remaining data - //Existing data will reflect user activity with the issue of a potencially large number of steps at midnight. - //Steps counters by day will be OK with this - - List samples = provider.getActivitySamples(record.timestamp - 3600 * 24 + 1, record.timestamp); - - int missingDistance = record.distance; - int missingSteps = record.steps; - - for(HPlusHealthActivitySample sample : samples){ - if(sample.getSteps() > 0) { - missingSteps -= sample.getSteps(); - } - if(sample.getDistance() > 0){ - missingDistance -= sample.getDistance(); - } - } - - HPlusHealthActivitySample sample = new HPlusHealthActivitySample( - record.timestamp, // ts - deviceId, userId, // User id - record.getRawData(), // Raw Data - ActivityKind.TYPE_UNKNOWN, - 0, // Intensity - Math.max( missingSteps, 0), // Steps - ActivitySample.NOT_MEASURED, // HR - Math.max( missingDistance, 0), // Distance - ActivitySample.NOT_MEASURED // Calories - ); + sample.setRawKind(record.type); + sample.setSteps(record.steps); + sample.setDistance(record.distance); + sample.setCalories(record.calories); + sample.setDistance(record.distance); + sample.setHeartRate((record.maxHeartRate - record.minHeartRate) / 2); //TODO: Find an alternative approach for Day Summary Heart Rate + sample.setRawHPlusHealthData(record.getRawData()); sample.setProvider(provider); provider.addGBActivitySample(sample); @@ -458,9 +454,17 @@ class HPlusHandlerThread extends GBDeviceIoThread { LOG.debug(ex.getMessage()); } + mGetDaySummaryTime = GregorianCalendar.getInstance(); + mGetDaySummaryTime.add(Calendar.SECOND, DAY_SUMMARY_SYNC_PERIOD); return true; } + /** + * Process a message containing information regarding firmware version + * + * @param data the message from the device + * @return boolean indicating success or fail + */ public boolean processVersion(byte[] data) { int major = data[2] & 0xFF; int minor = data[1] & 0xFF; @@ -471,4 +475,102 @@ class HPlusHandlerThread extends GBDeviceIoThread { return true; } -} \ No newline at end of file + + /** + * Issue a message requesting the next batch of sleep data + */ + private void requestNextSleepData() { + TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); + builder.queue(mHPlusSupport.getQueue()); + + + mGetSleepTime = GregorianCalendar.getInstance(); + mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_SYNC_RETRY_PERIOD); + } + + /** + * Issue a message requesting the next set of slots + * The process will sync 1h at a time until the device is in sync + * Then it will request samples until the end of the day in order to minimize data loss + * Messages will be provided every 10 minutes after they are available + */ + private void requestNextDaySlots() { + + Calendar now = GregorianCalendar.getInstance(); + int currentSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + now.get(Calendar.MINUTE) / 10; + + //Finished dumping the entire ring buffer + //Sync to current time + mGetDaySlotsTime = now; + + if(mSlotsInitialSync) { + if(mLastSlotReceived == 143) { + mSlotsInitialSync = false; + mGetDaySlotsTime.set(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); //Sync complete. Delay timer forever + mLastSlotReceived = -1; + mLastSlotRequested = mLastSlotReceived + 1; + return; + }else { + mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_RETRY_PERIOD); + } + }else{ + //Sync complete. Delay timer forever + mGetDaySlotsTime.set(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); + return; + } + + if(mLastSlotReceived == 143) + mLastSlotReceived = -1; + + byte hour = (byte) ((mLastSlotReceived + 1)/ 6); + byte minute = (byte) (((mLastSlotReceived + 1) % 6) * 10); + + byte nextHour = hour; + byte nextMinute = 59; + + mLastSlotRequested = nextHour * 6 + (nextMinute / 10); + + byte[] msg = new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY, hour, minute, nextHour, nextMinute}; + + TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); + builder.write(mHPlusSupport.ctrlCharacteristic, msg); + builder.queue(mHPlusSupport.getQueue()); + } + /** + * Request a batch of data with the summary of the previous days + */ + public void requestDaySummaryData(){ + TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary"); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); + builder.queue(mHPlusSupport.getQueue()); + + mGetDaySummaryTime = GregorianCalendar.getInstance(); + mGetDaySummaryTime.add(Calendar.SECOND, DAY_SUMMARY_SYNC_RETRY_PERIOD); + } + + /** + * Helper function to create a sample + * @param dbHandler The database handler + * @param timestamp The sample timestamp + * @return The sample just created + */ + private HPlusHealthActivitySample createSample(DBHandler dbHandler, int timestamp){ + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + timestamp, // ts + deviceId, userId, // User id + null, // Raw Data + ActivityKind.TYPE_UNKNOWN, + 0, // Intensity + ActivitySample.NOT_MEASURED, // Steps + ActivitySample.NOT_MEASURED, // HR + ActivitySample.NOT_MEASURED, // Distance + ActivitySample.NOT_MEASURED // Calories + ); + + return sample; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 17e9e666..dd229b81 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -37,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSuppo import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -344,14 +345,23 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setAllDayHeart(TransactionBuilder transaction) { - LOG.info("Attempting to set All Day HR..."); - byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + byte value = HPlusCoordinator.getHRState(getDevice().getAddress()); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.CMD_SET_HEARTRATE_STATE, + value + }); + + + value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_ALLDAY_HRM, value }); + return this; } @@ -415,6 +425,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { //TODO: Show different notifications according to source as Band supports this + //LOG.debug("OnNotification: Title: "+notificationSpec.title+" Body: "+notificationSpec.body+" Source: "+notificationSpec.sourceName+" Sender: "+notificationSpec.sender+" Subject: "+notificationSpec.subject); showText(notificationSpec.title, notificationSpec.body); } @@ -534,7 +545,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, 0x0A}); //Set Real Time... ? + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_HEARTRATE_STATE, HPlusConstants.ARG_HEARTRATE_MEASURE_ON}); //Set Real Time... ? builder.queue(getQueue()); } @@ -629,20 +640,14 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, 1}); //Show Call Icon - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, HPlusConstants.ARG_INCOMING_CALL}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_INCOMING_CALL, HPlusConstants.ARG_INCOMING_CALL}); builder.queue(getQueue()); - //TODO: Use WaitAction - try { - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); - } - byte[] msg = new byte[13]; builder = performInitialized("incomingCallNumber"); + builder.wait(200); //Show call number for (int i = 0; i < msg.length; i++) @@ -657,13 +662,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.write(ctrlCharacteristic, msg); builder.queue(getQueue()); - try { - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); - } - builder = performInitialized("incomingCallText"); + builder.wait(200); //Show call name //Must call twice, otherwise nothing happens @@ -676,11 +676,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME; builder.write(ctrlCharacteristic, msg); - try { - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); - } + builder.wait(200); msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME_CN; builder.write(ctrlCharacteristic, msg); @@ -693,6 +689,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private void showText(String title, String body) { + LOG.debug("Show Notification: "+title+" --> "+body); try { TransactionBuilder builder = performInitialized("notification"); @@ -720,6 +717,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_SOCIAL, (byte) 255}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_INCOMING_MESSAGE, HPlusConstants.ARG_INCOMING_MESSAGE}); + int remaining; if (message.length() % 17 > 0) @@ -801,4 +800,5 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return true; } } + } From b92b1c08bfd29fdb00e5c7ebf62fa2dce9e10ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Tue, 10 Jan 2017 13:44:32 +0000 Subject: [PATCH 123/244] HPlus: Fix deprecation warning --- .../gadgetbridge/daogen/GBDaoGenerator.java | 1 + .../devices/hplus/HPlusHandlerThread.java | 17 +++++------------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 2905d45f..d70679a1 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -225,6 +225,7 @@ public class GBDaoGenerator { private static Entity addHPlusHealthActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "HPlusHealthActivitySample"); + activitySample.implementsSerializable(); addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); activitySample.addByteArrayProperty("rawHPlusHealthData"); activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().primaryKey(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index d8e5de5a..7b05775d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -391,19 +391,12 @@ class HPlusHandlerThread extends GBDeviceIoThread { sample.setRawHPlusHealthData(record.getRawData()); sample.setProvider(provider); - if (sample.getSteps() != ActivitySample.NOT_MEASURED && sample.getSteps() - prevRealTimeRecord.steps > 0) { - Intent intent = new Intent(DeviceService.ACTION_REALTIME_STEPS) - .putExtra(DeviceService.EXTRA_REALTIME_STEPS, sample.getSteps() - prevRealTimeRecord.steps) - .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); - } + sample.setSteps(sample.getSteps() - prevRealTimeRecord.steps); - 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) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); provider.addGBActivitySample(sample); From f2e6ce63807f511118db889d0718ec9e153dc997 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 10 Jan 2017 18:23:35 +0100 Subject: [PATCH 124/244] Pebble: fix incoming calls (recently broken) --- .../gadgetbridge/service/devices/pebble/PebbleSupport.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 56087f23..122f6da5 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 @@ -121,10 +121,8 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { @Override public void onSetCallState(CallSpec callSpec) { if (reconnect()) { - if (callSpec.command == CallSpec.CALL_OUTGOING) { - if (GBApplication.getPrefs().getBoolean("pebble_enable_outgoing_call",true)) { - super.onSetCallState(callSpec); - } + if ((callSpec.command != CallSpec.CALL_OUTGOING) || GBApplication.getPrefs().getBoolean("pebble_enable_outgoing_call", true)) { + super.onSetCallState(callSpec); } } } From 185605211d00d914e6acaf9bd1aa133663a7ea5e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 10 Jan 2017 22:30:55 +0100 Subject: [PATCH 125/244] Pebble: fix bug in PebbleKit implementation regarding binary data transferred from a watchapp to a 3rd party Android app (Fixes a bug with TCW) --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ede3da7a..25c5cf2a 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 @@ -1811,7 +1811,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.get(bytes); if (type == TYPE_BYTEARRAY) { jsonObject.put("type", "bytes"); - jsonObject.put("value", Base64.encode(bytes, Base64.NO_WRAP)); + jsonObject.put("value", new String(Base64.encode(bytes, Base64.NO_WRAP))); } else { jsonObject.put("type", "string"); jsonObject.put("value", new String(bytes)); From 9132736428a8b209ee9d6994bad18f801bb2927e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 10 Jan 2017 22:43:10 +0100 Subject: [PATCH 126/244] Pebble: report current firmware string to PebbleKit (eg. "v3.12.2") not "Gadgetbridge" --- .../gadgetbridge/contentprovider/PebbleContentProvider.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java index d0235521..e16c1bdc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java @@ -63,10 +63,12 @@ public class PebbleContentProvider extends ContentProvider { if (prefs.getBoolean("pebble_enable_pebblekit", false)) { appMessage = 1; } + String fwString = "unknown"; if (mGBDevice != null && mGBDevice.getType() == DeviceType.PEBBLE && mGBDevice.isInitialized()) { connected = 1; + fwString = mGBDevice.getFirmwareVersion(); } - mc.addRow(new Object[]{connected, appMessage, 0, 3, 8, 2, "Gadgetbridge"}); + mc.addRow(new Object[]{connected, appMessage, 0, 3, 8, 2, fwString}); return mc; } else { From bcc4fa8e9c0b750ca098a23586f0cf819e397202 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 10 Jan 2017 23:28:55 +0100 Subject: [PATCH 127/244] update CHANGELOG --- 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 9980af04..c2ae741c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,13 @@ ####Version 0.17.0 (next) * Add weather support through "Weather Notification" app +* Various fixes for K9 mail when using the generic notification receiver * Pebble: Support for build-in weather system app (FW 4.x) * Pebble: Add weather support for various watchfaces * Pebble: Add option to disable call display * Pebble: Delete notifications that got dismissed on the phone +* Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other) * Pebble 2/LE: Improve reliablitly and transfer speed -* Various fixes for K9 mail when using the generic notification receiver ####Version 0.16.0 * New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 8895eea3..814d7e27 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,12 +2,13 @@ Add weather support through "Weather Notification" app + Various fixes for K9 mail when using the generic notification receiver Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces Pebble: Delete notifications that got dismissed on the phone Pebble: Add option to disable call display + Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other) Pebble 2/LE: Improve reliablitly and transfer speed - Various fixes for K9 mail when using the generic notification receiver New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca From 453eeb0bae71c033582127e7109120db9f5a6547 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 11 Jan 2017 08:38:44 +0100 Subject: [PATCH 128/244] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add João Paulo Barraca to Authors --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 53585a88..91b7f3ef 100644 --- a/README.md +++ b/README.md @@ -122,12 +122,17 @@ Known Issues: * set time (automatically upon connection) * display notifications and vibrate -## Authors (in order of first code contribution) +## Authors +### Core Team (in order of first code contribution) * Andreas Shimokawa * Carsten Pfeiffer * Daniele Gobbetti +### Additional device support + +* João Paulo Barraca (HPlus) + ## Contribute Contributions are welcome, be it feedback, bugreports, documentation, translation, research or code. Feel free to work From 5e74338efe161045fe098c87e5097c3c165049fa Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 11 Jan 2017 21:43:42 +0100 Subject: [PATCH 129/244] Add HPLus stuff to changelog --- CHANGELOG.md | 7 +++++++ app/src/main/res/xml/changelog_master.xml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ae741c..1ee0d0da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ * Pebble: Delete notifications that got dismissed on the phone * Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other) * Pebble 2/LE: Improve reliablitly and transfer speed +* HPlus: Improved discovery and pairing +* HPlus: Improved notifications (display + vibration) +* HPlus: Synchronize time and date +* HPlus: Display firmware version and battery charge +* HPlus: Near real time Heart rate measurement +* HPlus: Experimental synchronization of activity data (only sleep, steps and intensity) +* HPlus: Fix some disconnection issues ####Version 0.16.0 * New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 814d7e27..c54077e4 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -9,6 +9,13 @@ Pebble: Add option to disable call display Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other) Pebble 2/LE: Improve reliablitly and transfer speed + HPlus: Improved discovery and pairing + HPlus: Improved notifications (display + vibration) + HPlus: Synchronize time and date + HPlus: Display firmware version and battery charge + HPlus: Near real time Heart rate measurement + HPlus: Experimental synchronization of activity data (only sleep, steps and intensity) + HPlus: Fix some disconnection issues New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca From 50cb3c9db38e6cc65f03ebcabadb15545b8a2ad1 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 11 Jan 2017 22:39:08 +0100 Subject: [PATCH 130/244] Pebble: remove null termination from cstrings when converting to json for PebbleKit --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 4 ++++ 1 file changed, 4 insertions(+) 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 25c5cf2a..0a2d7cde 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 @@ -1783,6 +1783,9 @@ public class PebbleProtocol extends GBDeviceProtocol { byte type = buf.get(); short length = buf.getShort(); jsonObject.put("key", key); + if (type == TYPE_CSTRING) { + length--; + } jsonObject.put("length", length); switch (type) { case TYPE_UINT: @@ -1815,6 +1818,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } else { jsonObject.put("type", "string"); jsonObject.put("value", new String(bytes)); + buf.get(); // skip null-termination; } break; default: From 0218cee0e12ab988288b3cae410a8137fa9b9ece Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 11 Jan 2017 23:42:40 +0100 Subject: [PATCH 131/244] Pebble: fix long standing bug in uuid encoding for ACK messages (did not seem to do any harm) --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0a2d7cde..eff90ef3 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 @@ -1706,7 +1706,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.put(APPLICATIONMESSAGE_ACK); buf.put(id); buf.putLong(uuid.getMostSignificantBits()); - buf.putLong(uuid.getMostSignificantBits()); + buf.putLong(uuid.getLeastSignificantBits()); return buf.array(); } From 38e234552db86c6adf571de8f0072318c32376d6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 13 Jan 2017 08:16:33 +0100 Subject: [PATCH 132/244] Pebble: only ACK appmessages from pebble to pebblekit android apps after the app actually sent one --- .../service/devices/pebble/PebbleIoThread.java | 14 ++++++-------- .../service/devices/pebble/PebbleProtocol.java | 8 ++++++-- 2 files changed, 12 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 923ee95c..83e26912 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 @@ -117,24 +117,22 @@ class PebbleIoThread extends GBDeviceIoThread { try { JSONArray jsonArray = new JSONArray(jsonString); write(mPebbleProtocol.encodeApplicationMessageFromJSON(uuid, jsonArray)); - sendAppMessageAck(transaction_id); - + if (transaction_id >= 0 && transaction_id <= 255) { + sendAppMessageAck(transaction_id); + } } catch (JSONException e) { e.printStackTrace(); } break; case PEBBLEKIT_ACTION_APP_ACK: - // we do not get a uuid and cannot map a transaction id to it, so we ack in PebbleProtocol early - /* - uuid = (UUID) intent.getSerializableExtra("uuid"); - int transaction_id = intent.getIntExtra("transaction_id", -1); + transaction_id = intent.getIntExtra("transaction_id", -1); if (transaction_id >= 0 && transaction_id <= 255) { - write(mPebbleProtocol.encodeApplicationMessageAck(uuid, (byte) transaction_id)); + write(mPebbleProtocol.encodeApplicationMessageAck(null, (byte) transaction_id)); } else { LOG.warn("illegal transacktion id " + transaction_id); } - */ break; + } } }; 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 eff90ef3..121cbb44 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 @@ -1698,6 +1698,9 @@ public class PebbleProtocol extends GBDeviceProtocol { } byte[] encodeApplicationMessageAck(UUID uuid, byte id) { + if (uuid == null) { + uuid = currentRunningApp; + } ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + 18); // +ACK buf.order(ByteOrder.BIG_ENDIAN); @@ -1829,14 +1832,15 @@ public class PebbleProtocol extends GBDeviceProtocol { } // this is a hack we send an ack to the Pebble immediately because we cannot map the transaction_id from the intent back to a uuid yet + /* GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); sendBytesAck.encodedBytes = encodeApplicationMessageAck(uuid, last_id); - + */ GBDeviceEventAppMessage appMessage = new GBDeviceEventAppMessage(); appMessage.appUUID = uuid; appMessage.id = last_id & 0xff; appMessage.message = jsonArray.toString(); - return new GBDeviceEvent[]{appMessage, sendBytesAck}; + return new GBDeviceEvent[]{appMessage}; } byte[] encodeApplicationMessagePush(short endpoint, UUID uuid, ArrayList> pairs) { From 1e24fa7ad811af958ed6bd22e8ffbbe464471614 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 14 Jan 2017 00:26:21 +0100 Subject: [PATCH 133/244] Dummy weather notifucation config activity --- app/src/main/AndroidManifest.xml | 2 +- .../activities/WeatherNotificationConfig.java | 15 +++++++++++ .../WeatherNotificationConfig.java | 9 ------- .../layout/activity_weather_notification.xml | 25 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WeatherNotificationConfig.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java create mode 100644 app/src/main/res/layout/activity_weather_notification.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e1b691f8..ff2b210f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -220,7 +220,7 @@ - diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WeatherNotificationConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WeatherNotificationConfig.java new file mode 100644 index 00000000..49574bed --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WeatherNotificationConfig.java @@ -0,0 +1,15 @@ +package nodomain.freeyourgadget.gadgetbridge.activities; + +import android.os.Bundle; +import android.os.PersistableBundle; +import android.widget.TextView; + +import nodomain.freeyourgadget.gadgetbridge.R; + +public class WeatherNotificationConfig extends GBActivity { + @Override + public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { + super.onCreate(savedInstanceState, persistentState); + setContentView(R.layout.activity_weather_notification); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java deleted file mode 100644 index a2b325e3..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java +++ /dev/null @@ -1,9 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.externalevents; - -import android.app.Activity; - -public class WeatherNotificationConfig extends Activity { - - //TODO: we just need the user to enable us in the weather notification settings. There must be a better way - -} diff --git a/app/src/main/res/layout/activity_weather_notification.xml b/app/src/main/res/layout/activity_weather_notification.xml new file mode 100644 index 00000000..bbb5a737 --- /dev/null +++ b/app/src/main/res/layout/activity_weather_notification.xml @@ -0,0 +1,25 @@ + + + + + + + + + + \ 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 522883d9..64b880f4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -372,4 +372,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 + Enable this style to get weather information on your Pebble.\n\nConfiguration will be possible in a later version. From c8733128311ab65ae0be05ed484656693a7c68e0 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Sat, 14 Jan 2017 02:39:36 +0300 Subject: [PATCH 134/244] Transliterate unsupported Russian characters into English --- app/build.gradle | 1 + .../service/devices/hplus/HPlusSupport.java | 49 +++++++++++++++++++ app/src/main/res/xml/changelog_master.xml | 1 + 3 files changed, 51 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index b6b68af6..7a317525 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,6 +77,7 @@ dependencies { compile 'org.apache.commons:commons-lang3:3.4' // compile project(":DaoCore") + compile 'com.google.guava:guava:19.0' } preBuild.dependsOn(":GBDaoGenerator:genSources") diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 4689155d..a674d6b7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -14,6 +14,8 @@ import android.net.Uri; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; +import com.google.common.collect.ImmutableMap; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,6 +23,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.Map; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; @@ -646,6 +649,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private void showIncomingCall(String name, String number) { try { TransactionBuilder builder = performInitialized("incomingCallIcon"); + name = transliterate(name); //Enable call notifications builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, 1}); @@ -703,6 +707,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { LOG.debug("Show Notification: "+title+" --> "+body); try { TransactionBuilder builder = performInitialized("notification"); + title = transliterate(title); + body = transliterate(body); byte[] msg = new byte[20]; for (int i = 0; i < msg.length; i++) @@ -777,6 +783,49 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } } + //replace unsupported symbols to english analog + private String transliterate(String txt){ + StringBuilder message = new StringBuilder(); + char[] chars = txt.toCharArray(); + + for (char c : chars) + { + message.append(transliterate(c)); + } + + return message.toString(); + } + + //replace unsupported symbol to english analog text + private String transliterate(char c){ + Map map = ImmutableMap.builder() + .put('а', "a").put('б', "b").put('в', "v").put('г', "g") + .put('д', "d").put('е', "e").put('ё', "jo").put('ж', "zh") + .put('з', "z").put('и', "i").put('й', "jj").put('к', "k") + .put('л', "l").put('м', "m").put('н', "n").put('о', "o") + .put('п', "p").put('р', "r").put('с', "s").put('т', "t") + .put('у', "u").put('ф', "f").put('х', "kh").put('ц', "c") + .put('ч', "ch").put('ш', "sh").put('щ', "shh").put('ъ', "\"") + .put('ы', "y").put('ь', "'").put('э', "eh").put('ю', "ju") + .put('я', "ja") + .build(); + + char lowerChar = Character.toLowerCase(c); + + if (map.containsKey(lowerChar)) { + String replace = map.get(lowerChar); + + if (lowerChar != c) + { + return replace.toUpperCase(); + } + + return replace; + } + + return String.valueOf(c); + } + @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index c54077e4..34e61a41 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -16,6 +16,7 @@ HPlus: Near real time Heart rate measurement HPlus: Experimental synchronization of activity data (only sleep, steps and intensity) HPlus: Fix some disconnection issues + HPlus: Transliterate unsupported Russian characters into English New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca From 23a1663384c63a924c5ed63ce798d63689a62fa5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 14 Jan 2017 11:16:01 +0100 Subject: [PATCH 135/244] fix wording (style -> skin) --- app/src/main/res/values/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 64b880f4..300c6b74 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -372,5 +372,6 @@ 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 - Enable this style to get weather information on your Pebble.\n\nConfiguration will be possible in a later version. + + Enable this skin to get weather information on your Pebble.\n\nConfiguration will be possible in a later version. From cb3460912fcc3feb72f904d47935e092ce4b6cab Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 14 Jan 2017 14:53:56 +0100 Subject: [PATCH 136/244] Update changelog --- CHANGELOG.md | 1 + app/src/main/res/xml/changelog_master.xml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ee0d0da..806a2f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ####Version 0.17.0 (next) * Add weather support through "Weather Notification" app * Various fixes for K9 mail when using the generic notification receiver +* Add a preference to hide the persistent notification icon of Gadgetbridge * Pebble: Support for build-in weather system app (FW 4.x) * Pebble: Add weather support for various watchfaces * Pebble: Add option to disable call display diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index c54077e4..01795f09 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,11 +2,12 @@ Add weather support through "Weather Notification" app - Various fixes for K9 mail when using the generic notification receiver + Various fixes for K9 mail when using the generic notification receiver + Add a preference to hide the notification icon of Gadgetbridge Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces Pebble: Delete notifications that got dismissed on the phone - Pebble: Add option to disable call display + Pebble: Add option to disable call display Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other) Pebble 2/LE: Improve reliablitly and transfer speed HPlus: Improved discovery and pairing From 0152e7ce02dcb58034f3a590ad5a758bfae6055d Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 14 Jan 2017 15:10:57 +0100 Subject: [PATCH 137/244] Make the text in the Weather configuration mock activity a bit more clear. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 300c6b74..e51eea29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -373,5 +373,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 - Enable this skin to get weather information on your Pebble.\n\nConfiguration will be possible in a later version. + Make sure that this skin is enabled in the Weather Notification app to get weather information on your Pebble.\n\nNo configuration is needed here.\n\nYou can enable the system weather app of your Pebble from the app management.\n\nSupported watchfaces will show the weather automatically. From 4dfef382a9ea8c632c68576e96530ee369df6696 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 14 Jan 2017 18:19:41 +0100 Subject: [PATCH 138/244] Pebble: change the overflow menu of the weather system app. If the weather notification app is not installed, link to fdroid (app if installed, web page of the app if not). If the weather notification app is installed, show the options to activate and deactivate it. --- .../appmanager/AbstractAppManagerFragment.java | 17 +++++++++++++++++ app/src/main/res/menu/appmanager_context.xml | 3 +++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 21 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 3e68b2df..946d72ae 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 @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -298,6 +299,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { if (!PebbleProtocol.UUID_WEATHER.equals(selectedApp.getUUID())) { menu.removeItem(R.id.appmanager_weather_activate); menu.removeItem(R.id.appmanager_weather_deactivate); + menu.removeItem(R.id.appmanager_weather_install_provider); } if (selectedApp.getType() == GBDeviceApp.Type.APP_SYSTEM || selectedApp.getType() == GBDeviceApp.Type.WATCHFACE_SYSTEM) { menu.removeItem(R.id.appmanager_app_delete); @@ -305,6 +307,18 @@ public abstract class AbstractAppManagerFragment extends Fragment { if (!selectedApp.isConfigurable()) { menu.removeItem(R.id.appmanager_app_configure); } + + if (PebbleProtocol.UUID_WEATHER.equals(selectedApp.getUUID())) { + PackageManager pm = getActivity().getPackageManager(); + try { + pm.getPackageInfo("ru.gelin.android.weather.notification", PackageManager.GET_ACTIVITIES); + menu.removeItem(R.id.appmanager_weather_install_provider); + } catch (PackageManager.NameNotFoundException e) { + menu.removeItem(R.id.appmanager_weather_activate); + menu.removeItem(R.id.appmanager_weather_deactivate); + } + } + switch (selectedApp.getType()) { case WATCHFACE: case APP_GENERIC: @@ -382,6 +396,9 @@ public abstract class AbstractAppManagerFragment extends Fragment { case R.id.appmanager_weather_deactivate: GBApplication.deviceService().onAppDelete(selectedApp.getUUID()); return true; + case R.id.appmanager_weather_install_provider: + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://f-droid.org/app/ru.gelin.android.weather.notification"))); + return true; case R.id.appmanager_app_configure: GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true); diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml index 766f6c5a..2f281ce8 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -27,6 +27,9 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e51eea29..cabf031a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Deactivate HRM Activate system weather app Deactivate system weather app + Install the weather notification app Configure Move to top From b9249065eb4f5fd8d9c7b9a518608b39e3dbb83c Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Sat, 14 Jan 2017 23:01:44 +0300 Subject: [PATCH 139/244] Fix for send message from debug screen --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index a674d6b7..aa183687 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -785,7 +785,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //replace unsupported symbols to english analog private String transliterate(String txt){ + if (txt == null || txt.isEmpty()) { + return txt; + } + StringBuilder message = new StringBuilder(); + char[] chars = txt.toCharArray(); for (char c : chars) From ce67bf2c520b9836f8b987b35bca563b391d4a2b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 15 Jan 2017 00:10:40 +0100 Subject: [PATCH 140/244] Pebble: make the feature to automatically delete notifications from the pebble optional (This is not pebble specific at all but as long as other devices do not use that it will stay in the Pebble specific preference screen) --- CHANGELOG.md | 4 ++-- .../gadgetbridge/externalevents/NotificationListener.java | 7 +++++-- app/src/main/res/values/strings.xml | 3 +++ app/src/main/res/xml/changelog_master.xml | 2 +- app/src/main/res/xml/preferences.xml | 5 +++++ 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 806a2f20..35f3d512 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,13 @@ ###Changelog -####Version 0.17.0 (next) +####Version 0.17.0 * Add weather support through "Weather Notification" app * Various fixes for K9 mail when using the generic notification receiver * Add a preference to hide the persistent notification icon of Gadgetbridge * Pebble: Support for build-in weather system app (FW 4.x) * Pebble: Add weather support for various watchfaces * Pebble: Add option to disable call display -* Pebble: Delete notifications that got dismissed on the phone +* Pebble: Add option to automatically delete notifications that got dismissed on the phone * Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other) * Pebble 2/LE: Improve reliablitly and transfer speed * HPlus: Improved discovery and pairing diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 80942dd9..81a41795 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -410,9 +410,12 @@ public class NotificationListener extends NotificationListenerService { return; } - LOG.info("notification removed, will ask device to delete it"); + Prefs prefs = GBApplication.getPrefs(); + if (prefs.getBoolean("autoremove_notifications", false)) { + LOG.info("notification removed, will ask device to delete it"); - GBApplication.deviceService().onDeleteNotification((int) sbn.getPostTime()); //FIMXE: a truly unique id would be better + GBApplication.deviceService().onDeleteNotification((int) sbn.getPostTime()); //FIMXE: a truly unique id would be better + } } private void dumpExtras(Bundle bundle) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cabf031a..0667156f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,6 +114,9 @@ Sunrise and Sunset Send sunrise and sunset times based on the location to the pebble timeline + Autoremove dismissed Notifications + Notifications are automatically removed from the Pebble when dismissed from the Android device + Location Acquire Location Latitude diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 01795f09..d5f774bd 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -6,7 +6,7 @@ Add a preference to hide the notification icon of Gadgetbridge Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces - Pebble: Delete notifications that got dismissed on the phone + Pebble: Add option to automatically delete notifications that got dismissed on the phone Pebble: Add option to disable call display Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other) Pebble 2/LE: Improve reliablitly and transfer speed diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 43985431..84bba8c3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -172,6 +172,11 @@ android:title="@string/pref_title_sunrise_sunset" android:summary="@string/pref_summary_sunrise_sunset" android:key="send_sunrise_sunset" /> + Date: Sun, 15 Jan 2017 00:33:54 +0100 Subject: [PATCH 141/244] fix typo --- app/src/main/res/xml/preferences.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 84bba8c3..c8f1964e 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -174,7 +174,7 @@ android:key="send_sunrise_sunset" /> From 074394cba49aacdf8d0b19df5208f042f9739a73 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Sun, 15 Jan 2017 12:24:36 +0300 Subject: [PATCH 142/244] Transliteration is moved to a separate class, added settings option --- app/build.gradle | 1 - .../service/devices/hplus/HPlusSupport.java | 65 +++---------------- .../gadgetbridge/util/LanguageUtils.java | 64 ++++++++++++++++++ app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/changelog_master.xml | 3 +- app/src/main/res/xml/preferences.xml | 7 ++ 7 files changed, 87 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java diff --git a/app/build.gradle b/app/build.gradle index 7a317525..b6b68af6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,6 @@ dependencies { compile 'org.apache.commons:commons-lang3:3.4' // compile project(":DaoCore") - compile 'com.google.guava:guava:19.0' } preBuild.dependsOn(":GBDaoGenerator:genSources") diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index aa183687..319303db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -14,8 +14,6 @@ import android.net.Uri; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; -import com.google.common.collect.ImmutableMap; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +21,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; -import java.util.Map; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; @@ -41,10 +38,10 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSuppo import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; -import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; public class HPlusSupport extends AbstractBTLEDeviceSupport { @@ -649,7 +646,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private void showIncomingCall(String name, String number) { try { TransactionBuilder builder = performInitialized("incomingCallIcon"); - name = transliterate(name); + + if (LanguageUtils.transliterate()) { + name = LanguageUtils.transliterate(name); + } //Enable call notifications builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, 1}); @@ -707,8 +707,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { LOG.debug("Show Notification: "+title+" --> "+body); try { TransactionBuilder builder = performInitialized("notification"); - title = transliterate(title); - body = transliterate(body); + + if (LanguageUtils.transliterate()) { + title = LanguageUtils.transliterate(title); + body = LanguageUtils.transliterate(body); + } byte[] msg = new byte[20]; for (int i = 0; i < msg.length; i++) @@ -783,54 +786,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } } - //replace unsupported symbols to english analog - private String transliterate(String txt){ - if (txt == null || txt.isEmpty()) { - return txt; - } - - StringBuilder message = new StringBuilder(); - - char[] chars = txt.toCharArray(); - - for (char c : chars) - { - message.append(transliterate(c)); - } - - return message.toString(); - } - - //replace unsupported symbol to english analog text - private String transliterate(char c){ - Map map = ImmutableMap.builder() - .put('а', "a").put('б', "b").put('в', "v").put('г', "g") - .put('д', "d").put('е', "e").put('ё', "jo").put('ж', "zh") - .put('з', "z").put('и', "i").put('й', "jj").put('к', "k") - .put('л', "l").put('м', "m").put('н', "n").put('о', "o") - .put('п', "p").put('р', "r").put('с', "s").put('т', "t") - .put('у', "u").put('ф', "f").put('х', "kh").put('ц', "c") - .put('ч', "ch").put('ш', "sh").put('щ', "shh").put('ъ', "\"") - .put('ы', "y").put('ь', "'").put('э', "eh").put('ю', "ju") - .put('я', "ja") - .build(); - - char lowerChar = Character.toLowerCase(c); - - if (map.containsKey(lowerChar)) { - String replace = map.get(lowerChar); - - if (lowerChar != c) - { - return replace.toUpperCase(); - } - - return replace; - } - - return String.valueOf(c); - } - @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java new file mode 100644 index 00000000..ae41ed3f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -0,0 +1,64 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +import java.util.HashMap; +import java.util.Map; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; + +public class LanguageUtils { + //transliteration map with english equivalent for unsupported chars + private static Map transliterateMap = new HashMap(){ + { + //russian chars + put('а', "a"); put('б', "b"); put('в', "v"); put('г', "g"); put('д', "d"); put('е', "e"); put('ё', "jo"); put('ж', "zh"); + put('з', "z"); put('и', "i"); put('й', "jj"); put('к', "k"); put('л', "l"); put('м', "m"); put('н', "n"); put('о', "o"); + put('п', "p"); put('р', "r"); put('с', "s"); put('т', "t"); put('у', "u"); put('ф', "f"); put('х', "kh"); put('ц', "c"); + put('ч', "ch");put('ш', "sh");put('щ', "shh");put('ъ', "\"");put('ы', "y"); put('ь', "'"); put('э', "eh"); put('ю', "ju"); + put('я', "ja"); + + //continue for other languages... + } + }; + + //check transliterate option status + public static boolean transliterate() + { + return GBApplication.getPrefs().getBoolean("transliteration", true); + } + + //replace unsupported symbols to english analog + public static String transliterate(String txt){ + if (txt == null || txt.isEmpty()) { + return txt; + } + + StringBuilder message = new StringBuilder(); + + char[] chars = txt.toCharArray(); + + for (char c : chars) + { + message.append(transliterate(c)); + } + + return message.toString(); + } + + //replace unsupported symbol to english analog text + private static String transliterate(char c){ + char lowerChar = Character.toLowerCase(c); + + if (transliterateMap.containsKey(lowerChar)) { + String replace = transliterateMap.get(lowerChar); + + if (lowerChar != c) + { + return replace.toUpperCase(); + } + + return replace; + } + + return String.valueOf(c); + } +} diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7284db28..2ed467a6 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -52,6 +52,8 @@ … даже когда экран включён Не беспокоить Предотвращать отправку нежелательных уведомлений в режиме \"Не беспокоить\" + Транслитерация + Используйте, если устройство не может работать с вашим языком всегда когда экран выключен никогда diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 522883d9..765cc461 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,6 +80,8 @@ … also when screen is on Do Not Disturb Stop unwanted Notifications from being sent based on the Do Not Disturb mode. + Transliteration + Use, if the device can not work with your language always when screen is off diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 34e61a41..672517f6 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,7 +2,8 @@ Add weather support through "Weather Notification" app - Various fixes for K9 mail when using the generic notification receiver + Various fixes for K9 mail when using the generic notification receiver + Added transliteration option for notifications in the settings screen Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces Pebble: Delete notifications that got dismissed on the phone diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 43985431..48eb91d8 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -91,6 +91,13 @@ android:key="notifications_generic_whenscreenon" android:title="@string/pref_title_whenscreenon" /> + + Date: Sun, 15 Jan 2017 13:41:38 +0300 Subject: [PATCH 143/244] StyleCop --- 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 6dac6eb9..8544188a 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,7 +2,7 @@ Add weather support through "Weather Notification" app - Various fixes for K9 mail when using the generic notification receiver + Various fixes for K9 mail when using the generic notification receiver Added transliteration option for notifications in the settings screen Add a preference to hide the notification icon of Gadgetbridge From d9d153c4631c2bd3823e1ca234c97fe81b5d49da Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 15 Jan 2017 12:43:26 +0100 Subject: [PATCH 144/244] move WeatherNotificationConfig.java to its previous location to fix a crash --- app/src/main/AndroidManifest.xml | 2 +- .../WeatherNotificationConfig.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/{activities => externalevents}/WeatherNotificationConfig.java (77%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ff2b210f..e1b691f8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -220,7 +220,7 @@ - diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WeatherNotificationConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java similarity index 77% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WeatherNotificationConfig.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java index 49574bed..9d965de1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WeatherNotificationConfig.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java @@ -1,10 +1,11 @@ -package nodomain.freeyourgadget.gadgetbridge.activities; +package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.os.Bundle; import android.os.PersistableBundle; import android.widget.TextView; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; public class WeatherNotificationConfig extends GBActivity { @Override From 26a349210e0231370a8deb7f66268e0586fe3937 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 15 Jan 2017 18:19:30 +0100 Subject: [PATCH 145/244] Pebble: make the text in the dummy weather configuration activity visible. --- app/src/main/AndroidManifest.xml | 3 +-- .../externalevents/WeatherNotificationConfig.java | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e1b691f8..ceb2ac0b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -220,8 +220,7 @@ - + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java index 9d965de1..2901a15e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java @@ -1,16 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.os.Bundle; -import android.os.PersistableBundle; -import android.widget.TextView; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; public class WeatherNotificationConfig extends GBActivity { @Override - public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { - super.onCreate(savedInstanceState, persistentState); + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); setContentView(R.layout.activity_weather_notification); } } From 2de9580dea802a77c37d3335d4a92f8f625c9660 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Sun, 15 Jan 2017 22:10:12 +0300 Subject: [PATCH 146/244] Added diacritic convertation into Transliteration --- .../gadgetbridge/util/LanguageUtils.java | 19 +++++++++++++++++-- app/src/main/res/xml/changelog_master.xml | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index ae41ed3f..a9a80aae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -2,13 +2,16 @@ package nodomain.freeyourgadget.gadgetbridge.util; import java.util.HashMap; import java.util.Map; - +import java.text.Normalizer; import nodomain.freeyourgadget.gadgetbridge.GBApplication; public class LanguageUtils { //transliteration map with english equivalent for unsupported chars private static Map transliterateMap = new HashMap(){ { + //extended ASCII characters + put('æ', "ae"); put('œ', "oe"); put('ß', "B"); + //russian chars put('а', "a"); put('б', "b"); put('в', "v"); put('г', "g"); put('д', "d"); put('е', "e"); put('ё', "jo"); put('ж', "zh"); put('з', "z"); put('и', "i"); put('й', "jj"); put('к', "k"); put('л', "l"); put('м', "m"); put('н', "n"); put('о', "o"); @@ -41,7 +44,7 @@ public class LanguageUtils { message.append(transliterate(c)); } - return message.toString(); + return flattenToAscii(message.toString()); } //replace unsupported symbol to english analog text @@ -61,4 +64,16 @@ public class LanguageUtils { return String.valueOf(c); } + + //convert diacritic + private static String flattenToAscii(String string) { + char[] out = new char[string.length()]; + string = Normalizer.normalize(string, Normalizer.Form.NFD); + int j = 0; + for (int i = 0, n = string.length(); i < n; ++i) { + char c = string.charAt(i); + if (c <= '\u007F') out[j++] = c; + } + return new String(out); + } } diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 8544188a..d4b9c14b 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,7 +2,7 @@ Add weather support through "Weather Notification" app - Various fixes for K9 mail when using the generic notification receiver + Various fixes for K9 mail when using the generic notification receiver Added transliteration option for notifications in the settings screen Add a preference to hide the notification icon of Gadgetbridge From bfe24dd9f0356ad571cde123c5810865a2207f9e Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Sun, 15 Jan 2017 22:46:30 +0300 Subject: [PATCH 147/244] Refactoring --- .../gadgetbridge/util/LanguageUtils.java | 10 ++-------- app/src/main/res/xml/changelog_master.xml | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index a9a80aae..735dfd1f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -10,7 +10,7 @@ public class LanguageUtils { private static Map transliterateMap = new HashMap(){ { //extended ASCII characters - put('æ', "ae"); put('œ', "oe"); put('ß', "B"); + put('æ', "ae"); put('œ', "oe"); put('ß', "B"); put('ª', "a"); put('º', "o"); //russian chars put('а', "a"); put('б', "b"); put('в', "v"); put('г', "g"); put('д', "d"); put('е', "e"); put('ё', "jo"); put('ж', "zh"); @@ -67,13 +67,7 @@ public class LanguageUtils { //convert diacritic private static String flattenToAscii(String string) { - char[] out = new char[string.length()]; string = Normalizer.normalize(string, Normalizer.Form.NFD); - int j = 0; - for (int i = 0, n = string.length(); i < n; ++i) { - char c = string.charAt(i); - if (c <= '\u007F') out[j++] = c; - } - return new String(out); + return string.replaceAll("\\p{M}", ""); } } diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index d4b9c14b..8e71c9a0 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,7 +2,7 @@ Add weather support through "Weather Notification" app - Various fixes for K9 mail when using the generic notification receiver + Various fixes for K9 mail when using the generic notification receiver Added transliteration option for notifications in the settings screen Add a preference to hide the notification icon of Gadgetbridge From f19541c6548321028872675f60726bd3a5d66de6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 15 Jan 2017 21:43:06 +0100 Subject: [PATCH 148/244] update translations from transifex (minus French patient) --- app/src/main/res/values-es/strings.xml | 9 +++++++++ app/src/main/res/values-it/strings.xml | 23 +++++++++++++++++++++++ app/src/main/res/values-ja/strings.xml | 9 +++++++++ 3 files changed, 41 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1c613394..1acab334 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -29,6 +29,7 @@ Desactivar Monitor de Ritmo Cardíaco Activar la aplicación del tiempo del sistema Desactivar la aplicación del tiempo del sistema + Instalar la aplicación de notificación del tiempo Configurar Mover a la parte de arriba @@ -54,6 +55,9 @@ Claro Oscuro Idioma + Ocultar la notificación de Gadgetbridge + El icono en la barra de estado y la notificación en la pantalla de bloqueo están activos + El icono en la barra de estado y la notificación en la pantalla de bloqueo están ocultos Notificaciones Repeticiones Llamadas telefónicas @@ -82,10 +86,14 @@ Sincronizar con Pebble Health Sincronizar con Misfit Sincronizar con Morpheuz + Soporte para llamadas salientes + Desactivar esto evitará que el Pebble 2/LE vibre en llamadas salientes Permitir el acceso a aplicaciones Android de terceros Permitir el soporte experimental para aplicaciones Android que usan PebbleKit Salida y puesta de Sol Enviar las horas de salida y puesta de Sol basándose en la localización a la línea cronológica del Pebble + Eliminar automáticamente las notificaciones rechazadas + Las notificaciones se borran automáticamente de Pebble cuando son rechazadas en Android Localización Buscar localización Latitud @@ -327,4 +335,5 @@ Por favor, ten en cuenta que puedes importar datos desde Mi Band, Pebble Health Emparejando con Pebble En su dispositivo Android va a aparecer un mensaje para emparejarse. Si no apareciera, mira en el cajón de notificaciones y acepta la propuesta de emparejamiento. Después acepta también en tu Pebble. + Asegúrate de que este tema esté activado en la aplicación de notificación del tiempo para obtener la información en tu Pebble.\n\nNo se requiere configuración.\n\nPuedes activar la aplicación del tiempo del sistema desde la configuración de la app.\n\nLas watchfaces soportadas mostrarán la información del tiempo automáticamente. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 288bd93d..80fe8241 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -22,8 +22,13 @@ Cancella Cancella e rimuovi dalla cache Re-installazione + Cerca nell\'appstore di Pebble Attiva Disattiva + Attiva il monitor del battito cardiaco + Disattiva il monitor del battito cardiaco + Attiva l\'applicazione meteo + Disattiva l\'applicazione meteo Configura Sposta in cima @@ -49,6 +54,9 @@ Chiaro Scuro Lingua + Nascondi la notifica di gadgetbridge + L\'icona nella barra di stato e la notifica nella schermata di blocco vengono mostrate + L\'icona nella barra di stato e la notifica nella schermata di blocco non vengono mostrate Notifiche Ripetizioni Chiamate telefoniche @@ -77,6 +85,8 @@ Sincronizza Pebble Health Sincronizza Misfit Sincronizza Morpheuz + Mostra le chiamate in uscita + Disabilitando questa funzionalità impedirá la vibrazione del Pebble 2 quando si effettua una chiamata Consenti accesso ad altre applicazioni Attiva l\'accesso sperimentale ad applicazioni Android che usano PebbleKit Alba e tramonto @@ -85,12 +95,20 @@ Acquisisci posizione Latitudine Longitudine + Mantieni aggiornata la posizion + Cerca di ottenere la posizione aggiornata durante l\'utilizzo, usa quella memorizzata come backup Per cortesia abilita la localizzazione utilizzando la rete posizione acquisita Forza protocollo delle notifiche Questa opzione forza l\'utilizzo della versione più recente delle notifiche in dipendenza del firmware del tuo dispositivo. ABILITALO SOLO SE SAI COSA STAI FACENDO! Abilita funzionalità non testate Abilita funzionalità non testate. ABILITARE SOLO SE SI SA QUELLO CHE SI STA FACENDO! + Usa sempre BLE + Utilizza il supporto sperimentale Pebble BLE per tutti i Pebble. Si devei effettuare il \"pairing\" nuovamente se si dovesse utilizzare di nuovo il BT classico + Limita la MTU del Pebble 2/LE + Se il tuo Pebble 2/LE non funziona come dovrebbe, prova a impostare un limite alla MTU (range valido 20-512) + Abilita il log delle applicazioni che girano su Pebble + Il log delle applicazioni che girano su Pebble verrà aggiunto a quello di Gadgetbridge (richiede riconessione) Tentativi di riconessione non connesso in collegamento @@ -163,6 +181,9 @@ Notifiche generiche Notifiche Email Notifiche chiamata in arrivo + Cha + Navigazione + Social Network Trova dispositivo smarrito Annulla per fermare la vibrazione Le tue attività @@ -236,6 +257,8 @@ Sveglie da riservare per i prossimi eventi del calendario Utilizza il sensore del battito cardiaco per migliorare il riconoscimento del sonno Sfasamento dell\'orario per il device (per consentire il rilevamento del sonno per chi lavora a turni) + Mi2: formato della data + Ora in attesa di riconnessione Informazioni sull\'utilizzatore Anno di nascita diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 09222a91..c5daf71f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -29,6 +29,7 @@ HRM を非アクティベート システムの天気アプリを有効にする システムの天気アプリを無効にする + 天気予報アプリをインストール 設定 先頭に移動 @@ -54,6 +55,9 @@ ライト ダーク 言語 + ガジェットブリッジの通知を非表示 + ステータスバーのアイコンとロック画面の通知が表示されます + ステータスバーのアイコンとロック画面の通知は非表示です 通知 繰り返し 電話通知 @@ -82,10 +86,14 @@ Pebble Health 同期 Misfit 同期 Morpheuz 同期 + 発信のサポート + これを無効にすると、Pebble 2/LE は発信時に振動しなくなります 第三者のアンドロイドアップにアクセス権利を与える PebbleKitを使用してAndroidアプリ用の実験的なサポートを有効にします 日の出と日の入り 場所に基づいて、Pebble のタイムラインに日の出・日の入りの時間を送ります + 消去した通知を自動削除 + 通知は、Androidデバイスから削除されたときに、Pebbleから自動的に削除されます 場所 場所の取得 緯度 @@ -327,4 +335,5 @@ Mi Band、Pebble Health、Morpheuz からデータをインポートすること Pebbleペアリング お使いのAndroidデバイスでペアリングのダイアログがポップアップすると思います。 起こらない場合は、通知ドロワーを調べて、ペアリング要求を受け入れます。 その後、Pebbleでペアリング要求を受け入れます + 天気予報アプリでこのスキンが有効になっていることを確認してください。\n\nここで必要な設定はありません。\n\nアプリ管理からPebbleのシステム天気アプリを有効にすることができます。\n\nサポートされているウォッチフェイスが自動的に天気を表示します。 From 5222cf99a21da0414dd49d54661d92813e1c6a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Mon, 16 Jan 2017 22:04:52 +0000 Subject: [PATCH 149/244] HPlus: fixed bug setting current date --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 4689155d..3575e8c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -211,7 +211,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setCurrentDate(TransactionBuilder transaction) { Calendar c = GregorianCalendar.getInstance(); - int year = c.get(Calendar.YEAR) - 1900; + int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH); int day = c.get(Calendar.DAY_OF_MONTH); From cbc91e7fef7856379bf3b08a871478490e04e820 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Tue, 17 Jan 2017 23:07:12 +0300 Subject: [PATCH 150/244] Moving transliteration call from module "HPlus" to common support --- .../service/DeviceCommunicationService.java | 41 +++++++++++++------ .../service/devices/hplus/HPlusSupport.java | 10 ----- .../gadgetbridge/util/LanguageUtils.java | 2 +- 3 files changed, 29 insertions(+), 24 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 0edf9171..da4fa9a0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -51,6 +51,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; +import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ADD_CALENDAREVENT; @@ -322,21 +323,23 @@ public class DeviceCommunicationService extends Service implements SharedPrefere break; case ACTION_NOTIFICATION: { NotificationSpec notificationSpec = new NotificationSpec(); - notificationSpec.phoneNumber = intent.getStringExtra(EXTRA_NOTIFICATION_PHONENUMBER); - notificationSpec.sender = intent.getStringExtra(EXTRA_NOTIFICATION_SENDER); - notificationSpec.subject = intent.getStringExtra(EXTRA_NOTIFICATION_SUBJECT); - notificationSpec.title = intent.getStringExtra(EXTRA_NOTIFICATION_TITLE); - notificationSpec.body = intent.getStringExtra(EXTRA_NOTIFICATION_BODY); + notificationSpec.phoneNumber = getStringExtra(intent, EXTRA_NOTIFICATION_PHONENUMBER); + notificationSpec.sender = getStringExtra(intent, EXTRA_NOTIFICATION_SENDER); + notificationSpec.subject = getStringExtra(intent, EXTRA_NOTIFICATION_SUBJECT); + notificationSpec.title = getStringExtra(intent, EXTRA_NOTIFICATION_TITLE); + notificationSpec.body = getStringExtra(intent, EXTRA_NOTIFICATION_BODY); + notificationSpec.sourceName = getStringExtra(intent, EXTRA_NOTIFICATION_SOURCENAME); notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE); notificationSpec.id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1); notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0); - notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME); + if (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null) { notificationSpec.sender = getContactDisplayNameByNumber(notificationSpec.phoneNumber); notificationSpec.id = mRandom.nextInt(); // FIXME: add this in external SMS Receiver? GBApplication.getIDSenderLookup().add(notificationSpec.id, notificationSpec.phoneNumber); } + if (((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_REPLY) > 0) || (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null)) { // NOTE: maybe not where it belongs @@ -353,6 +356,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere notificationSpec.cannedReplies = replies.toArray(new String[replies.size()]); } } + mDeviceSupport.onNotification(notificationSpec); break; } @@ -366,8 +370,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere calendarEventSpec.type = intent.getByteExtra(EXTRA_CALENDAREVENT_TYPE, (byte) -1); calendarEventSpec.timestamp = intent.getIntExtra(EXTRA_CALENDAREVENT_TIMESTAMP, -1); calendarEventSpec.durationInSeconds = intent.getIntExtra(EXTRA_CALENDAREVENT_DURATION, -1); - calendarEventSpec.title = intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE); - calendarEventSpec.description = intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION); + calendarEventSpec.title = getStringExtra(intent, EXTRA_CALENDAREVENT_TITLE); + calendarEventSpec.description = getStringExtra(intent, EXTRA_CALENDAREVENT_DESCRIPTION); mDeviceSupport.onAddCalendarEvent(calendarEventSpec); break; } @@ -412,7 +416,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere case ACTION_CALLSTATE: int command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED); - String phoneNumber = intent.getStringExtra(EXTRA_CALL_PHONENUMBER); + String phoneNumber = getStringExtra(intent, EXTRA_CALL_PHONENUMBER); String callerName = null; if (phoneNumber != null) { callerName = getContactDisplayNameByNumber(phoneNumber); @@ -438,9 +442,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere break; case ACTION_SETMUSICINFO: MusicSpec musicSpec = new MusicSpec(); - musicSpec.artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST); - musicSpec.album = intent.getStringExtra(EXTRA_MUSIC_ALBUM); - musicSpec.track = intent.getStringExtra(EXTRA_MUSIC_TRACK); + musicSpec.artist = getStringExtra(intent, EXTRA_MUSIC_ARTIST); + musicSpec.album = getStringExtra(intent, EXTRA_MUSIC_ALBUM); + musicSpec.track = getStringExtra(intent, EXTRA_MUSIC_TRACK); musicSpec.duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0); musicSpec.trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0); musicSpec.trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0); @@ -703,7 +707,18 @@ public class DeviceCommunicationService extends Service implements SharedPrefere // ignore, just return name below } - return name; + return LanguageUtils.transliterate() + ? LanguageUtils.transliterate(name) + : name; + } + + //Standard method with transliteration + private String getStringExtra(Intent intent, String event){ + String extra = intent.getStringExtra(event); + + return LanguageUtils.transliterate() + ? LanguageUtils.transliterate(extra) + : extra; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 319303db..8335830f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -41,7 +41,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateA import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.util.GB; -import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; public class HPlusSupport extends AbstractBTLEDeviceSupport { @@ -647,10 +646,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { try { TransactionBuilder builder = performInitialized("incomingCallIcon"); - if (LanguageUtils.transliterate()) { - name = LanguageUtils.transliterate(name); - } - //Enable call notifications builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, 1}); @@ -708,11 +703,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { try { TransactionBuilder builder = performInitialized("notification"); - if (LanguageUtils.transliterate()) { - title = LanguageUtils.transliterate(title); - body = LanguageUtils.transliterate(body); - } - byte[] msg = new byte[20]; for (int i = 0; i < msg.length; i++) msg[i] = ' '; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index 735dfd1f..48359d09 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -10,7 +10,7 @@ public class LanguageUtils { private static Map transliterateMap = new HashMap(){ { //extended ASCII characters - put('æ', "ae"); put('œ', "oe"); put('ß', "B"); put('ª', "a"); put('º', "o"); + put('æ', "ae"); put('œ', "oe"); put('ß', "B"); put('ª', "a"); put('º', "o"); put('«',"\""); put('»',"\""); //russian chars put('а', "a"); put('б', "b"); put('в', "v"); put('г', "g"); put('д', "d"); put('е', "e"); put('ё', "jo"); put('ж', "zh"); From 0094805359cd99e087318fc3e342f85782835cd0 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Tue, 17 Jan 2017 23:24:03 +0300 Subject: [PATCH 151/244] ChangeLog --- app/src/main/res/xml/changelog_master.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 8e71c9a0..6f3a0a33 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -3,7 +3,8 @@ Add weather support through "Weather Notification" app Various fixes for K9 mail when using the generic notification receiver - Added transliteration option for notifications in the settings screen + Added transliteration support for Russian characters into English + Added transliteration option in the settings screen Add a preference to hide the notification icon of Gadgetbridge Pebble: Support for build-in weather system app (FW 4.x) @@ -19,7 +20,6 @@ HPlus: Near real time Heart rate measurement HPlus: Experimental synchronization of activity data (only sleep, steps and intensity) HPlus: Fix some disconnection issues - HPlus: Transliterate unsupported Russian characters into English New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca From ed020c2a97b243837cf2f6efb286257364b2ccac Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 18 Jan 2017 21:47:15 +0100 Subject: [PATCH 152/244] Pebble: raise limit of appinfo.json. Some pbws have huge ones :/ Fixes #505 --- .../gadgetbridge/devices/pebble/PBWReader.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index 50f8ef9d..a045079e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -145,8 +145,11 @@ public class PBWReader { } else if (fileName.equals("appinfo.json")) { long bytes = ze.getSize(); - if (bytes > 65536) // that should be too much + if (bytes > 500000) { + LOG.warn(fileName + " exeeds maximum of 500000 bytes"); + // that should be too much break; + } ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((count = zis.read(buffer)) != -1) { From 26a751977e63e391c54c62c21808cffc1980e141 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 18 Jan 2017 22:10:10 +0100 Subject: [PATCH 153/244] Pebble: try to improve PebbleKit compatibility (Might help with glance #506) --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 121cbb44..b271a0bf 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 @@ -1910,8 +1910,8 @@ public class PebbleProtocol extends GBDeviceProtocol { try { JSONObject jsonObject = (JSONObject) jsonArray.get(i); String type = (String) jsonObject.get("type"); - int key = (int) jsonObject.get("key"); - int length = (int) jsonObject.get("length"); + int key = jsonObject.getInt("key"); + int length = jsonObject.getInt("length"); switch (type) { case "uint": case "int": From cf45c665a5227cbaf62842d36c7d15491c4e7813 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 18 Jan 2017 22:17:59 +0100 Subject: [PATCH 154/244] bump version update CHANGELOG --- CHANGELOG.md | 5 +++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35f3d512..86b321c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ###Changelog +####Version 0.17.1 +* Pebble: Fix installation of some watchapps +* Pebble: Try to improve PebbleKit compatibility +* HPlus: Fix bug setting current date + ####Version 0.17.0 * Add weather support through "Weather Notification" app * Various fixes for K9 mail when using the generic notification receiver diff --git a/app/build.gradle b/app/build.gradle index b6b68af6..23646f82 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.17.0" - versionCode 81 + versionName "0.17.1" + versionCode 82 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index d5f774bd..1d0c0a2b 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,10 @@ + + Pebble: Fix installation of some watchapps + Pebble: Try to improve PebbleKit compatibility + HPlus: Fix bug setting current date + Add weather support through "Weather Notification" app Various fixes for K9 mail when using the generic notification receiver From c13725911f3cd01f2b05f7a9529617d81092db8a Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Thu, 19 Jan 2017 08:09:36 +0300 Subject: [PATCH 155/244] Transliteration off by default, if setting not exist --- .../freeyourgadget/gadgetbridge/util/LanguageUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index 48359d09..59d7aa81 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -26,7 +26,7 @@ public class LanguageUtils { //check transliterate option status public static boolean transliterate() { - return GBApplication.getPrefs().getBoolean("transliteration", true); + return GBApplication.getPrefs().getBoolean("transliteration", false); } //replace unsupported symbols to english analog From befedaf7d2165e63d6a9a16c2d83aee03815319a Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Fri, 20 Jan 2017 15:33:27 +0100 Subject: [PATCH 156/244] Added "known issues" item for Mi Band devices about firmware version related issues. #508 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91b7f3ef..dac25b36 100644 --- a/README.md +++ b/README.md @@ -108,14 +108,16 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/ 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: +**Known Issues:** * The initial connection to a Mi Band sometimes takes a little patience. Try to connect a few times, wait, and try connecting again. This only happens until you have "bonded" with the Mi Band, i.e. until it knows your MAC address. This behavior may also only occur with older firmware versions. * If you use other apps like Mi Fit, and "bonding" with Gadgetbridge does not work, please try to unpair the band in the other app and try again with Gadgetbridge. - +* While all Mi Band devices are supported, some firmware versions might work better than others. + You can consult the [projects wiki pages](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band) + to check if your firmware version is fully supported or if an upgrade/downgrade might be beneficial. ## Features (Liveview) From a7a37fd9c8841d0e41879222bd4c13b4020d20ab Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 20 Jan 2017 19:17:00 +0100 Subject: [PATCH 157/244] Pebble: add a method to use the JSON keys instead of hardcoding the key ID. This needs parsing the json but it is only done once. So far only Timestyle apphandler uses the new approach and this fi_xes the issue reported here https://github.com/Freeyourgadget/Gadgetbridge/issues/482#issuecomment-273757492 Fixes also a potential crash when the message for pebble contained a null key in one of the Pairs --- CHANGELOG.md | 3 ++ .../devices/pebble/AppMessageHandler.java | 19 +++++++ .../AppMessageHandlerTimeStylePebble.java | 50 +++++++++++++------ .../devices/pebble/PebbleProtocol.java | 2 + app/src/main/res/xml/changelog_master.xml | 3 ++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86b321c0..c69264c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version next +* Pebble: Fix temperature unit in Timestyle Pebble watchface + ####Version 0.17.1 * Pebble: Fix installation of some watchapps * Pebble: Try to improve PebbleKit compatibility diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java index 0e0ae701..8705cb23 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java @@ -3,17 +3,25 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.IOException; import java.util.ArrayList; +import java.util.Map; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; class AppMessageHandler { final PebbleProtocol mPebbleProtocol; final UUID mUUID; + protected Map messageKeys; AppMessageHandler(UUID uuid, PebbleProtocol pebbleProtocol) { mUUID = uuid; @@ -46,4 +54,15 @@ class AppMessageHandler { protected GBDevice getDevice() { return mPebbleProtocol.getDevice(); } + + protected JSONObject getAppKeys() throws IOException, JSONException { + File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); + File configurationFile = new File(destDir, mUUID.toString() + ".json"); + if (configurationFile.exists()) { + String jsonstring = FileUtils.getStringFromFile(configurationFile); + JSONObject json = new JSONObject(jsonstring); + return json.getJSONObject("appKeys"); + } + throw new IOException(); + } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 25d995ab..6ca62975 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -1,23 +1,24 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; +import android.widget.Toast; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.util.GB; class AppMessageHandlerTimeStylePebble extends AppMessageHandler { - private static final int MESSAGE_KEY_WeatherCondition = 10000; - private static final int MESSAGE_KEY_WeatherForecastCondition = 10002; - private static final int MESSAGE_KEY_WeatherForecastHighTemp = 10003; - private static final int MESSAGE_KEY_WeatherForecastLowTemp = 10004; - private static final int MESSAGE_KEY_WeatherTemperature = 10001; - private static final int MESSAGE_KEY_WeatherUseNightIcon = 10025; - private static final int ICON_CLEAR_DAY = 0; private static final int ICON_CLEAR_NIGHT = 1; @@ -34,6 +35,27 @@ class AppMessageHandlerTimeStylePebble extends AppMessageHandler { AppMessageHandlerTimeStylePebble(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); + messageKeys = new HashMap<>(); + try { + JSONObject appKeys = getAppKeys(); + Iterator appKeysIterator = appKeys.keys(); + while (appKeysIterator.hasNext()) { + String current = appKeysIterator.next(); + switch (current) { + case "WeatherCondition": + case "WeatherForecastCondition": + case "WeatherForecastHighTemp": + case "WeatherForecastLowTemp": + case "WeatherTemperature": + case "SettingUseMetric": + case "WeatherUseNightIcon": + messageKeys.put(current, appKeys.getInt(current)); + break; + } + } + } catch (IOException | JSONException e) { + GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } } /* @@ -98,13 +120,13 @@ class AppMessageHandlerTimeStylePebble extends AppMessageHandler { ArrayList> pairs = new ArrayList<>(); boolean isNight = false; //TODO: use the night icons when night - pairs.add(new Pair<>(MESSAGE_KEY_WeatherUseNightIcon, (Object) (isNight ? 1 : 0))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherTemperature, (Object) (weatherSpec.currentTemp - 273))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherCondition, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode, isNight)))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastCondition, (Object) (getIconForConditionCode(weatherSpec.tomorrowConditionCode, isNight)))); - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastHighTemp, (Object) (weatherSpec.todayMaxTemp - 273))); - - pairs.add(new Pair<>(MESSAGE_KEY_WeatherForecastLowTemp, (Object) (weatherSpec.todayMinTemp - 273))); + pairs.add(new Pair<>(messageKeys.get("SettingUseMetric"), (Object) 1)); //celsius + pairs.add(new Pair<>(messageKeys.get("WeatherUseNightIcon"), (Object) (isNight ? 1 : 0))); + pairs.add(new Pair<>(messageKeys.get("WeatherTemperature"), (Object) (weatherSpec.currentTemp - 273))); + pairs.add(new Pair<>(messageKeys.get("WeatherCondition"), (Object) (getIconForConditionCode(weatherSpec.currentConditionCode, isNight)))); + pairs.add(new Pair<>(messageKeys.get("WeatherForecastCondition"), (Object) (getIconForConditionCode(weatherSpec.tomorrowConditionCode, isNight)))); + pairs.add(new Pair<>(messageKeys.get("WeatherForecastHighTemp"), (Object) (weatherSpec.todayMaxTemp - 273))); + pairs.add(new Pair<>(messageKeys.get("WeatherForecastLowTemp"), (Object) (weatherSpec.todayMinTemp - 273))); return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); } 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 b271a0bf..6ef013bb 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 @@ -1874,6 +1874,8 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.order(ByteOrder.LITTLE_ENDIAN); for (Pair pair : pairs) { + if (pair.first == null || pair.second == null) + continue; buf.putInt(pair.first); if (pair.second instanceof Integer) { buf.put(TYPE_INT); diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index fe8e9179..99edf2ac 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + Pebble: Fix temperature unit in Timestyle Pebble watchface + Pebble: Fix installation of some watchapps Pebble: Try to improve PebbleKit compatibility From 373e96ca300b0ee77a7e22e2868850f09db9543f Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 22 Jan 2017 16:53:26 +0100 Subject: [PATCH 158/244] Fix formatting of issue template, add GB version h/t @IzzySoft --- .github/ISSUE_TEMPLATE.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 51177558..3d46d303 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,8 +1,10 @@ -####Your issue is: +#### Your issue is: *In case of a bug, do not forget to attach logs!* -####Your wearable device is: +#### Your wearable device is: *Please specify model and firmware version if possible* -####Your android version is: +#### Your android version is: + +#### Your Gadgetbridge version is: From c39318af058fada27b51333c064d1a77df7046bd Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 22 Jan 2017 22:30:40 +0100 Subject: [PATCH 159/244] CHANGELOG, bump version, improve strings about transliteration --- CHANGELOG.md | 3 ++- app/build.gradle | 4 ++-- .../gadgetbridge/service/DeviceCommunicationService.java | 1 + app/src/main/res/values/strings.xml | 2 +- app/src/main/res/xml/changelog_master.xml | 6 ++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c69264c8..5abc7e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ###Changelog -####Version next +####Version 0.17.2 * Pebble: Fix temperature unit in Timestyle Pebble watchface +* Add optional Cyrillic transliteration (for devices lacking the font) ####Version 0.17.1 * Pebble: Fix installation of some watchapps diff --git a/app/build.gradle b/app/build.gradle index 23646f82..17ca06fd 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.17.1" - versionCode 82 + versionName "0.17.2" + versionCode 83 } buildTypes { release { 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 da4fa9a0..b2d9be35 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -178,6 +178,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere String action = intent.getAction(); if (action.equals(GBDevice.ACTION_DEVICE_CHANGED)) { GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE); + // FIXME: mGBDevice was null here once if (mGBDevice.equals(device)) { mGBDevice = device; boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized()); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e219e610..2af54dfc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,7 +82,7 @@ Do Not Disturb Stop unwanted Notifications from being sent based on the Do Not Disturb mode. Transliteration - Use, if the device can not work with your language + Use, if you device has no support for your language's font (Currently Cyrillic only) always when screen is off diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 99edf2ac..1c84aab9 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,7 +1,8 @@ - + Pebble: Fix temperature unit in Timestyle Pebble watchface + Add optional Cyrillic transliteration (for devices lacking the font) Pebble: Fix installation of some watchapps @@ -11,10 +12,7 @@ Add weather support through "Weather Notification" app Various fixes for K9 mail when using the generic notification receiver - Added transliteration support for Russian characters into English - Added transliteration option in the settings screen Add a preference to hide the notification icon of Gadgetbridge - Pebble: Support for build-in weather system app (FW 4.x) Pebble: Add weather support for various watchfaces Pebble: Add option to automatically delete notifications that got dismissed on the phone From 019da98dfa4954105cd02b6498ab2ed84a67ae07 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 22 Jan 2017 22:40:24 +0100 Subject: [PATCH 160/244] escape ' in strings.xml --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2af54dfc..26694f18 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,7 +82,7 @@ Do Not Disturb Stop unwanted Notifications from being sent based on the Do Not Disturb mode. Transliteration - Use, if you device has no support for your language's font (Currently Cyrillic only) + Use, if you device has no support for your language\'s font (Currently Cyrillic only) always when screen is off From c4a0c60b8cd929a0c6fae078ece68789ee3d56f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Sun, 22 Jan 2017 23:33:30 +0000 Subject: [PATCH 161/244] HPlus: Fix bug related to steps and heart rate --- .../service/devices/hplus/HPlusDataRecordDaySlot.java | 2 +- .../service/devices/hplus/HPlusDataRecordRealtime.java | 2 ++ .../gadgetbridge/service/devices/hplus/HPlusHandlerThread.java | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java index c742e046..997ad3f1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java @@ -46,7 +46,7 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord { slot = a; heartRate = data[1] & 0xFF; - if(heartRate == 255) + if(heartRate == 255 || heartRate == 0) heartRate = ActivityKind.TYPE_NOT_MEASURED; steps = (data[2] & 0xFF) * 256 + data[3] & 0xFF; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java index 8b39f84f..4a618bca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java @@ -9,6 +9,7 @@ import java.util.GregorianCalendar; import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; class HPlusDataRecordRealtime extends HPlusDataRecord { @@ -71,6 +72,7 @@ class HPlusDataRecordRealtime extends HPlusDataRecord { if(heartRate == 255) { intensity = 0; activityKind = ActivityKind.TYPE_NOT_MEASURED; + heartRate = ActivitySample.NOT_MEASURED; } else { intensity = (int) (100 * Math.max(0, Math.min((heartRate - 60) / 120.0, 1))); // TODO: Calculate a proper value diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index 7b05775d..54cb040e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -391,6 +391,8 @@ class HPlusHandlerThread extends GBDeviceIoThread { sample.setRawHPlusHealthData(record.getRawData()); sample.setProvider(provider); + provider.addGBActivitySample(sample); + sample.setSteps(sample.getSteps() - prevRealTimeRecord.steps); Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) @@ -398,7 +400,6 @@ class HPlusHandlerThread extends GBDeviceIoThread { .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); - provider.addGBActivitySample(sample); //TODO: Handle Active Time. With Overlay? } catch (GBException ex) { From 1f083041b942e9275165a0dac2bbb4425a42a7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Tue, 24 Jan 2017 01:44:30 +0000 Subject: [PATCH 162/244] HPlus: Improve display of new messages and phone calls --- .../devices/hplus/HPlusConstants.java | 59 +++++++++ .../service/devices/hplus/HPlusSupport.java | 112 +++++++++++------- .../gadgetbridge/util/StringUtils.java | 25 ++++ 3 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 5feaf6d7..01e619bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -4,6 +4,8 @@ package nodomain.freeyourgadget.gadgetbridge.devices.hplus; * @author João Paulo Barraca <jpbarraca@gmail.com> */ +import java.util.HashMap; +import java.util.Map; import java.util.UUID; public final class HPlusConstants { @@ -110,4 +112,61 @@ public final class HPlusConstants { public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time"; public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time"; public static final String PREF_HPLUS_COUNTRY = "hplus_country"; + + public static final Map transliterateMap = new HashMap(){ + { + //These are missing + put('ó', new Byte((byte) 111)); + put('Ó', new Byte((byte) 79)); + put('í', new Byte((byte) 105)); + put('Í', new Byte((byte) 73)); + put('ú', new Byte((byte) 117)); + put('Ú', new Byte((byte) 85)); + + //These mostly belong to the extended ASCII table + put('Ç', new Byte((byte) 128)); + put('ü', new Byte((byte) 129)); + put('é', new Byte((byte) 130)); + put('â', new Byte((byte) 131)); + put('ä', new Byte((byte) 132)); + put('à', new Byte((byte) 133)); + put('ã', new Byte((byte) 134)); + put('ç', new Byte((byte) 135)); + put('ê', new Byte((byte) 136)); + put('ë', new Byte((byte) 137)); + put('è', new Byte((byte) 138)); + put('Ï', new Byte((byte) 139)); + put('Î', new Byte((byte) 140)); + put('Ì', new Byte((byte) 141)); + put('Ã', new Byte((byte) 142)); + put('Ä', new Byte((byte) 143)); + put('É', new Byte((byte) 144)); + put('æ', new Byte((byte) 145)); + put('Æ', new Byte((byte) 146)); + put('ô', new Byte((byte) 147)); + put('ö', new Byte((byte) 148)); + put('ò', new Byte((byte) 149)); + put('û', new Byte((byte) 150)); + put('ù', new Byte((byte) 151)); + put('ÿ', new Byte((byte) 152)); + put('Ö', new Byte((byte) 153)); + put('Ü', new Byte((byte) 154)); + put('¢', new Byte((byte) 155)); + put('£', new Byte((byte) 156)); + put('¥', new Byte((byte) 157)); + put('ƒ', new Byte((byte) 159)); + put('á', new Byte((byte) 160)); + put('ñ', new Byte((byte) 164)); + put('Ñ', new Byte((byte) 165)); + put('ª', new Byte((byte) 166)); + put('º', new Byte((byte) 167)); + put('¿', new Byte((byte) 168)); + put('¬', new Byte((byte) 170)); + put('½', new Byte((byte) 171)); + put('¼', new Byte((byte) 172)); + put('¡', new Byte((byte) 173)); + put('«', new Byte((byte) 174)); + put('»', new Byte((byte) 175)); + } + }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 73072c51..076a0296 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -14,13 +14,16 @@ 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.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.List; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; @@ -41,6 +44,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateA import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; +import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class HPlusSupport extends AbstractBTLEDeviceSupport { @@ -644,7 +649,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private void showIncomingCall(String name, String number) { try { - TransactionBuilder builder = performInitialized("incomingCallIcon"); + TransactionBuilder builder = performInitialized("incomingCall"); //Enable call notifications builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_CALL, 1}); @@ -652,13 +657,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //Show Call Icon builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_INCOMING_CALL, HPlusConstants.ARG_INCOMING_CALL}); - builder.queue(getQueue()); - byte[] msg = new byte[13]; - builder = performInitialized("incomingCallNumber"); - builder.wait(200); - //Show call number for (int i = 0; i < msg.length; i++) msg[i] = ' '; @@ -666,28 +666,28 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { for (int i = 0; i < number.length() && i < (msg.length - 1); i++) msg[i + 1] = (byte) number.charAt(i); - msg[0] = HPlusConstants.CMD_SET_INCOMING_CALL_NUMBER; builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); - - builder = performInitialized("incomingCallText"); builder.wait(200); + msg = msg.clone(); //Show call name - //Must call twice, otherwise nothing happens + for (int i = 0; i < msg.length; i++) msg[i] = ' '; - for (int i = 0; i < name.length() && i < (msg.length - 1); i++) - msg[i + 1] = (byte) name.charAt(i); + if(LanguageUtils.transliterate()) { + name = LanguageUtils.transliterate(name); + } + + byte[] nameBytes = encodeStringToDevice(name); + for (int i = 0; i < nameBytes.length && i < (msg.length - 1); i++) + msg[i + 1] = nameBytes[i]; msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME; builder.write(ctrlCharacteristic, msg); - builder.wait(200); - msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME_CN; builder.write(ctrlCharacteristic, msg); @@ -703,45 +703,44 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { try { TransactionBuilder builder = performInitialized("notification"); - byte[] msg = new byte[20]; - for (int i = 0; i < msg.length; i++) - msg[i] = ' '; - - msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT; - String message = ""; - //TODO: Create StringUtils.pad and StringUtils.truncate - if (title != null) { - if (title.length() > 17) { - message = title.substring(0, 17); - } else { - message = title; - for (int i = message.length(); i < 17; i++) - message += " "; + if (title != null && title.length() > 0) { + + if(LanguageUtils.transliterate()){ + title = LanguageUtils.transliterate(title); } + message = StringUtils.pad(StringUtils.truncate(title, 16), 16); //Limit title to top row } - message += body; - int length = message.length() / 17; + if(body != null) { + if(LanguageUtils.transliterate()){ + body = LanguageUtils.transliterate(body); + } - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_ACTION_INCOMING_SOCIAL, (byte) 255}); + message += body; + } + + byte[] messageBytes = encodeStringToDevice(message); + + int length = messageBytes.length / 17; builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_INCOMING_MESSAGE, HPlusConstants.ARG_INCOMING_MESSAGE}); - int remaining; - - if (message.length() % 17 > 0) - remaining = length + 1; - else - remaining = length; + int remaining = Math.min(255, (messageBytes.length % 17 > 0) ? length + 1 : length); + byte[] msg = new byte[20]; + msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT; msg[1] = (byte) remaining; + + for (int i = 2; i < msg.length; i++) + msg[i] = ' '; + int message_index = 0; int i = 3; - for (int j = 0; j < message.length(); j++) { - msg[i++] = (byte) message.charAt(j); + for (int j = 0; j < messageBytes.length; j++) { + msg[i++] = messageBytes[j]; if (i == msg.length) { message_index++; @@ -776,6 +775,39 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } } + /** + * HPlus devices accept a subset of GB2312 with some modifications. + * This function will apply a custom transliteration. + * While related to the methods implemented in LanguageUtils. These are specific for HPLUS + * + * @param s The String to transliterate + * @return An array of bytes ready to be sent to the device + */ + private byte[] encodeStringToDevice(String s){ + + List outBytes = new ArrayList(); + + for(int i = 0; i < s.length(); i++){ + Character c = s.charAt(i); + byte[] cs; + + if(HPlusConstants.transliterateMap.containsKey(c)){ + cs = new byte[] {HPlusConstants.transliterateMap.get(c)}; + }else { + try { + cs = c.toString().getBytes("GB2312"); + } catch (UnsupportedEncodingException e) { + //Fallback. Result string may be strange, but better than nothing + cs = c.toString().getBytes(); + } + } + for(int j = 0; j < cs.length; j++) + outBytes.add(cs[j]); + } + + return ArrayUtils.toPrimitive(outBytes.toArray(new Byte[outBytes.size()])); + } + @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java new file mode 100644 index 00000000..8dd6d8cc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java @@ -0,0 +1,25 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +public class StringUtils { + + public static String truncate(String s, int maxLength){ + int length = Math.min(s.length(), maxLength); + + if(length < 0) + return ""; + + return s.substring(0, length); + } + + public static String pad(String s, int length){ + return pad(s, length, ' '); + } + + public static String pad(String s, int length, char padChar){ + + while(s.length() < length) + s += padChar; + + return s; + } +} From 8027b8ac9647966b8e7c3daa56a8acb512323c65 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 11:07:00 +0100 Subject: [PATCH 163/244] Pebble: fix potential crash when encoding appmessages with null values --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 2 ++ 1 file changed, 2 insertions(+) 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 6ef013bb..14ae5f9e 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 @@ -1846,6 +1846,8 @@ public class PebbleProtocol extends GBDeviceProtocol { byte[] encodeApplicationMessagePush(short endpoint, UUID uuid, ArrayList> pairs) { int length = LENGTH_UUID + 3; // UUID + (PUSH + id + length of dict) for (Pair pair : pairs) { + if (pair.first == null || pair.second == null) + continue; length += 7; // key + type + length if (pair.second instanceof Integer) { length += 4; From 746eeda777965677bec230d2726d2c76e033b1a0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 11:07:49 +0100 Subject: [PATCH 164/244] Pebble: use dynamic appkeys for TrekVolle handler --- .../devices/pebble/AppMessageHandler.java | 4 +- .../pebble/AppMessageHandlerTrekVolle.java | 52 ++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java index 8705cb23..b11b9660 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java @@ -21,7 +21,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; class AppMessageHandler { final PebbleProtocol mPebbleProtocol; final UUID mUUID; - protected Map messageKeys; + Map messageKeys; AppMessageHandler(UUID uuid, PebbleProtocol pebbleProtocol) { mUUID = uuid; @@ -55,7 +55,7 @@ class AppMessageHandler { return mPebbleProtocol.getDevice(); } - protected JSONObject getAppKeys() throws IOException, JSONException { + JSONObject getAppKeys() throws IOException, JSONException { File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); File configurationFile = new File(destDir, mUUID.toString() + ".json"); if (configurationFile.exists()) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java index d926e8d8..becf1a3c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java @@ -1,25 +1,65 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; +import android.widget.Toast; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.util.GB; class AppMessageHandlerTrekVolle extends AppMessageHandler { - private static final int MESSAGE_KEY_WEATHER_TEMPERATURE = 10000; - private static final int MESSAGE_KEY_WEATHER_CONDITIONS = 10001; - private static final int MESSAGE_KEY_WEATHER_ICON = 10002; - private static final int MESSAGE_KEY_WEATHER_TEMPERATURE_MIN = 10024; - private static final int MESSAGE_KEY_WEATHER_TEMPERATURE_MAX = 10025; - private static final int MESSAGE_KEY_WEATHER_LOCATION = 10030; + private Integer MESSAGE_KEY_WEATHER_TEMPERATURE; + private Integer MESSAGE_KEY_WEATHER_CONDITIONS; + private Integer MESSAGE_KEY_WEATHER_ICON; + private Integer MESSAGE_KEY_WEATHER_TEMPERATURE_MIN; + private Integer MESSAGE_KEY_WEATHER_TEMPERATURE_MAX; + private Integer MESSAGE_KEY_WEATHER_LOCATION; AppMessageHandlerTrekVolle(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); + + messageKeys = new HashMap<>(); + try { + JSONObject appKeys = getAppKeys(); + Iterator appKeysIterator = appKeys.keys(); + while (appKeysIterator.hasNext()) { + String current = appKeysIterator.next(); + int appKey = appKeys.getInt(current); + switch (current) { + case "WEATHER_TEMPERATURE": + MESSAGE_KEY_WEATHER_TEMPERATURE = appKey; + break; + case "WEATHER_CONDITIONS": + MESSAGE_KEY_WEATHER_CONDITIONS = appKey; + break; + case "WEATHER_ICON": + MESSAGE_KEY_WEATHER_ICON = appKey; + break; + case "WEATHER_TEMPERATURE_MIN": + MESSAGE_KEY_WEATHER_TEMPERATURE_MIN = appKey; + break; + case "WEATHER_TEMPERATURE_MAX": + MESSAGE_KEY_WEATHER_TEMPERATURE_MAX = appKey; + break; + case "WEATHER_LOCATION": + MESSAGE_KEY_WEATHER_LOCATION = appKey; + break; + } + } + } catch (IOException | JSONException e) { + GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } } private int getIconForConditionCode(int conditionCode, boolean isNight) { From b4a4b3916a6e785bb58b10e9ef25b12d8104ac96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Tue, 24 Jan 2017 10:39:24 +0000 Subject: [PATCH 165/244] HPlus: Remove LanguageUtils transliterate from HPlusSupport --- .../service/devices/hplus/HPlusSupport.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 076a0296..c410537c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -44,7 +44,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateA import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.util.GB; -import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; @@ -677,10 +676,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { for (int i = 0; i < msg.length; i++) msg[i] = ' '; - if(LanguageUtils.transliterate()) { - name = LanguageUtils.transliterate(name); - } - byte[] nameBytes = encodeStringToDevice(name); for (int i = 0; i < nameBytes.length && i < (msg.length - 1); i++) msg[i + 1] = nameBytes[i]; @@ -706,18 +701,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { String message = ""; if (title != null && title.length() > 0) { - - if(LanguageUtils.transliterate()){ - title = LanguageUtils.transliterate(title); - } message = StringUtils.pad(StringUtils.truncate(title, 16), 16); //Limit title to top row } if(body != null) { - if(LanguageUtils.transliterate()){ - body = LanguageUtils.transliterate(body); - } - message += body; } From fb7db523c7e66ad80a5f5830fd4c10cb80f8beb1 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 11:58:13 +0100 Subject: [PATCH 166/244] Pebble: dynamic appKey suppoort for Morpheuz --- .../pebble/AppMessageHandlerMorpheuz.java | 231 ++++++++++-------- 1 file changed, 124 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 4b152289..8b12aa69 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -1,11 +1,18 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; +import android.widget.Toast; +import org.json.JSONException; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Objects; import java.util.SimpleTimeZone; import java.util.TimeZone; import java.util.UUID; @@ -18,31 +25,21 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSleepMonitorResult; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleMorpheuzSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMorpheuzSample; +import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; class AppMessageHandlerMorpheuz extends AppMessageHandler { - - private static final int KEY_POINT = 1; - private static final int KEY_POINT_46 = 10000; - private static final int KEY_CTRL = 2; - private static final int KEY_CTRL_46 = 10001; - private static final int KEY_FROM = 3; - private static final int KEY_FROM_46 = 10002; - private static final int KEY_TO = 4; - private static final int KEY_TO_46 = 10003; - private static final int KEY_BASE = 5; - private static final int KEY_BASE_46 = 10004; - private static final int KEY_VERSION = 6; - private static final int KEY_VERSION_46 = 10005; - private static final int KEY_GONEOFF = 7; - private static final int KEY_GONEOFF_46 = 10006; - private static final int KEY_TRANSMIT = 8; - private static final int KEY_TRANSMIT_46 = 10007; - private static final int KEY_AUTO_RESET = 9; - private static final int KEY_AUTO_RESET_46 = 10008; - private static final int KEY_SNOOZES = 10; - private static final int KEY_SNOOZES_46 = 10009; - private static final int KEY_FAULT_46 = 10010; + private Integer keyPoint; + private Integer keyCtrl; + private Integer keyFrom; + private Integer keyTo; + private Integer keyBase; + private Integer keyVersion; + private Integer keyGoneoff; + private Integer keyTransmit; + private Integer keyAutoReset; + private Integer keySnoozes; + private Integer keyFault; private static final int CTRL_TRANSMIT_DONE = 1; private static final int CTRL_VERSION_DONE = 2; @@ -53,7 +50,6 @@ class AppMessageHandlerMorpheuz extends AppMessageHandler { private static final int CTRL_SNOOZES_DONE = 64; // data received from Morpheuz in native format - private int version = 0; private int smartalarm_from = -1; // time in minutes relative from 0:00 for smart alarm (earliest) private int smartalarm_to = -1;// time in minutes relative from 0:00 for smart alarm (latest) private int recording_base_timestamp = -1; // timestamp for the first "point", all folowing are +10 minutes offset each @@ -63,6 +59,53 @@ class AppMessageHandlerMorpheuz extends AppMessageHandler { public AppMessageHandlerMorpheuz(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); + + messageKeys = new HashMap<>(); + try { + JSONObject appKeys = getAppKeys(); + Iterator appKeysIterator = appKeys.keys(); + while (appKeysIterator.hasNext()) { + String current = appKeysIterator.next(); + int appKey = appKeys.getInt(current); + switch (current) { + case "keyPoint": + keyPoint = appKey; + break; + case "keyCtrl": + keyCtrl = appKey; + break; + case "keyFrom": + keyFrom = appKey; + break; + case "keyTo": + keyTo = appKey; + break; + case "keyBase": + keyBase = appKey; + break; + case "keyVersion": + keyVersion = appKey; + break; + case "keyGoneoff": + keyGoneoff = appKey; + break; + case "keyTransmit": + keyTransmit = appKey; + break; + case "keyAutoReset": + keyAutoReset = appKey; + break; + case "keySnoozes": + keySnoozes = appKey; + break; + case "keyFault": + keyFault = appKey; + break; + } + } + } catch (IOException | JSONException e) { + GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } } private byte[] encodeMorpheuzMessage(int key, int value) { @@ -84,88 +127,66 @@ class AppMessageHandlerMorpheuz extends AppMessageHandler { GBDeviceEventSleepMonitorResult sleepMonitorResult = null; for (Pair pair : pairs) { - switch (pair.first) { - case KEY_TRANSMIT: - case KEY_TRANSMIT_46: - sleepMonitorResult = new GBDeviceEventSleepMonitorResult(); - sleepMonitorResult.smartalarm_from = smartalarm_from; - sleepMonitorResult.smartalarm_to = smartalarm_to; - sleepMonitorResult.alarm_gone_off = alarm_gone_off; - sleepMonitorResult.recording_base_timestamp = recording_base_timestamp; - ctrl_message |= CTRL_TRANSMIT_DONE; - break; - case KEY_GONEOFF: - case KEY_GONEOFF_46: - alarm_gone_off = (int) pair.second; - LOG.info("got gone off: " + alarm_gone_off / 60 + ":" + alarm_gone_off % 60); - ctrl_message |= CTRL_DO_NEXT | CTRL_GONEOFF_DONE; - break; - case KEY_POINT: - case KEY_POINT_46: - if (recording_base_timestamp == -1) { - // we have no base timestamp but received points, stop this - ctrl_message = CTRL_VERSION_DONE | CTRL_GONEOFF_DONE | CTRL_TRANSMIT_DONE | CTRL_SET_LAST_SENT; - } else { - int index = ((int) pair.second >> 16); - int intensity = ((int) pair.second & 0xffff); - LOG.info("got point:" + index + " " + intensity); - if (index >= 0) { - try (DBHandler db = GBApplication.acquireDB()) { - Long userId = DBHelper.getUser(db.getDaoSession()).getId(); - Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); - PebbleMorpheuzSampleProvider sampleProvider = new PebbleMorpheuzSampleProvider(getDevice(), db.getDaoSession()); - PebbleMorpheuzSample sample = new PebbleMorpheuzSample(recording_base_timestamp + index * 600, deviceId, userId, intensity); - sample.setProvider(sampleProvider); - sampleProvider.addGBActivitySample(sample); - } catch (Exception e) { - LOG.error("Error acquiring database", e); - } + if (Objects.equals(pair.first, keyTransmit)) { + sleepMonitorResult = new GBDeviceEventSleepMonitorResult(); + sleepMonitorResult.smartalarm_from = smartalarm_from; + sleepMonitorResult.smartalarm_to = smartalarm_to; + sleepMonitorResult.alarm_gone_off = alarm_gone_off; + sleepMonitorResult.recording_base_timestamp = recording_base_timestamp; + ctrl_message |= CTRL_TRANSMIT_DONE; + } else if (pair.first.equals(keyGoneoff)) { + alarm_gone_off = (int) pair.second; + LOG.info("got gone off: " + alarm_gone_off / 60 + ":" + alarm_gone_off % 60); + ctrl_message |= CTRL_DO_NEXT | CTRL_GONEOFF_DONE; + } else if (pair.first.equals(keyPoint)) { + if (recording_base_timestamp == -1) { + // we have no base timestamp but received points, stop this + ctrl_message = CTRL_VERSION_DONE | CTRL_GONEOFF_DONE | CTRL_TRANSMIT_DONE | CTRL_SET_LAST_SENT; + } else { + int index = ((int) pair.second >> 16); + int intensity = ((int) pair.second & 0xffff); + LOG.info("got point:" + index + " " + intensity); + if (index >= 0) { + try (DBHandler db = GBApplication.acquireDB()) { + Long userId = DBHelper.getUser(db.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); + PebbleMorpheuzSampleProvider sampleProvider = new PebbleMorpheuzSampleProvider(getDevice(), db.getDaoSession()); + PebbleMorpheuzSample sample = new PebbleMorpheuzSample(recording_base_timestamp + index * 600, deviceId, userId, intensity); + sample.setProvider(sampleProvider); + sampleProvider.addGBActivitySample(sample); + } catch (Exception e) { + LOG.error("Error acquiring database", e); } - - ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; } - break; - case KEY_FROM: - case KEY_FROM_46: - smartalarm_from = (int) pair.second; - LOG.info("got from: " + smartalarm_from / 60 + ":" + smartalarm_from % 60); + ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; - break; - case KEY_TO: - case KEY_TO_46: - smartalarm_to = (int) pair.second; - LOG.info("got to: " + smartalarm_to / 60 + ":" + smartalarm_to % 60); - ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; - break; - case KEY_VERSION: - case KEY_VERSION_46: - version = (int) pair.second; - LOG.info("got version: " + ((float) version / 10.0f)); - ctrl_message |= CTRL_VERSION_DONE; - break; - case KEY_BASE: - case KEY_BASE_46: - // fix timestamp - TimeZone tz = SimpleTimeZone.getDefault(); - recording_base_timestamp = (int) pair.second - (tz.getOffset(System.currentTimeMillis())) / 1000; - LOG.info("got base: " + recording_base_timestamp); - ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; - break; - case KEY_AUTO_RESET: - case KEY_AUTO_RESET_46: - ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; - break; - case KEY_SNOOZES: - case KEY_SNOOZES_46: - ctrl_message |= CTRL_SNOOZES_DONE | CTRL_DO_NEXT; - break; - case KEY_FAULT_46: - LOG.info("fault code: " + (int) pair.second); - ctrl_message |= CTRL_DO_NEXT; - break; - default: - LOG.info("unhandled key: " + pair.first); - break; + } + } else if (pair.first.equals(keyFrom)) { + smartalarm_from = (int) pair.second; + LOG.info("got from: " + smartalarm_from / 60 + ":" + smartalarm_from % 60); + ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; + } else if (pair.first.equals(keyTo)) { + smartalarm_to = (int) pair.second; + LOG.info("got to: " + smartalarm_to / 60 + ":" + smartalarm_to % 60); + ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; + } else if (pair.first.equals(keyVersion)) { + int version = (int) pair.second; + LOG.info("got version: " + ((float) version / 10.0f)); + ctrl_message |= CTRL_VERSION_DONE; + } else if (pair.first.equals(keyBase)) {// fix timestamp + TimeZone tz = SimpleTimeZone.getDefault(); + recording_base_timestamp = (int) pair.second - (tz.getOffset(System.currentTimeMillis())) / 1000; + LOG.info("got base: " + recording_base_timestamp); + ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; + } else if (pair.first.equals(keyAutoReset)) { + ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; + } else if (pair.first.equals(keySnoozes)) { + ctrl_message |= CTRL_SNOOZES_DONE | CTRL_DO_NEXT; + } else if (pair.first.equals(keyFault)) { + LOG.info("fault code: " + (int) pair.second); + ctrl_message |= CTRL_DO_NEXT; + } else { + LOG.info("unhandled key: " + pair.first); } } @@ -177,11 +198,7 @@ class AppMessageHandlerMorpheuz extends AppMessageHandler { GBDeviceEventSendBytes sendBytesCtrl = null; if (ctrl_message > 0) { sendBytesCtrl = new GBDeviceEventSendBytes(); - int ctrlkey = KEY_CTRL; - if (version >= 46) { - ctrlkey = KEY_CTRL_46; - } - sendBytesCtrl.encodedBytes = encodeMorpheuzMessage(ctrlkey, ctrl_message); + sendBytesCtrl.encodedBytes = encodeMorpheuzMessage(keyCtrl, ctrl_message); } // ctrl and sleep monitor might be null, thats okay From 3233432ee19639aae102ea59044ffe4f30e22af9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 18:38:26 +0100 Subject: [PATCH 167/244] Pebble: simplify AppMessageHandler --- .../pebble/AppMessageHandlerMorpheuz.java | 52 ++++--------------- .../pebble/AppMessageHandlerTrekVolle.java | 32 +++--------- 2 files changed, 17 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 8b12aa69..37122ec6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.Objects; import java.util.SimpleTimeZone; import java.util.TimeZone; @@ -63,46 +62,17 @@ class AppMessageHandlerMorpheuz extends AppMessageHandler { messageKeys = new HashMap<>(); try { JSONObject appKeys = getAppKeys(); - Iterator appKeysIterator = appKeys.keys(); - while (appKeysIterator.hasNext()) { - String current = appKeysIterator.next(); - int appKey = appKeys.getInt(current); - switch (current) { - case "keyPoint": - keyPoint = appKey; - break; - case "keyCtrl": - keyCtrl = appKey; - break; - case "keyFrom": - keyFrom = appKey; - break; - case "keyTo": - keyTo = appKey; - break; - case "keyBase": - keyBase = appKey; - break; - case "keyVersion": - keyVersion = appKey; - break; - case "keyGoneoff": - keyGoneoff = appKey; - break; - case "keyTransmit": - keyTransmit = appKey; - break; - case "keyAutoReset": - keyAutoReset = appKey; - break; - case "keySnoozes": - keySnoozes = appKey; - break; - case "keyFault": - keyFault = appKey; - break; - } - } + keyPoint = appKeys.getInt("keyPoint"); + keyCtrl = appKeys.getInt("keyCtrl"); + keyFrom = appKeys.getInt("keyFrom"); + keyTo = appKeys.getInt("keyTo"); + keyBase = appKeys.getInt("keyBase"); + keyVersion = appKeys.getInt("keyVersion"); + keyGoneoff = appKeys.getInt("keyGoneoff"); + keyTransmit = appKeys.getInt("keyTransmit"); + keyAutoReset = appKeys.getInt("keyAutoReset"); + keySnoozes = appKeys.getInt("keySnoozes"); + keyFault = appKeys.getInt("keyFault"); } catch (IOException | JSONException e) { GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java index becf1a3c..6d28fa8e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java @@ -9,7 +9,6 @@ import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; @@ -32,31 +31,12 @@ class AppMessageHandlerTrekVolle extends AppMessageHandler { messageKeys = new HashMap<>(); try { JSONObject appKeys = getAppKeys(); - Iterator appKeysIterator = appKeys.keys(); - while (appKeysIterator.hasNext()) { - String current = appKeysIterator.next(); - int appKey = appKeys.getInt(current); - switch (current) { - case "WEATHER_TEMPERATURE": - MESSAGE_KEY_WEATHER_TEMPERATURE = appKey; - break; - case "WEATHER_CONDITIONS": - MESSAGE_KEY_WEATHER_CONDITIONS = appKey; - break; - case "WEATHER_ICON": - MESSAGE_KEY_WEATHER_ICON = appKey; - break; - case "WEATHER_TEMPERATURE_MIN": - MESSAGE_KEY_WEATHER_TEMPERATURE_MIN = appKey; - break; - case "WEATHER_TEMPERATURE_MAX": - MESSAGE_KEY_WEATHER_TEMPERATURE_MAX = appKey; - break; - case "WEATHER_LOCATION": - MESSAGE_KEY_WEATHER_LOCATION = appKey; - break; - } - } + MESSAGE_KEY_WEATHER_TEMPERATURE = appKeys.getInt("WEATHER_TEMPERATURE"); + MESSAGE_KEY_WEATHER_CONDITIONS = appKeys.getInt("WEATHER_CONDITIONS"); + MESSAGE_KEY_WEATHER_ICON = appKeys.getInt("WEATHER_ICON"); + MESSAGE_KEY_WEATHER_TEMPERATURE_MIN = appKeys.getInt("WEATHER_TEMPERATURE_MIN"); + MESSAGE_KEY_WEATHER_TEMPERATURE_MAX = appKeys.getInt("WEATHER_TEMPERATURE_MAX"); + MESSAGE_KEY_WEATHER_LOCATION = appKeys.getInt("WEATHER_LOCATION"); } catch (IOException | JSONException e) { GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); } From 712ce1aa8b368d388a06ac4ef04e59609877e484 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 18:50:43 +0100 Subject: [PATCH 168/244] Pebble: dynamic keys support for healthify --- .../pebble/AppMessageHandlerHealthify.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java index 29dad593..1ae46cdb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java @@ -1,25 +1,41 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; +import android.widget.Toast; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.HashMap; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.util.GB; class AppMessageHandlerHealthify extends AppMessageHandler { - private static final int KEY_TEMPERATURE = 10021; - private static final int KEY_CONDITIONS = 10022; + private Integer KEY_TEMPERATURE; + private Integer KEY_CONDITIONS; AppMessageHandlerHealthify(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); + + messageKeys = new HashMap<>(); + try { + JSONObject appKeys = getAppKeys(); + KEY_TEMPERATURE = appKeys.getInt("TEMPERATURE"); + KEY_CONDITIONS = appKeys.getInt("CONDITIONS"); + } catch (IOException | JSONException e) { + GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } } - private byte[] encodeMarioWeatherMessage(WeatherSpec weatherSpec) { + private byte[] encodeHelthifyWeatherMessage(WeatherSpec weatherSpec) { if (weatherSpec == null) { return null; } @@ -43,12 +59,12 @@ class AppMessageHandlerHealthify extends AppMessageHandler { return new GBDeviceEvent[]{null}; } GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeMarioWeatherMessage(weatherSpec); + sendBytes.encodedBytes = encodeHelthifyWeatherMessage(weatherSpec); return new GBDeviceEvent[]{sendBytes}; } @Override public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { - return encodeMarioWeatherMessage(weatherSpec); + return encodeHelthifyWeatherMessage(weatherSpec); } } From a451b5367b06b2f292487a907d228933d3d46dfe Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 19:02:45 +0100 Subject: [PATCH 169/244] Pebble: dynamic key support for Square handler --- .../pebble/AppMessageHandlerHealthify.java | 2 - .../pebble/AppMessageHandlerMorpheuz.java | 2 - .../pebble/AppMessageHandlerSquare.java | 47 +++++++++++-------- .../pebble/AppMessageHandlerTrekVolle.java | 2 - 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java index 1ae46cdb..e45c4a16 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java @@ -9,7 +9,6 @@ import org.json.JSONObject; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.HashMap; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; @@ -25,7 +24,6 @@ class AppMessageHandlerHealthify extends AppMessageHandler { AppMessageHandlerHealthify(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); - messageKeys = new HashMap<>(); try { JSONObject appKeys = getAppKeys(); KEY_TEMPERATURE = appKeys.getInt("TEMPERATURE"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 37122ec6..0e05d0bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.Objects; import java.util.SimpleTimeZone; import java.util.TimeZone; @@ -59,7 +58,6 @@ class AppMessageHandlerMorpheuz extends AppMessageHandler { public AppMessageHandlerMorpheuz(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); - messageKeys = new HashMap<>(); try { JSONObject appKeys = getAppKeys(); keyPoint = appKeys.getInt("keyPoint"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java index bcb77679..8a080037 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java @@ -1,7 +1,12 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; +import android.widget.Toast; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.UUID; @@ -10,26 +15,28 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.util.GB; class AppMessageHandlerSquare extends AppMessageHandler { - // "CfgKeyCelsiusTemperature":10001, - // CfgKeyConditions":10002, - //"CfgKeyWeatherError":10003, - // "CfgKeyWeatherMode":10004, - // "CfgKeyUseCelsius":10005," - // CfgKeyWeatherLocation":10006," - // "CfgKeyTemperature":10000, - // - // - private static final int KEY_TEMP = 10001; //celsius - private static final int KEY_WEATHER = 10002; - private static final int KEY_WEATHER_MODE = 10004; - private static final int KEY_USE_CELSIUS = 10005; //celsius - private static final int KEY_LOCATION = 10006; - private static final int KEY_TEMP_F = 10000; //fahrenheit + private int CfgKeyCelsiusTemperature; + private int CfgKeyConditions; + private int CfgKeyWeatherMode; + private int CfgKeyUseCelsius; + private int CfgKeyWeatherLocation; AppMessageHandlerSquare(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); + + try { + JSONObject appKeys = getAppKeys(); + CfgKeyCelsiusTemperature = appKeys.getInt("CfgKeyCelsiusTemperature"); + CfgKeyConditions = appKeys.getInt("CfgKeyConditions"); + CfgKeyWeatherMode = appKeys.getInt("CfgKeyWeatherMode"); + CfgKeyUseCelsius = appKeys.getInt("CfgKeyUseCelsius"); + CfgKeyWeatherLocation = appKeys.getInt("CfgKeyWeatherLocation"); + } catch (IOException | JSONException e) { + GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } } private byte[] encodeSquareWeatherMessage(WeatherSpec weatherSpec) { @@ -38,11 +45,11 @@ class AppMessageHandlerSquare extends AppMessageHandler { } ArrayList> pairs = new ArrayList<>(2); - pairs.add(new Pair<>(KEY_WEATHER_MODE, (Object) 1)); - pairs.add(new Pair<>(KEY_WEATHER, (Object) weatherSpec.currentCondition)); - pairs.add(new Pair<>(KEY_USE_CELSIUS, (Object) 1)); - pairs.add(new Pair<>(KEY_TEMP, (Object) (weatherSpec.currentTemp - 273))); - pairs.add(new Pair<>(KEY_LOCATION, (Object) (weatherSpec.location))); + pairs.add(new Pair<>(CfgKeyWeatherMode, (Object) 1)); + pairs.add(new Pair<>(CfgKeyConditions, (Object) weatherSpec.currentCondition)); + pairs.add(new Pair<>(CfgKeyUseCelsius, (Object) 1)); + pairs.add(new Pair<>(CfgKeyCelsiusTemperature, (Object) (weatherSpec.currentTemp - 273))); + pairs.add(new Pair<>(CfgKeyWeatherLocation, (Object) (weatherSpec.location))); byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java index 6d28fa8e..e1f30463 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java @@ -8,7 +8,6 @@ import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; @@ -28,7 +27,6 @@ class AppMessageHandlerTrekVolle extends AppMessageHandler { AppMessageHandlerTrekVolle(UUID uuid, PebbleProtocol pebbleProtocol) { super(uuid, pebbleProtocol); - messageKeys = new HashMap<>(); try { JSONObject appKeys = getAppKeys(); MESSAGE_KEY_WEATHER_TEMPERATURE = appKeys.getInt("WEATHER_TEMPERATURE"); From 06295abcb63376a6e51be455d4657c7d967294af Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Tue, 24 Jan 2017 21:04:06 +0300 Subject: [PATCH 170/244] Simplification of transliteration integration --- .../activities/DebugActivity.java | 7 ++-- .../gadgetbridge/impl/GBDeviceService.java | 23 +++++++++++ .../service/DeviceCommunicationService.java | 38 +++++++------------ 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index 30d0e631..80363273 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -180,9 +180,10 @@ public class DebugActivity extends GBActivity { @Override public void onClick(View v) { MusicSpec musicSpec = new MusicSpec(); - musicSpec.artist = editContent.getText().toString() + "(artist)"; - musicSpec.album = editContent.getText().toString() + "(album)"; - musicSpec.track = editContent.getText().toString() + "(track)"; + String testString = editContent.getText().toString(); + musicSpec.artist = testString + "(artist)"; + musicSpec.album = testString + "(album)"; + musicSpec.track = testString + "(track)"; musicSpec.duration = 10; musicSpec.trackCount = 5; musicSpec.trackNr = 2; 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 949791c7..dea375c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -19,10 +19,25 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; +import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; public class GBDeviceService implements DeviceService { protected final Context mContext; private final Class mServiceClass; + private final String[] transliterationExtras = new String[]{ + EXTRA_NOTIFICATION_PHONENUMBER, + EXTRA_NOTIFICATION_SENDER, + EXTRA_NOTIFICATION_SUBJECT, + EXTRA_NOTIFICATION_TITLE, + EXTRA_NOTIFICATION_BODY, + EXTRA_NOTIFICATION_SOURCENAME, + EXTRA_CALL_PHONENUMBER, + EXTRA_MUSIC_ARTIST, + EXTRA_MUSIC_ALBUM, + EXTRA_MUSIC_TRACK, + EXTRA_CALENDAREVENT_TITLE, + EXTRA_CALENDAREVENT_DESCRIPTION + }; public GBDeviceService(Context context) { mContext = context; @@ -34,6 +49,14 @@ public class GBDeviceService implements DeviceService { } protected void invokeService(Intent intent) { + if(LanguageUtils.transliterate()){ + for (String extra: transliterationExtras) { + if (intent.hasExtra(extra)){ + intent.putExtra(extra, LanguageUtils.transliterate(intent.getStringExtra(extra))); + } + } + } + mContext.startService(intent); } 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 da4fa9a0..b35a9417 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -51,7 +51,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; -import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ADD_CALENDAREVENT; @@ -323,12 +322,12 @@ public class DeviceCommunicationService extends Service implements SharedPrefere break; case ACTION_NOTIFICATION: { NotificationSpec notificationSpec = new NotificationSpec(); - notificationSpec.phoneNumber = getStringExtra(intent, EXTRA_NOTIFICATION_PHONENUMBER); - notificationSpec.sender = getStringExtra(intent, EXTRA_NOTIFICATION_SENDER); - notificationSpec.subject = getStringExtra(intent, EXTRA_NOTIFICATION_SUBJECT); - notificationSpec.title = getStringExtra(intent, EXTRA_NOTIFICATION_TITLE); - notificationSpec.body = getStringExtra(intent, EXTRA_NOTIFICATION_BODY); - notificationSpec.sourceName = getStringExtra(intent, EXTRA_NOTIFICATION_SOURCENAME); + notificationSpec.phoneNumber = intent.getStringExtra(EXTRA_NOTIFICATION_PHONENUMBER); + notificationSpec.sender = intent.getStringExtra(EXTRA_NOTIFICATION_SENDER); + notificationSpec.subject = intent.getStringExtra(EXTRA_NOTIFICATION_SUBJECT); + notificationSpec.title = intent.getStringExtra(EXTRA_NOTIFICATION_TITLE); + notificationSpec.body = intent.getStringExtra(EXTRA_NOTIFICATION_BODY); + notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME); notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE); notificationSpec.id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1); notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0); @@ -370,8 +369,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere calendarEventSpec.type = intent.getByteExtra(EXTRA_CALENDAREVENT_TYPE, (byte) -1); calendarEventSpec.timestamp = intent.getIntExtra(EXTRA_CALENDAREVENT_TIMESTAMP, -1); calendarEventSpec.durationInSeconds = intent.getIntExtra(EXTRA_CALENDAREVENT_DURATION, -1); - calendarEventSpec.title = getStringExtra(intent, EXTRA_CALENDAREVENT_TITLE); - calendarEventSpec.description = getStringExtra(intent, EXTRA_CALENDAREVENT_DESCRIPTION); + calendarEventSpec.title = intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE); + calendarEventSpec.description = intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION); mDeviceSupport.onAddCalendarEvent(calendarEventSpec); break; } @@ -416,7 +415,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere case ACTION_CALLSTATE: int command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED); - String phoneNumber = getStringExtra(intent, EXTRA_CALL_PHONENUMBER); + String phoneNumber = intent.getStringExtra(EXTRA_CALL_PHONENUMBER); String callerName = null; if (phoneNumber != null) { callerName = getContactDisplayNameByNumber(phoneNumber); @@ -442,9 +441,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere break; case ACTION_SETMUSICINFO: MusicSpec musicSpec = new MusicSpec(); - musicSpec.artist = getStringExtra(intent, EXTRA_MUSIC_ARTIST); - musicSpec.album = getStringExtra(intent, EXTRA_MUSIC_ALBUM); - musicSpec.track = getStringExtra(intent, EXTRA_MUSIC_TRACK); + musicSpec.artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST); + musicSpec.album = intent.getStringExtra(EXTRA_MUSIC_ALBUM); + musicSpec.track = intent.getStringExtra(EXTRA_MUSIC_TRACK); musicSpec.duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0); musicSpec.trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0); musicSpec.trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0); @@ -707,18 +706,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere // ignore, just return name below } - return LanguageUtils.transliterate() - ? LanguageUtils.transliterate(name) - : name; - } - - //Standard method with transliteration - private String getStringExtra(Intent intent, String event){ - String extra = intent.getStringExtra(event); - - return LanguageUtils.transliterate() - ? LanguageUtils.transliterate(extra) - : extra; + return name; } @Override From 09539fd9bf0b0848fe248bf4b038a6d7c16c5168 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Wed, 25 Jan 2017 00:04:05 +0300 Subject: [PATCH 171/244] Add transliteration test --- .../DeviceCommunicationServiceTestCase.java | 17 +++++++++ .../service/TestDeviceService.java | 2 +- .../gadgetbridge/test/LanguageUtilsTest.java | 38 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java index 58dccd72..11fc855e 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java @@ -1,17 +1,21 @@ package nodomain.freeyourgadget.gadgetbridge.service; import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.test.TestBase; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_BODY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -97,4 +101,17 @@ public class DeviceCommunicationServiceTestCase extends TestBase { inOrder.verifyNoMoreInteractions(); } + @Test + public void testTransliterationSupport() { + SharedPreferences settings = GBApplication.getPrefs().getPreferences(); + SharedPreferences.Editor editor = settings.edit(); + editor.putBoolean("transliteration", true); + editor.commit(); + + Intent intent = mDeviceService.createIntent().putExtra(EXTRA_NOTIFICATION_BODY, "Прõсто текčт"); + mDeviceService.invokeService(intent); + String result = intent.getStringExtra(EXTRA_NOTIFICATION_BODY); + + assertTrue("Transliteration support fail!", result.equals("Prosto tekct")); + } } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java index 8eff6f79..9de950fa 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java @@ -29,7 +29,7 @@ class TestDeviceService extends GBDeviceService { // calling though to the service natively does not work with robolectric, // we have to use the ServiceController to do that service.onStartCommand(intent, Service.START_FLAG_REDELIVERY, (int) (Math.random() * 10000)); -// super.invokeService(intent); + super.invokeService(intent); } @Override diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java new file mode 100644 index 00000000..cfc8ec49 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java @@ -0,0 +1,38 @@ +package nodomain.freeyourgadget.gadgetbridge.test; + +import android.content.SharedPreferences; + +import org.junit.Test; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests LanguageUtils + */ +public class LanguageUtilsTest extends TestBase { + @Test + public void testStringTransliterate() throws Exception { + //input with cyrillic and diacritic letters + String input = "Прõсто текčт"; + String output = LanguageUtils.transliterate(input); + String result = "Prosto tekct"; + + assertTrue(String.format("Transliteration fail! Expected '%s', but found '%s'}", result, output), output.equals(result)); + } + + @Test + public void testTransliterateOption() throws Exception { + assertFalse("Transliteration option fail! Expected 'Off' by default, but result is 'On'", LanguageUtils.transliterate()); + + SharedPreferences settings = GBApplication.getPrefs().getPreferences(); + SharedPreferences.Editor editor = settings.edit(); + editor.putBoolean("transliteration", true); + editor.commit(); + + assertTrue("Transliteration option fail! Expected 'On', but result is 'Off'", LanguageUtils.transliterate()); + } +} From 4f45ad660d3f497e7354043ec5522afdc96b1be6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 22:56:09 +0100 Subject: [PATCH 172/244] Pebble: refactor PebbleKit stuff into its own class --- .../devices/pebble/PebbleIoThread.java | 108 ++-------------- .../devices/pebble/PebbleKitSupport.java | 117 ++++++++++++++++++ 2 files changed, 128 insertions(+), 97 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.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 83e26912..f4e6e31e 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 @@ -3,16 +3,12 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.net.Uri; import android.os.ParcelUuid; import android.support.v4.content.LocalBroadcastManager; -import org.json.JSONArray; -import org.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +23,6 @@ import java.net.InetAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; @@ -53,21 +48,11 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; class PebbleIoThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(PebbleIoThread.class); - public static final String PEBBLEKIT_ACTION_PEBBLE_CONNECTED = "com.getpebble.action.PEBBLE_CONNECTED"; - public static final String PEBBLEKIT_ACTION_PEBBLE_DISCONNECTED = "com.getpebble.action.PEBBLE_DISCONNECTED"; - public static final String PEBBLEKIT_ACTION_APP_ACK = "com.getpebble.action.app.ACK"; - public static final String PEBBLEKIT_ACTION_APP_NACK = "com.getpebble.action.app.NACK"; - public static final String PEBBLEKIT_ACTION_APP_RECEIVE = "com.getpebble.action.app.RECEIVE"; - public static final String PEBBLEKIT_ACTION_APP_RECEIVE_ACK = "com.getpebble.action.app.RECEIVE_ACK"; - public static final String PEBBLEKIT_ACTION_APP_RECEIVE_NACK = "com.getpebble.action.app.RECEIVE_NACK"; - public static final String PEBBLEKIT_ACTION_APP_SEND = "com.getpebble.action.app.SEND"; - 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"; - private final Prefs prefs = GBApplication.getPrefs(); private final PebbleProtocol mPebbleProtocol; private final PebbleSupport mPebbleSupport; + private PebbleKitSupport mPebbleKitSupport; private final boolean mEnablePebblekit; private boolean mIsTCP = false; @@ -94,59 +79,6 @@ class PebbleIoThread extends GBDeviceIoThread { private int mBinarySize = -1; private int mBytesWritten = -1; - private final BroadcastReceiver mPebbleKitReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - LOG.info("Got action: " + action); - UUID uuid; - switch (action) { - case PEBBLEKIT_ACTION_APP_START: - case PEBBLEKIT_ACTION_APP_STOP: - uuid = (UUID) intent.getSerializableExtra("uuid"); - if (uuid != null) { - write(mPebbleProtocol.encodeAppStart(uuid, action.equals(PEBBLEKIT_ACTION_APP_START))); - } - break; - case PEBBLEKIT_ACTION_APP_SEND: - int transaction_id = intent.getIntExtra("transaction_id", -1); - uuid = (UUID) intent.getSerializableExtra("uuid"); - String jsonString = intent.getStringExtra("msg_data"); - LOG.info("json string: " + jsonString); - - try { - JSONArray jsonArray = new JSONArray(jsonString); - write(mPebbleProtocol.encodeApplicationMessageFromJSON(uuid, jsonArray)); - if (transaction_id >= 0 && transaction_id <= 255) { - sendAppMessageAck(transaction_id); - } - } catch (JSONException e) { - e.printStackTrace(); - } - break; - case PEBBLEKIT_ACTION_APP_ACK: - transaction_id = intent.getIntExtra("transaction_id", -1); - if (transaction_id >= 0 && transaction_id <= 255) { - write(mPebbleProtocol.encodeApplicationMessageAck(null, (byte) transaction_id)); - } else { - LOG.warn("illegal transacktion id " + transaction_id); - } - break; - - } - } - }; - - private void sendAppMessageIntent(GBDeviceEventAppMessage appMessage) { - Intent intent = new Intent(); - intent.setAction(PEBBLEKIT_ACTION_APP_RECEIVE); - intent.putExtra("uuid", appMessage.appUUID); - intent.putExtra("msg_data", appMessage.message); - intent.putExtra("transaction_id", appMessage.id); - LOG.info("broadcasting to uuid " + appMessage.appUUID + " transaction id: " + appMessage.id + " JSON: " + appMessage.message); - getContext().sendBroadcast(intent); - } - PebbleIoThread(PebbleSupport pebbleSupport, GBDevice gbDevice, GBDeviceProtocol gbDeviceProtocol, BluetoothAdapter btAdapter, Context context) { super(gbDevice, context); mPebbleProtocol = (PebbleProtocol) gbDeviceProtocol; @@ -163,14 +95,6 @@ class PebbleIoThread extends GBDeviceIoThread { return ret; } - private void sendAppMessageAck(int transactionId) { - Intent intent = new Intent(); - intent.setAction(PEBBLEKIT_ACTION_APP_RECEIVE_ACK); - intent.putExtra("transaction_id", transactionId); - LOG.info("broadcasting ACK (transaction id " + transactionId + ")"); - getContext().sendBroadcast(intent); - } - @Override protected boolean connect() { String deviceAddress = gbDevice.getAddress(); @@ -244,7 +168,7 @@ class PebbleIoThread extends GBDeviceIoThread { } byte[] buffer = new byte[8192]; - enablePebbleKitReceiver(true); + enablePebbleKitSupport(true); mQuit = false; while (!mQuit) { try { @@ -420,7 +344,7 @@ class PebbleIoThread extends GBDeviceIoThread { mBtSocket = null; } - enablePebbleKitReceiver(false); + enablePebbleKitSupport(false); if (mQuit) { gbDevice.setState(GBDevice.State.NOT_CONNECTED); @@ -430,25 +354,13 @@ class PebbleIoThread extends GBDeviceIoThread { gbDevice.sendDeviceUpdateIntent(getContext()); } - private void enablePebbleKitReceiver(boolean enable) { - + private void enablePebbleKitSupport(boolean enable) { if (enable && mEnablePebblekit) { - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(PEBBLEKIT_ACTION_APP_ACK); - intentFilter.addAction(PEBBLEKIT_ACTION_APP_NACK); - intentFilter.addAction(PEBBLEKIT_ACTION_APP_SEND); - intentFilter.addAction(PEBBLEKIT_ACTION_APP_START); - intentFilter.addAction(PEBBLEKIT_ACTION_APP_STOP); - try { - getContext().registerReceiver(mPebbleKitReceiver, intentFilter); - } catch (IllegalArgumentException e) { - // ignore - } + mPebbleKitSupport = new PebbleKitSupport(getContext(), PebbleIoThread.this, mPebbleProtocol); } else { - try { - getContext().unregisterReceiver(mPebbleKitReceiver); - } catch (IllegalArgumentException e) { - // ignore + if (mPebbleKitSupport != null) { + mPebbleKitSupport.close(); + mPebbleKitSupport = null; } } } @@ -576,7 +488,9 @@ class PebbleIoThread extends GBDeviceIoThread { } else if (deviceEvent instanceof GBDeviceEventAppMessage) { if (mEnablePebblekit) { LOG.info("Got AppMessage event"); - sendAppMessageIntent((GBDeviceEventAppMessage) deviceEvent); + if (mPebbleKitSupport != null) { + mPebbleKitSupport.sendAppMessageIntent((GBDeviceEventAppMessage) deviceEvent); + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java new file mode 100644 index 00000000..6d05dc8c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -0,0 +1,117 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import org.json.JSONArray; +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppMessage; + +class PebbleKitSupport { + //private static final String PEBBLEKIT_ACTION_PEBBLE_CONNECTED = "com.getpebble.action.PEBBLE_CONNECTED"; + //private static final String PEBBLEKIT_ACTION_PEBBLE_DISCONNECTED = "com.getpebble.action.PEBBLE_DISCONNECTED"; + private static final String PEBBLEKIT_ACTION_APP_ACK = "com.getpebble.action.app.ACK"; + private static final String PEBBLEKIT_ACTION_APP_NACK = "com.getpebble.action.app.NACK"; + private static final String PEBBLEKIT_ACTION_APP_RECEIVE = "com.getpebble.action.app.RECEIVE"; + private static final String PEBBLEKIT_ACTION_APP_RECEIVE_ACK = "com.getpebble.action.app.RECEIVE_ACK"; + //private static final String PEBBLEKIT_ACTION_APP_RECEIVE_NACK = "com.getpebble.action.app.RECEIVE_NACK"; + private static final String PEBBLEKIT_ACTION_APP_SEND = "com.getpebble.action.app.SEND"; + private static final String PEBBLEKIT_ACTION_APP_START = "com.getpebble.action.app.START"; + private static final String PEBBLEKIT_ACTION_APP_STOP = "com.getpebble.action.app.STOP"; + + private static final Logger LOG = LoggerFactory.getLogger(PebbleKitSupport.class); + + private final PebbleProtocol mPebbleProtocol; + private final Context mContext; + private final PebbleIoThread mPebbleIoThread; + + private final BroadcastReceiver mPebbleKitReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + LOG.info("Got action: " + action); + UUID uuid; + switch (action) { + case PEBBLEKIT_ACTION_APP_START: + case PEBBLEKIT_ACTION_APP_STOP: + uuid = (UUID) intent.getSerializableExtra("uuid"); + if (uuid != null) { + mPebbleIoThread.write(mPebbleProtocol.encodeAppStart(uuid, action.equals(PEBBLEKIT_ACTION_APP_START))); + } + break; + case PEBBLEKIT_ACTION_APP_SEND: + int transaction_id = intent.getIntExtra("transaction_id", -1); + uuid = (UUID) intent.getSerializableExtra("uuid"); + String jsonString = intent.getStringExtra("msg_data"); + LOG.info("json string: " + jsonString); + + try { + JSONArray jsonArray = new JSONArray(jsonString); + mPebbleIoThread.write(mPebbleProtocol.encodeApplicationMessageFromJSON(uuid, jsonArray)); + if (transaction_id >= 0 && transaction_id <= 255) { + sendAppMessageAck(transaction_id); + } + } catch (JSONException e) { + e.printStackTrace(); + } + break; + case PEBBLEKIT_ACTION_APP_ACK: + transaction_id = intent.getIntExtra("transaction_id", -1); + if (transaction_id >= 0 && transaction_id <= 255) { + mPebbleIoThread.write(mPebbleProtocol.encodeApplicationMessageAck(null, (byte) transaction_id)); + } else { + LOG.warn("illegal transaction id " + transaction_id); + } + break; + + } + } + }; + + PebbleKitSupport(Context context, PebbleIoThread pebbleIoThread, PebbleProtocol pebbleProtocol) { + mContext = context; + mPebbleIoThread = pebbleIoThread; + mPebbleProtocol = pebbleProtocol; + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_ACK); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_NACK); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_SEND); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_START); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_STOP); + mContext.registerReceiver(mPebbleKitReceiver, intentFilter); + } + + void sendAppMessageIntent(GBDeviceEventAppMessage appMessage) { + Intent intent = new Intent(); + intent.setAction(PEBBLEKIT_ACTION_APP_RECEIVE); + intent.putExtra("uuid", appMessage.appUUID); + intent.putExtra("msg_data", appMessage.message); + intent.putExtra("transaction_id", appMessage.id); + LOG.info("broadcasting to uuid " + appMessage.appUUID + " transaction id: " + appMessage.id + " JSON: " + appMessage.message); + mContext.sendBroadcast(intent); + } + + private void sendAppMessageAck(int transactionId) { + Intent intent = new Intent(); + intent.setAction(PEBBLEKIT_ACTION_APP_RECEIVE_ACK); + intent.putExtra("transaction_id", transactionId); + LOG.info("broadcasting ACK (transaction id " + transactionId + ")"); + mContext.sendBroadcast(intent); + } + + void close() { + try { + mContext.unregisterReceiver(mPebbleKitReceiver); + } catch (IllegalArgumentException ignore) { + } + } + +} From 25433ef6bc22ad1a9636fc17b13337d7c374cce8 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 24 Jan 2017 23:12:36 +0100 Subject: [PATCH 173/244] Pebble: do not display a toast when watchapp configuration could not be found during initialization of appmessage handler Unfortunately all users without TimeStyle installed got an error in Gadgetbridge 0.17.2 --- .../service/devices/pebble/AppMessageHandlerHealthify.java | 5 +++-- .../service/devices/pebble/AppMessageHandlerMorpheuz.java | 5 +++-- .../service/devices/pebble/AppMessageHandlerSquare.java | 5 +++-- .../devices/pebble/AppMessageHandlerTimeStylePebble.java | 5 +++-- .../service/devices/pebble/AppMessageHandlerTrekVolle.java | 5 +++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java index e45c4a16..830e31e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java @@ -28,8 +28,9 @@ class AppMessageHandlerHealthify extends AppMessageHandler { JSONObject appKeys = getAppKeys(); KEY_TEMPERATURE = appKeys.getInt("TEMPERATURE"); KEY_CONDITIONS = appKeys.getInt("CONDITIONS"); - } catch (IOException | JSONException e) { - GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (JSONException e) { + GB.toast("There was an error accessing the Helthify watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (IOException ignore) { } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 0e05d0bf..3bfaca48 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -71,8 +71,9 @@ class AppMessageHandlerMorpheuz extends AppMessageHandler { keyAutoReset = appKeys.getInt("keyAutoReset"); keySnoozes = appKeys.getInt("keySnoozes"); keyFault = appKeys.getInt("keyFault"); - } catch (IOException | JSONException e) { - GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (JSONException e) { + GB.toast("There was an error accessing the morpheuz watchapp configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (IOException ignore) { } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java index 8a080037..a3aafa59 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java @@ -34,8 +34,9 @@ class AppMessageHandlerSquare extends AppMessageHandler { CfgKeyWeatherMode = appKeys.getInt("CfgKeyWeatherMode"); CfgKeyUseCelsius = appKeys.getInt("CfgKeyUseCelsius"); CfgKeyWeatherLocation = appKeys.getInt("CfgKeyWeatherLocation"); - } catch (IOException | JSONException e) { - GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (JSONException e) { + GB.toast("There was an error accessing the Square watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (IOException ignore) { } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 6ca62975..a8fb16ca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -53,8 +53,9 @@ class AppMessageHandlerTimeStylePebble extends AppMessageHandler { break; } } - } catch (IOException | JSONException e) { - GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (JSONException e) { + GB.toast("There was an error accessing the timestyle watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (IOException ignore) { } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java index e1f30463..014a2166 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java @@ -35,8 +35,9 @@ class AppMessageHandlerTrekVolle extends AppMessageHandler { MESSAGE_KEY_WEATHER_TEMPERATURE_MIN = appKeys.getInt("WEATHER_TEMPERATURE_MIN"); MESSAGE_KEY_WEATHER_TEMPERATURE_MAX = appKeys.getInt("WEATHER_TEMPERATURE_MAX"); MESSAGE_KEY_WEATHER_LOCATION = appKeys.getInt("WEATHER_LOCATION"); - } catch (IOException | JSONException e) { - GB.toast("There was an error accessing the watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (JSONException e) { + GB.toast("There was an error accessing the TrekVolle watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (IOException ignore) { } } From 2b632d8b3944cc3d978c12f57072045cf3b1d0c2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 25 Jan 2017 22:15:21 +0100 Subject: [PATCH 174/244] bump version, update changelog --- CHANGELOG.md | 6 ++++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5abc7e0b..7d015ea8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ###Changelog +####Version 0.17.3 (next) +* HPlus: Improve display of new messages and phone calls +* HPlus: Fix bug related to steps and heart rate +* Pebble: Support dynamic keys for natively supported watchfaces and watchapps (more stability accross versions) +* Pebble: Fix error Toast being displayed when TimeStyle watchface is not installed + ####Version 0.17.2 * Pebble: Fix temperature unit in Timestyle Pebble watchface * Add optional Cyrillic transliteration (for devices lacking the font) diff --git a/app/build.gradle b/app/build.gradle index 17ca06fd..a86bb12b 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.17.2" - versionCode 83 + versionName "0.17.3" + versionCode 84 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 1c84aab9..772f7c45 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,11 @@ + + HPlus: Improve display of new messages and phone calls + HPlus: Fix bug related to steps and heart rate + Pebble: Support dynamic keys for natively supported watchfaces and watchapps (more stability accross versions) + Pebble: Fix error Toast being displayed when TimeStyle watchface is not installed + Pebble: Fix temperature unit in Timestyle Pebble watchface Add optional Cyrillic transliteration (for devices lacking the font) From 083cbdbfbee5f9ad20847b0b976eac81de3f67d7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 25 Jan 2017 22:17:35 +0100 Subject: [PATCH 175/244] update Spanish and Japanese from transifex (THANKS) --- app/src/main/res/values-es/strings.xml | 2 ++ app/src/main/res/values-ja/strings.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1acab334..a6de97f2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -69,6 +69,8 @@ … también con pantalla encendida No Molestar Dejar de enviar notificaciones no deseadas basándose en el modo No Molestar + Transcripción + Utilizar en caso de que tu dispositivo no soporte la fuente de tu idioma (de momento solo el Cirílico) siempre cuando la pantalla está apagada nunca diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index c5daf71f..6071e09e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -69,6 +69,8 @@ … スクリーンがオンのときにも サイレント サイレントモードに基づいて、送信される不要な通知を停止します。 + 音訳 + デバイスでお使いの言語のフォントがサポートされていない場合に、使用してください (現在はキリル文字のみ) いつも スクリーンがオフのとき なし From d103d09fcf3bff895555f750d27a68f4cc0c5d03 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 25 Jan 2017 21:13:10 +0100 Subject: [PATCH 176/244] Mi Band: just a method rename --- .../gadgetbridge/devices/miband/MiBandPairingActivity.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java index 89424a1f..2e8e665e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java @@ -86,7 +86,7 @@ public class MiBandPairingActivity extends GBActivity { new Handler(mainLooper).postDelayed(new Runnable() { @Override public void run() { - performPair(); + performApplicationLevelPair(); } }, DELAY_AFTER_BONDING); } @@ -210,7 +210,7 @@ public class MiBandPairingActivity extends GBActivity { 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); - performPair(); + performApplicationLevelPair(); return; } @@ -226,7 +226,7 @@ public class MiBandPairingActivity extends GBActivity { } } - private void performPair() { + private void performApplicationLevelPair() { GBApplication.deviceService().disconnect(); // just to make sure... GBApplication.deviceService().connect(macAddress, true); } From 4c26c2933b88e2cbb116420011d82c0e37770444 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 25 Jan 2017 22:59:32 +0100 Subject: [PATCH 177/244] Mi Band 1+2: make BT pairing optional (Attemts to fix #512, #514, #518) --- .../devices/miband/MiBandConst.java | 1 + .../devices/miband/MiBandPairingActivity.java | 25 ++++++++++++++----- .../miband/MiBandPreferencesActivity.java | 1 + app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/miband_preferences.xml | 5 ++++ 5 files changed, 28 insertions(+), 6 deletions(-) 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 f4d5ec9f..98877a83 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 @@ -19,6 +19,7 @@ public final class MiBandConst { 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_MI2_ACTIVATE_DISPLAY_ON_LIFT = "mi2_activate_display_on_lift_wrist"; + public static final String PREF_MIBAND_SETUP_BT_PAIRING = "mi_setup_bt_pairing"; public static final String ORIGIN_INCOMING_CALL = "incoming_call"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java index 2e8e665e..878352ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java @@ -159,20 +159,33 @@ public class MiBandPairingActivity extends GBActivity { } private void startPairing() { + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); + if (device == null) { + GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR); + return; + } + isPairing = true; message.setText(getString(R.string.pairing, macAddress)); + if (!shouldSetupBTLevelPairing()) { + // there are connection problems on certain Galaxy S devices at least; + // try to connect without BT pairing (bonding) + attemptToConnect(); + return; + } + 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) { - performBluetoothPair(device); - } else { - GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR); - } + performBluetoothPair(device); + } + + private boolean shouldSetupBTLevelPairing() { + Prefs prefs = GBApplication.getPrefs(); + return prefs.getPreferences().getBoolean(MiBandConst.PREF_MIBAND_SETUP_BT_PAIRING, true); } private void pairingFinished(boolean pairedSuccessfully, String macAddress) { 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 00b77c06..caeb4e2a 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 @@ -25,6 +25,7 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_FITNESS_GOAL; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_SETUP_BT_PAIRING; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_ALIAS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_COUNT; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefKey; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 26694f18..0b53f84a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -380,4 +380,6 @@ 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 Make sure that this skin is enabled in the Weather Notification app to get weather information on your Pebble.\n\nNo configuration is needed here.\n\nYou can enable the system weather app of your Pebble from the app management.\n\nSupported watchfaces will show the weather automatically. + Enable Bluetooth pairing + Deactivate this if you have trouble connecting diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 6b0e1032..ed215a5a 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -275,6 +275,11 @@ + Date: Thu, 26 Jan 2017 00:06:11 +0100 Subject: [PATCH 178/244] Fix parceling GBDeviceCandidate --- .../freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3696ec5d..cec517a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -57,7 +57,7 @@ public class GBDeviceCandidate implements Parcelable { dest.writeParcelable(device, 0); dest.writeInt(rssi); dest.writeString(deviceType.name()); - dest.writeArray(serviceUuds); + dest.writeParcelableArray(serviceUuds, 0); } public static final Creator CREATOR = new Creator() { From ac68bfe3515506c1b4cf2e80a089df7f4d5d5073 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 26 Jan 2017 00:11:52 +0100 Subject: [PATCH 179/244] Mi Band 1+2: backend implementation of making BT pairing optional - cleaned up the DeviceService.connect() variants - discovery: pass the device candidate around instead of the mac address Attempts to fix #512, #514, #518 --- .../activities/DiscoveryActivity.java | 2 +- .../devices/DeviceCoordinator.java | 2 +- .../devices/miband/MiBandPairingActivity.java | 66 ++++++++++--------- .../devices/pebble/PebblePairingActivity.java | 7 +- .../gadgetbridge/impl/GBDeviceService.java | 17 ++--- .../gadgetbridge/model/DeviceService.java | 8 +-- .../service/DeviceCommunicationService.java | 10 ++- .../gadgetbridge/util/AndroidUtils.java | 33 ++++++++++ .../gadgetbridge/util/DeviceHelper.java | 3 + 9 files changed, 89 insertions(+), 59 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 b883fc1c..8016041e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -509,7 +509,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC Class pairingActivity = coordinator.getPairingActivity(); if (pairingActivity != null) { Intent intent = new Intent(this, pairingActivity); - intent.putExtra(DeviceCoordinator.EXTRA_DEVICE_MAC_ADDRESS, deviceCandidate.getMacAddress()); + intent.putExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE, deviceCandidate); startActivity(intent); } else { try { 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 6f0fb39d..ef872cf3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -27,7 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; * the given device. */ public interface DeviceCoordinator { - String EXTRA_DEVICE_MAC_ADDRESS = "nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate.EXTRA_MAC_ADDRESS"; + String EXTRA_DEVICE_CANDIDATE = "nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate.EXTRA_DEVICE_CANDIDATE"; /** * Checks whether this coordinator handles the given candidate. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java index 878352ab..9f825a7b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java @@ -23,6 +23,9 @@ 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.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; +import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -34,7 +37,7 @@ public class MiBandPairingActivity extends GBActivity { private static final long DELAY_AFTER_BONDING = 1000; // 1s private TextView message; private boolean isPairing; - private String macAddress; + private GBDeviceCandidate deviceCandidate; private String bondingMacAddress; private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() { @@ -43,9 +46,9 @@ public class MiBandPairingActivity 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 (deviceCandidate.getMacAddress().equals(device.getAddress())) { if (device.isInitialized()) { - pairingFinished(true, macAddress); + pairingFinished(true, deviceCandidate); } else if (device.isConnecting() || device.isInitializing()) { LOG.info("still connecting/initializing device..."); } @@ -74,7 +77,7 @@ public class MiBandPairingActivity extends GBActivity { attemptToConnect(); } else { LOG.warn("Unknown bond state for device " + device.getAddress() + ": " + bondState); - pairingFinished(false, bondingMacAddress); + pairingFinished(false, deviceCandidate); } } } @@ -98,11 +101,11 @@ public class MiBandPairingActivity extends GBActivity { message = (TextView) findViewById(R.id.miband_pair_message); Intent intent = getIntent(); - macAddress = intent.getStringExtra(DeviceCoordinator.EXTRA_DEVICE_MAC_ADDRESS); - if (macAddress == null && savedInstanceState != null) { - macAddress = savedInstanceState.getString(STATE_MIBAND_ADDRESS, null); + deviceCandidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE); + if (deviceCandidate == null && savedInstanceState != null) { + deviceCandidate = savedInstanceState.getParcelable(STATE_MIBAND_ADDRESS); } - if (macAddress == null) { + if (deviceCandidate == 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(); @@ -122,13 +125,13 @@ public class MiBandPairingActivity extends GBActivity { @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(STATE_MIBAND_ADDRESS, macAddress); + outState.putParcelable(STATE_MIBAND_ADDRESS, deviceCandidate); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - macAddress = savedInstanceState.getString(STATE_MIBAND_ADDRESS, macAddress); + deviceCandidate = savedInstanceState.getParcelable(STATE_MIBAND_ADDRESS); } @Override @@ -145,13 +148,9 @@ public class MiBandPairingActivity extends GBActivity { @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 - } + // just to be sure, remove the receivers -- might actually be already unregistered + AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver); + AndroidUtils.safeUnregisterBroadcastReceiver(this, mBondingReceiver); if (isPairing) { stopPairing(); } @@ -159,14 +158,11 @@ public class MiBandPairingActivity extends GBActivity { } private void startPairing() { - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); - if (device == null) { - GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR); - return; - } - isPairing = true; - message.setText(getString(R.string.pairing, macAddress)); + message.setText(getString(R.string.pairing, deviceCandidate)); + + IntentFilter filter = new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED); + LocalBroadcastManager.getInstance(this).registerReceiver(mPairingReceiver, filter); if (!shouldSetupBTLevelPairing()) { // there are connection problems on certain Galaxy S devices at least; @@ -175,12 +171,10 @@ public class MiBandPairingActivity extends GBActivity { return; } - 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); - performBluetoothPair(device); + performBluetoothPair(deviceCandidate); } private boolean shouldSetupBTLevelPairing() { @@ -188,7 +182,7 @@ public class MiBandPairingActivity extends GBActivity { return prefs.getPreferences().getBoolean(MiBandConst.PREF_MIBAND_SETUP_BT_PAIRING, true); } - private void pairingFinished(boolean pairedSuccessfully, String macAddress) { + private void pairingFinished(boolean pairedSuccessfully, GBDeviceCandidate candidate) { LOG.debug("pairingFinished: " + pairedSuccessfully); if (!isPairing) { // already gone? @@ -196,13 +190,14 @@ public class MiBandPairingActivity extends GBActivity { } isPairing = false; - LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver); - unregisterReceiver(mBondingReceiver); + AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver); + AndroidUtils.safeUnregisterBroadcastReceiver(this, mBondingReceiver); if (pairedSuccessfully) { // remember the device since we do not necessarily pair... temporary -- we probably need // to query the db for available devices in ControlCenter. But only remember un-bonded // devices, as bonded devices are displayed anyway. + String macAddress = deviceCandidate.getMacAddress(); BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) { Prefs prefs = GBApplication.getPrefs(); @@ -219,7 +214,9 @@ public class MiBandPairingActivity extends GBActivity { isPairing = false; } - protected void performBluetoothPair(BluetoothDevice device) { + protected void performBluetoothPair(GBDeviceCandidate deviceCandidate) { + BluetoothDevice device = deviceCandidate.getDevice(); + 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); @@ -241,6 +238,11 @@ public class MiBandPairingActivity extends GBActivity { private void performApplicationLevelPair() { GBApplication.deviceService().disconnect(); // just to make sure... - GBApplication.deviceService().connect(macAddress, true); + GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate); + if (device != null) { + GBApplication.deviceService().connect(device, true); + } else { + GB.toast(this, "Unable to connect, can't recognize the device type: " + deviceCandidate, Toast.LENGTH_LONG, GB.ERROR); + } } } 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 2cf3b545..3fb16918 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 @@ -7,6 +7,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; +import android.os.Parcelable; import android.support.v4.content.LocalBroadcastManager; import android.widget.TextView; import android.widget.Toast; @@ -28,6 +29,7 @@ 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.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -90,7 +92,10 @@ public class PebblePairingActivity extends GBActivity { message = (TextView) findViewById(R.id.pebble_pair_message); Intent intent = getIntent(); - macAddress = intent.getStringExtra(DeviceCoordinator.EXTRA_DEVICE_MAC_ADDRESS); + GBDeviceCandidate candidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE); + if (candidate != null) { + macAddress = candidate.getMacAddress(); + } if (macAddress == null) { Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show(); returnToPairingActivity(); 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 949791c7..bee14d7d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -53,21 +53,14 @@ public class GBDeviceService implements DeviceService { } @Override - public void connect(GBDevice device) { - Intent intent = createIntent().setAction(ACTION_CONNECT) - .putExtra(GBDevice.EXTRA_DEVICE, device); - invokeService(intent); + public void connect(@Nullable GBDevice device) { + connect(device, false); } - @Override - public void connect(@Nullable String deviceAddress) { - connect(deviceAddress, false); - } - - @Override - public void connect(@Nullable String deviceAddress, boolean performPair) { + @Override + public void connect(@Nullable GBDevice device, boolean performPair) { Intent intent = createIntent().setAction(ACTION_CONNECT) - .putExtra(EXTRA_DEVICE_ADDRESS, deviceAddress) + .putExtra(GBDevice.EXTRA_DEVICE, device) .putExtra(EXTRA_PERFORM_PAIR, performPair); invokeService(intent); } 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 f03a58d5..a7be20f8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -47,7 +47,6 @@ public interface DeviceService extends EventHandler { String ACTION_SEND_CONFIGURATION = PREFIX + ".action.send_configuration"; String ACTION_SEND_WEATHER = PREFIX + ".action.send_weather"; String ACTION_TEST_NEW_FUNCTION = PREFIX + ".action.test_new_function"; - String EXTRA_DEVICE_ADDRESS = "device_address"; String EXTRA_NOTIFICATION_BODY = "notification_body"; String EXTRA_NOTIFICATION_FLAGS = "notification_flags"; String EXTRA_NOTIFICATION_ID = "notification_id"; @@ -112,17 +111,14 @@ public interface DeviceService extends EventHandler { String EXTRA_CALENDAREVENT_DURATION = "calendarevent_duration"; String EXTRA_CALENDAREVENT_TITLE = "calendarevent_title"; String EXTRA_CALENDAREVENT_DESCRIPTION = "calendarevent_description"; - String EXTRA_MIBAND2_AUTH_BYTE = "miband2_auth_byte"; void start(); void connect(); - void connect(GBDevice device); + void connect(@Nullable GBDevice device); - void connect(@Nullable String deviceAddress); - - void connect(@Nullable String deviceAddress, boolean performPair); + void connect(@Nullable GBDevice device, boolean performPair); void disconnect(); 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 b2d9be35..a376dfe6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -102,7 +102,6 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CAL 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; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST; @@ -278,12 +277,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere GBDevice gbDevice = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE); String btDeviceAddress = null; if (gbDevice == null) { - btDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS); - if (btDeviceAddress == null && prefs != null) { // may be null in test cases + if (prefs != null) { // may be null in test cases btDeviceAddress = prefs.getString("last_device_address", null); - } - if (btDeviceAddress != null) { - gbDevice = DeviceHelper.getInstance().findAvailableDevice(btDeviceAddress, this); + if (btDeviceAddress != null) { + gbDevice = DeviceHelper.getInstance().findAvailableDevice(btDeviceAddress, this); + } } } else { btDeviceAddress = gbDevice.getAddress(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index f08691e2..a343c49b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -1,7 +1,10 @@ package nodomain.freeyourgadget.gadgetbridge.util; +import android.content.BroadcastReceiver; +import android.content.Context; import android.os.ParcelUuid; import android.os.Parcelable; +import android.support.v4.content.LocalBroadcastManager; public class AndroidUtils { public static ParcelUuid[] toParcelUUids(Parcelable[] uuids) { @@ -12,4 +15,34 @@ public class AndroidUtils { System.arraycopy(uuids, 0, uuids2, 0, uuids.length); return uuids2; } + + /** + * Unregisters the given receiver from the given context. + * @param context the context from which to unregister + * @param receiver the receiver to unregister + * @return true if it was successfully unregistered, or false if the receiver was not registered + */ + public static boolean safeUnregisterBroadcastReceiver(Context context, BroadcastReceiver receiver) { + try { + context.unregisterReceiver(receiver); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Unregisters the given receiver from the given {@link LocalBroadcastManager}. + * @param manager the manager from which to unregister + * @param receiver the receiver to unregister + * @return true if it was successfully unregistered, or false if the receiver was not registered + */ + public static boolean safeUnregisterBroadcastReceiver(LocalBroadcastManager manager, BroadcastReceiver receiver) { + try { + manager.unregisterReceiver(receiver); + return true; + } catch (IllegalArgumentException ex) { + 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 c57abb93..0fbd5451 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -123,7 +123,10 @@ public class DeviceHelper { public GBDevice toSupportedDevice(BluetoothDevice device) { GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN, device.getUuids()); + return toSupportedDevice(candidate); + } + public GBDevice toSupportedDevice(GBDeviceCandidate candidate) { for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(candidate)) { return coordinator.createDevice(candidate); From 5d3028c123a293aa0d9b0a4ee573a26409cdf383 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 26 Jan 2017 00:17:50 +0100 Subject: [PATCH 180/244] Mi1+2: Updated changelog --- 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 7d015ea8..3b2aba42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * HPlus: Fix bug related to steps and heart rate * Pebble: Support dynamic keys for natively supported watchfaces and watchapps (more stability accross versions) * Pebble: Fix error Toast being displayed when TimeStyle watchface is not installed +* Mi Band 1+2: Support for connecting wihout BT pairing (workaround for certain connection problems) ####Version 0.17.2 * Pebble: Fix temperature unit in Timestyle Pebble watchface diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 772f7c45..d8fef016 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -8,6 +8,7 @@ Pebble: Fix temperature unit in Timestyle Pebble watchface + Mi Band 1+2: Support for connecting wihout BT pairing (workaround for certain connection problems) Add optional Cyrillic transliteration (for devices lacking the font) From b3e1cbf55eeb73baefcfc69c5d64b5b068622393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Mon, 23 Jan 2017 00:08:36 +0000 Subject: [PATCH 181/244] HPlus: Support of Makibes F68 and small fixes to HPlus devices --- .../devices/hplus/HPlusConstants.java | 6 +- .../devices/hplus/HPlusCoordinator.java | 8 +- .../hplus/HPlusHealthSampleProvider.java | 36 +++-- .../devices/hplus/MakibesF68Coordinator.java | 33 +++++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 6 +- .../devices/hplus/HPlusDataRecordDaySlot.java | 21 +-- .../hplus/HPlusDataRecordRealtime.java | 4 +- .../devices/hplus/HPlusHandlerThread.java | 135 ++++++++---------- .../service/devices/hplus/HPlusSupport.java | 132 +++++++++++------ .../gadgetbridge/util/DeviceHelper.java | 3 + 11 files changed, 232 insertions(+), 153 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 01e619bf..73515ee7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -15,8 +15,8 @@ public final class HPlusConstants { public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); - public static final byte ARG_COUNTRY_CN = 1; - public static final byte ARG_COUNTRY_OTHER = 2; + public static final byte ARG_LANGUAGE_CN = 1; + public static final byte ARG_LANGUAGE_EN = 2; public static final byte ARG_TIMEMODE_24H = 0; public static final byte ARG_TIMEMODE_12H = 1; @@ -111,7 +111,7 @@ public final class HPlusConstants { public static final String PREF_HPLUS_ALERT_TIME = "hplus_alert_time"; public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time"; public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time"; - public static final String PREF_HPLUS_COUNTRY = "hplus_country"; + public static final String PREF_HPLUS_LANGUAGE = "hplus_language"; public static final Map transliterateMap = new HashMap(){ { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 8a596d4c..8495e664 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -38,8 +38,8 @@ import java.util.Collection; import java.util.Collections; public class HPlusCoordinator extends AbstractDeviceCoordinator { - private static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); - private static Prefs prefs = GBApplication.getPrefs(); + protected static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); + protected static Prefs prefs = GBApplication.getPrefs(); @NonNull @Override @@ -144,8 +144,8 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return activityUser.getStepsGoal(); } - public static byte getCountry(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_COUNTRY + "_" + address, 10); + public static byte getLanguage(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_LANGUAGE + "_" + address, HPlusConstants.ARG_LANGUAGE_EN); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java index 9a7fa8c1..23f4090e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -141,30 +141,46 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider= today.getTimeInMillis() / 1000){ - //Only consider these for the current day as a single message is enough for steps - //HR and Overlays will still benefit from the full set of samples + + /**Strategy is: + * Calculate max steps from realtime messages + * Calculate sum of steps from day 10 minute slot summaries + */ + if(sample.getRawKind() == HPlusDataRecord.TYPE_REALTIME) { - int aux = sample.getSteps(); - sample.setSteps(sample.getSteps() - stepsToday); - stepsToday = aux; - }else - sample.setSteps(ActivitySample.NOT_MEASURED); + stepsTodayMax = Math.max(stepsTodayMax, sample.getSteps()); + }else if(sample.getRawKind() == HPlusDataRecord.TYPE_DAY_SLOT) { + stepsTodayCount += sample.getSteps(); + } + + sample.setSteps(ActivitySample.NOT_MEASURED); + lastSample = sample; }else{ if (sample.getRawKind() != HPlusDataRecord.TYPE_DAY_SUMMARY) { - sample.setSteps(ActivityKind.TYPE_NOT_MEASURED); + sample.setSteps(ActivitySample.NOT_MEASURED); } } } + if(lastSample != null) + lastSample.setSteps(Math.max(stepsTodayCount, stepsTodayMax)); + for (HPlusHealthActivityOverlay overlay : overlayRecords) { //Create fake events to improve activity counters if there are no events around the overlay //timestamp boundaries + //Insert one before, one at the beginning, one at the end, and one 1s after. + insertVirtualItem(samples, Math.max(overlay.getTimestampFrom() - 1, timestamp_from), overlay.getDeviceId(), overlay.getUserId()); insertVirtualItem(samples, Math.max(overlay.getTimestampFrom(), timestamp_from), overlay.getDeviceId(), overlay.getUserId()); insertVirtualItem(samples, Math.min(overlay.getTimestampTo() - 1, timestamp_to - 1), overlay.getDeviceId(), overlay.getUserId()); + insertVirtualItem(samples, Math.min(overlay.getTimestampTo(), timestamp_to), overlay.getDeviceId(), overlay.getUserId()); + for (HPlusHealthActivitySample sample : samples) { if (sample.getTimestamp() >= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) { @@ -191,7 +207,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider mDaySlotSamples = new ArrayList<>(); + List mDaySlotRecords = new ArrayList<>(); + + private HPlusDataRecordDaySlot mCurrentDaySlot = null; public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) { super(gbDevice, context); @@ -113,10 +109,6 @@ class HPlusHandlerThread extends GBDeviceIoThread { Calendar now = GregorianCalendar.getInstance(); - if (now.compareTo(mHelloTime) > 0) { - sendHello(); - } - if (now.compareTo(mGetDaySlotsTime) > 0) { requestNextDaySlots(); } @@ -130,7 +122,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { } now = GregorianCalendar.getInstance(); - waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()), mHelloTime.getTimeInMillis())) - now.getTimeInMillis(); + waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis())) - now.getTimeInMillis(); } } @@ -152,54 +144,22 @@ class HPlusHandlerThread extends GBDeviceIoThread { mSlotsInitialSync = true; mLastSlotReceived = -1; mLastSlotRequested = 0; + mCurrentDaySlot = null; + mDaySlotRecords.clear(); TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); - builder.wait(400); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); - builder.wait(400); - - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); - builder.wait(400); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); - builder.wait(400); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY}); - builder.wait(400); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA}); builder.queue(mHPlusSupport.getQueue()); - scheduleHello(); synchronized (waitObject) { waitObject.notify(); } } - /** - * Send an Hello/Null Packet to keep connection - */ - private void sendHello() { - TransactionBuilder builder = new TransactionBuilder("hello"); - - builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO); - builder.queue(mHPlusSupport.getQueue()); - - scheduleHello(); - - synchronized (waitObject) { - waitObject.notify(); - } - } - - /** - * Schedule an Hello Packet in the future - */ - public void scheduleHello(){ - mHelloTime = GregorianCalendar.getInstance(); - mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL); - } - /** * Process a message containing information regarding a day slot * A slot summarizes 10 minutes of data @@ -218,32 +178,56 @@ class HPlusHandlerThread extends GBDeviceIoThread { return false; } - //Ignore real time messages as they are still not understood - if(!mSlotsInitialSync){ - mGetDaySlotsTime.set(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); - return true; - } - Calendar now = GregorianCalendar.getInstance(); int nowSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + (now.get(Calendar.MINUTE) / 10); - - //If the slot is in the future, actually it is from the previous day - //Subtract a day of seconds - if(record.slot >= nowSlot){ - record.timestamp -= 3600 * 24; + if(record.slot == nowSlot){ + if(mCurrentDaySlot != null && mCurrentDaySlot != record){ + mCurrentDaySlot.accumulate(record); + mDaySlotRecords.add(mCurrentDaySlot); + mCurrentDaySlot = null; + }else{ + //Store it to a temp variable as this is an intermediate value + mCurrentDaySlot = record; + if(!mSlotsInitialSync) + return true; + } } - //Ignore out of order messages - if(record.slot == mLastSlotReceived + 1) { - mLastSlotReceived = record.slot; + if(mSlotsInitialSync) { + + //If the slot is in the future, actually it is from the previous day + //Subtract a day of seconds + if(record.slot > nowSlot){ + record.timestamp -= 3600 * 24; + } + + if (record.slot == mLastSlotReceived + 1) { + mLastSlotReceived = record.slot; + } + + //Ignore the current slot as it is incomplete + if(record.slot != nowSlot) + mDaySlotRecords.add(record); + + //Still fetching ring buffer. Request the next slots + if (record.slot == mLastSlotRequested) { + mGetDaySlotsTime.clear(); + synchronized (waitObject) { + waitObject.notify(); + } + } + + //Keep buffering + if(record.slot != 143) + return true; + } else { + mGetDaySlotsTime = GregorianCalendar.getInstance(); + mGetDaySlotsTime.add(Calendar.DAY_OF_MONTH, 1); } - if(record.slot < 143){ - mDaySlotSamples.add(record); - }else { - + if(mDaySlotRecords.size() > 0) { //Sort the samples - Collections.sort(mDaySlotSamples, new Comparator() { + Collections.sort(mDaySlotRecords, new Comparator() { public int compare(HPlusDataRecordDaySlot one, HPlusDataRecordDaySlot other) { return one.timestamp - other.timestamp; } @@ -253,20 +237,20 @@ class HPlusHandlerThread extends GBDeviceIoThread { HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); List samples = new ArrayList<>(); - for(HPlusDataRecordDaySlot storedRecord : mDaySlotSamples) { + for (HPlusDataRecordDaySlot storedRecord : mDaySlotRecords) { HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp); - sample.setRawHPlusHealthData(record.getRawData()); - sample.setSteps(record.steps); - sample.setHeartRate(record.heartRate); - sample.setRawKind(record.type); + sample.setRawHPlusHealthData(storedRecord.getRawData()); + sample.setSteps(storedRecord.steps); + sample.setHeartRate(storedRecord.heartRate); + sample.setRawKind(storedRecord.type); sample.setProvider(provider); samples.add(sample); } provider.getSampleDao().insertOrReplaceInTx(samples); - mDaySlotSamples.clear(); + mDaySlotRecords.clear(); } catch (GBException ex) { LOG.debug((ex.getMessage())); @@ -274,13 +258,6 @@ class HPlusHandlerThread extends GBDeviceIoThread { LOG.debug(ex.getMessage()); } } - //Still fetching ring buffer. Request the next slots - if (record.slot == mLastSlotRequested) { - mGetDaySlotsTime.clear(); - synchronized (waitObject) { - waitObject.notify(); - } - } return true; } @@ -357,6 +334,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { LOG.debug((e.getMessage())); return false; } + //Skip duplicated messages as the device seems to send the same record multiple times //This can be used to detect the user is moving (not sleeping) if(prevRealTimeRecord != null && record.same(prevRealTimeRecord)) @@ -490,7 +468,6 @@ class HPlusHandlerThread extends GBDeviceIoThread { * Messages will be provided every 10 minutes after they are available */ private void requestNextDaySlots() { - Calendar now = GregorianCalendar.getInstance(); int currentSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + now.get(Calendar.MINUTE) / 10; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index c410537c..4bf02c55 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; @@ -54,6 +55,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { public BluetoothGattCharacteristic measureCharacteristic = null; private HPlusHandlerThread syncHelper; + private DeviceType deviceType = DeviceType.UNKNOWN; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -65,8 +67,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } }; - public HPlusSupport() { + public HPlusSupport(DeviceType type) { super(LOG); + + deviceType = type; + addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); addSupportedService(HPlusConstants.UUID_SERVICE_HP); @@ -75,7 +80,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { IntentFilter intentFilter = new IntentFilter(); broadcastManager.registerReceiver(mReceiver, intentFilter); - } @Override @@ -107,7 +111,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { sendUserInfo(builder); //Sync preferences setSIT(builder); //Sync SIT Interval setCurrentDate(builder); // Sync Current Date + setDayOfWeek(builder); setCurrentTime(builder); // Sync Current Time + setLanguage(builder); requestDeviceInfo(builder); @@ -133,51 +139,69 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport syncPreferences(TransactionBuilder transaction) { - byte gender = HPlusCoordinator.getUserGender(getDevice().getAddress()); - byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); - byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); - byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); - int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); - byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); - byte country = HPlusCoordinator.getCountry(getDevice().getAddress()); - byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? - byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); - byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); - byte alertTimeHour = 0; - byte alertTimeMinute = 0; - if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { - int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); + if(deviceType == DeviceType.HPLUS) { + byte gender = HPlusCoordinator.getUserGender(getDevice().getAddress()); + byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); + byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); + byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + byte country = HPlusCoordinator.getLanguage(getDevice().getAddress()); + byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? + byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); + byte alertTimeHour = 0; + byte alertTimeMinute = 0; - alertTimeHour = (byte) ((t / 256) & 0xff); - alertTimeMinute = (byte) (t % 256); + if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { + int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); + + alertTimeHour = (byte) ((t / 256) & 0xff); + alertTimeMinute = (byte) (t % 256); + } + + byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); + byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.CMD_SET_PREFS, + gender, + age, + bodyHeight, + bodyWeight, + 0, + 0, + (byte) ((goal / 256) & 0xff), + (byte) (goal % 256), + displayTime, + country, + 0, + social, + allDayHeart, + wrist, + 0, + alertTimeHour, + alertTimeMinute, + unit, + timemode + }); + + }else if(deviceType == DeviceType.MAKIBESF68){ + //Makibes doesn't support setting everything at once. + + setGender(transaction); + setAge(transaction); + setWeight(transaction); + setHeight(transaction); + setGoal(transaction); + setLanguage(transaction); + setScreenTime(transaction); + //setAlarm(transaction, t); + setUnit(transaction); + setTimeMode(transaction); } - byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); - byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); - - transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.CMD_SET_PREFS, - gender, - age, - bodyHeight, - bodyWeight, - 0, - 0, - (byte) ((goal / 256) & 0xff), - (byte) (goal % 256), - displayTime, - country, - 0, - social, - allDayHeart, - wrist, - 0, - alertTimeHour, - alertTimeMinute, - unit, - timemode - }); setAllDayHeart(transaction); @@ -185,7 +209,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private HPlusSupport setLanguage(TransactionBuilder transaction) { - byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); + byte value = HPlusCoordinator.getLanguage(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_LANGUAGE, value @@ -248,13 +272,20 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_WEEK, - (byte) c.get(Calendar.DAY_OF_WEEK) + (byte) (c.get(Calendar.DAY_OF_WEEK) - 1) }); return this; } private HPlusSupport setSIT(TransactionBuilder transaction) { + + //Makibes F68 doesn't like this command. + //Just ignore. + if(deviceType == DeviceType.MAKIBESF68){ + return this; + } + int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); @@ -646,8 +677,17 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } - private void showIncomingCall(String name, String number) { + private void showIncomingCall(String name, String rawNumber) { try { + StringBuilder number = new StringBuilder(); + + //Clean up number as the device only accepts digits + for(char c : rawNumber.toCharArray()){ + if(Character.isDigit(c)){ + number.append(c); + } + } + TransactionBuilder builder = performInitialized("incomingCall"); //Enable call notifications 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 0fbd5451..9e2ebb41 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -23,6 +23,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; @@ -172,6 +173,8 @@ public class DeviceHelper { result.add(new VibratissimoCoordinator()); result.add(new LiveviewCoordinator()); result.add(new HPlusCoordinator()); + result.add(new MakibesF68Coordinator()); + return result; } From cfa08d4fc4144ea609a947be7fe91c0f77e94322 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 26 Jan 2017 14:38:38 +0100 Subject: [PATCH 182/244] fix 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 d8fef016..2d861355 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -5,10 +5,10 @@ HPlus: Fix bug related to steps and heart rate Pebble: Support dynamic keys for natively supported watchfaces and watchapps (more stability accross versions) Pebble: Fix error Toast being displayed when TimeStyle watchface is not installed + Mi Band 1+2: Support for connecting wihout BT pairing (workaround for certain connection problems) Pebble: Fix temperature unit in Timestyle Pebble watchface - Mi Band 1+2: Support for connecting wihout BT pairing (workaround for certain connection problems) Add optional Cyrillic transliteration (for devices lacking the font) From b0ac785066868aad1df06b11c1a7afca24f81f09 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 26 Jan 2017 15:23:43 +0100 Subject: [PATCH 183/244] release time --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b2aba42..4d756f9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ###Changelog -####Version 0.17.3 (next) +####Version 0.17.3 * HPlus: Improve display of new messages and phone calls * HPlus: Fix bug related to steps and heart rate * Pebble: Support dynamic keys for natively supported watchfaces and watchapps (more stability accross versions) From d7db6559d803f8940de4da49e5d5da05bbf89376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Thu, 26 Jan 2017 16:04:33 +0000 Subject: [PATCH 184/244] HPlus: Change Makibes F68 device type --- .../nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1488f46b..5777b5d9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -14,7 +14,7 @@ public enum DeviceType { VIBRATISSIMO(20), LIVEVIEW(30), HPLUS(40), - MAKIBESF68(50), + MAKIBESF68(41), TEST(1000); private final int key; From f81ff8591b18182d01010f7bc557cca161719351 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 26 Jan 2017 17:18:43 +0100 Subject: [PATCH 185/244] Pebble: add a FAB in App Manager which launches a file manager to chose a file This is similar to #247 but simpler and using a FAB, also it explicitly targets our Activity instead of allowing to open a video in a video player which using this feature Also suggested in #520 --- .../appmanager/AppManagerActivity.java | 29 ++++++++++++++++++- .../main/res/layout/activity_appmanager.xml | 11 ++----- .../layout/activity_fragmentappmanager.xml | 13 +++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java index d464d032..92a1a97f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java @@ -1,16 +1,19 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; +import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.ViewPager; import android.view.MenuItem; +import android.view.View; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdapter; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity; +import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -35,6 +39,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; public class AppManagerActivity extends AbstractGBFragmentActivity { private static final Logger LOG = LoggerFactory.getLogger(AbstractAppManagerFragment.class); + private int READ_REQUEST_CODE = 42; private GBDevice mGBDevice = null; @@ -68,6 +73,18 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { throw new IllegalArgumentException("Must provide a device when invoking this activity"); } + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + assert fab != null; + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + startActivityForResult(intent, READ_REQUEST_CODE); + } + }); + IntentFilter filterLocal = new IntentFilter(); filterLocal.addAction(GBApplication.ACTION_QUIT); filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED); @@ -93,7 +110,7 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { public class SectionsPagerAdapter extends AbstractFragmentPagerAdapter { - public SectionsPagerAdapter(FragmentManager fm) { + SectionsPagerAdapter(FragmentManager fm) { super(fm); } @@ -179,6 +196,16 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { return uuids; } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent resultData) { + if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + Intent startIntent = new Intent(AppManagerActivity.this, FwAppInstallerActivity.class); + startIntent.setAction(Intent.ACTION_VIEW); + startIntent.setDataAndType(resultData.getData(), null); + startActivity(startIntent); + } + } + @Override protected void onDestroy() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); diff --git a/app/src/main/res/layout/activity_appmanager.xml b/app/src/main/res/layout/activity_appmanager.xml index bdc6317c..bf304a27 100644 --- a/app/src/main/res/layout/activity_appmanager.xml +++ b/app/src/main/res/layout/activity_appmanager.xml @@ -1,17 +1,10 @@ - - - - - - - + diff --git a/app/src/main/res/layout/activity_fragmentappmanager.xml b/app/src/main/res/layout/activity_fragmentappmanager.xml index 639a58f7..d32ada66 100644 --- a/app/src/main/res/layout/activity_fragmentappmanager.xml +++ b/app/src/main/res/layout/activity_fragmentappmanager.xml @@ -2,6 +2,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto" android:paddingBottom="0px" android:paddingLeft="0px" android:paddingRight="0px" @@ -28,5 +29,17 @@ + From ba7d13fa5da455519f246ace667f133fb335050a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 26 Jan 2017 20:55:00 +0100 Subject: [PATCH 186/244] Disable smart alarms for Mi2 Closes #471 --- .../gadgetbridge/activities/AlarmDetails.java | 24 ++++++++++++++++--- .../activities/ConfigureAlarms.java | 12 ++++++++-- .../activities/ControlCenter.java | 1 + .../devices/DeviceCoordinator.java | 6 +++++ .../devices/UnknownDeviceCoordinator.java | 5 ++++ .../devices/hplus/HPlusCoordinator.java | 5 ++++ .../devices/liveview/LiveviewCoordinator.java | 5 ++++ .../devices/miband/MiBand2Coordinator.java | 5 ++++ .../devices/miband/MiBandCoordinator.java | 5 ++++ .../devices/pebble/PebbleCoordinator.java | 5 ++++ .../vibratissimo/VibratissimoCoordinator.java | 5 ++++ 11 files changed, 73 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java index 7b3342ae..c9f2b09d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java @@ -1,15 +1,19 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.os.Bundle; -import android.os.Parcelable; import android.text.format.DateFormat; import android.view.MenuItem; +import android.view.View; import android.widget.CheckBox; +import android.widget.TextView; import android.widget.TimePicker; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; public class AlarmDetails extends GBActivity { @@ -23,16 +27,19 @@ public class AlarmDetails extends GBActivity { private CheckBox cbFriday; private CheckBox cbSaturday; private CheckBox cbSunday; + private GBDevice device; + private TextView smartAlarmLabel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_alarm_details); - Parcelable p = getIntent().getExtras().getParcelable("alarm"); - alarm = (GBAlarm) p; + alarm = getIntent().getParcelableExtra("alarm"); + device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE); timePicker = (TimePicker) findViewById(R.id.alarm_time_picker); + smartAlarmLabel = (TextView) findViewById(R.id.alarm_label_smart_wakeup); cbSmartWakeup = (CheckBox) findViewById(R.id.alarm_cb_smart_wakeup); cbMonday = (CheckBox) findViewById(R.id.alarm_cb_mon); cbTuesday = (CheckBox) findViewById(R.id.alarm_cb_tue); @@ -47,6 +54,9 @@ public class AlarmDetails extends GBActivity { timePicker.setCurrentMinute(alarm.getMinute()); cbSmartWakeup.setChecked(alarm.isSmartWakeup()); + int smartAlarmVisibility = supportsSmartWakeup() ? View.VISIBLE : View.GONE; + cbSmartWakeup.setVisibility(smartAlarmVisibility); + smartAlarmLabel.setVisibility(smartAlarmVisibility); cbMonday.setChecked(alarm.getRepetition(GBAlarm.ALARM_MON)); cbTuesday.setChecked(alarm.getRepetition(GBAlarm.ALARM_TUE)); @@ -58,6 +68,14 @@ public class AlarmDetails extends GBActivity { } + private boolean supportsSmartWakeup() { + if (device != null) { + DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device); + return coordinator.supportsSmartWakeup(device); + } + return false; + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index 0a978d5d..420112b3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -14,6 +14,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.adapter.GBAlarmListAdapter; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ALARMS; @@ -26,6 +27,7 @@ public class ConfigureAlarms extends GBActivity { private GBAlarmListAdapter mGBAlarmListAdapter; private Set preferencesAlarmListSet; private boolean avoidSendAlarmsToDevice; + private GBDevice device; @Override protected void onCreate(Bundle savedInstanceState) { @@ -33,6 +35,8 @@ public class ConfigureAlarms extends GBActivity { setContentView(R.layout.activity_configure_alarms); + device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE); + Prefs prefs = GBApplication.getPrefs(); preferencesAlarmListSet = prefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); if (preferencesAlarmListSet.isEmpty()) { @@ -86,12 +90,16 @@ public class ConfigureAlarms extends GBActivity { public void configureAlarm(GBAlarm alarm) { avoidSendAlarmsToDevice = true; - Intent startIntent; - startIntent = new Intent(getApplicationContext(), AlarmDetails.class); + Intent startIntent = new Intent(getApplicationContext(), AlarmDetails.class); startIntent.putExtra("alarm", alarm); + startIntent.putExtra(GBDevice.EXTRA_DEVICE, getDevice()); startActivityForResult(startIntent, REQ_CONFIGURE_ALARM); } + private GBDevice getDevice() { + return device; + } + private void sendAlarmsToDevice() { GBApplication.deviceService().onSetAlarms(mGBAlarmListAdapter.getAlarmList()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 6b9178fb..35f0009e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -278,6 +278,7 @@ public class ControlCenter extends GBActivity { if (selectedDevice != null) { Intent startIntent; startIntent = new Intent(ControlCenter.this, ConfigureAlarms.class); + startIntent.putExtra(GBDevice.EXTRA_DEVICE, selectedDevice); startActivity(startIntent); } return true; 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 ef872cf3..346446da 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -154,6 +154,12 @@ public interface DeviceCoordinator { */ boolean supportsAlarmConfiguration(); + /** + * Returns true if this device/coordinator supports alarms with smart wakeup + * @return + */ + boolean supportsSmartWakeup(GBDevice device); + /** * Returns true if the given device supports heart rate measurements. * @return 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 6a50ba0f..4d3cf3c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -135,6 +135,11 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { return false; } + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + @Override public boolean supportsHeartRateMeasurement(GBDevice device) { return false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 8495e664..8224a107 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -106,6 +106,11 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return true; } + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + @Override public boolean supportsHeartRateMeasurement(GBDevice device) { return true; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java index 338137ce..d68341f4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java @@ -72,6 +72,11 @@ public class LiveviewCoordinator extends AbstractDeviceCoordinator { return false; } + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + @Override public boolean supportsHeartRateMeasurement(GBDevice device) { return false; 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 01538e09..3ba9e543 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 @@ -106,4 +106,9 @@ public class MiBand2Coordinator extends MiBandCoordinator { MiBand2FWInstallHandler handler = new MiBand2FWInstallHandler(uri, context); return handler.isValid() ? handler : null; } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } } 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 abff3bc1..bf534a35 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 @@ -124,6 +124,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return true; } + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return true; + } + @Override public boolean supportsActivityTracking() { return true; 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 5953113d..316967cb 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 @@ -108,6 +108,11 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { return false; } + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + @Override public boolean supportsHeartRateMeasurement(GBDevice device) { return PebbleUtils.hasHRM(device.getModel()); 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 8f71f234..0c06672d 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 @@ -73,6 +73,11 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator { return false; } + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + @Override public boolean supportsHeartRateMeasurement(GBDevice device) { return false; From f9779d969574cb018a78c1bffe16ed59ddd67d7f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 26 Jan 2017 23:22:57 +0100 Subject: [PATCH 187/244] Improve some messages --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0b53f84a..88226d4c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -66,7 +66,7 @@ Language - Hide the gadgetbridge notification + Hide the Gadgetbridge notification The icon in the status bar and the notification in the lockscreen are shown The icon in the status bar and the notification in the lockscreen are hidden @@ -82,7 +82,7 @@ Do Not Disturb Stop unwanted Notifications from being sent based on the Do Not Disturb mode. Transliteration - Use, if you device has no support for your language\'s font (Currently Cyrillic only) + Enable this if your device has no support for your language\'s font (Currently Cyrillic only) always when screen is off From 13af1c1e1102bdba706519ffb5b7a816dc398791 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 27 Jan 2017 00:23:45 +0100 Subject: [PATCH 188/244] Ignore Gadgetbridge's own notifications Fixes #411 --- .../gadgetbridge/externalevents/NotificationListener.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 81a41795..745b0b8f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; @@ -269,6 +270,13 @@ public class NotificationListener extends NotificationListenerService { dissectNotificationTo(notification, notificationSpec, preferBigText); notificationSpec.id = (int) sbn.getPostTime(); //FIMXE: a truly unique id would be better + // ignore Gadgetbridge's very own notifications, except for those from the debug screen + if (getApplicationContext().getPackageName().equals(source)) { + if (!getApplicationContext().getString(R.string.test_notification).equals(notificationSpec.title)) { + return; + } + } + NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification); List actions = wearableExtender.getActions(); From 2d3907b0f0cfca4d25682e7d09a889404a834936 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Fri, 27 Jan 2017 23:16:19 +0300 Subject: [PATCH 189/244] Fix issue #522 : Transliterate Caller Name --- .../gadgetbridge/impl/GBDeviceService.java | 34 +++++++++++++++- .../gadgetbridge/model/DeviceService.java | 1 + .../service/DeviceCommunicationService.java | 40 ++----------------- .../gadgetbridge/util/JavaExtensions.java | 15 +++++++ .../gadgetbridge/util/LanguageUtils.java | 23 +++++++++-- 5 files changed, 71 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java 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 6f217b5d..c8f83926 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -3,7 +3,9 @@ package nodomain.freeyourgadget.gadgetbridge.impl; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.database.Cursor; import android.net.Uri; +import android.provider.ContactsContract; import android.support.annotation.Nullable; import java.util.ArrayList; @@ -21,6 +23,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils; +import static nodomain.freeyourgadget.gadgetbridge.util.JavaExtensions.coalesce; + public class GBDeviceService implements DeviceService { protected final Context mContext; private final Class mServiceClass; @@ -32,6 +36,7 @@ public class GBDeviceService implements DeviceService { EXTRA_NOTIFICATION_BODY, EXTRA_NOTIFICATION_SOURCENAME, EXTRA_CALL_PHONENUMBER, + EXTRA_CALL_DISPLAYNAME, EXTRA_MUSIC_ARTIST, EXTRA_MUSIC_ALBUM, EXTRA_MUSIC_TRACK, @@ -111,7 +116,7 @@ public class GBDeviceService implements DeviceService { Intent intent = createIntent().setAction(ACTION_NOTIFICATION) .putExtra(EXTRA_NOTIFICATION_FLAGS, notificationSpec.flags) .putExtra(EXTRA_NOTIFICATION_PHONENUMBER, notificationSpec.phoneNumber) - .putExtra(EXTRA_NOTIFICATION_SENDER, notificationSpec.sender) + .putExtra(EXTRA_NOTIFICATION_SENDER, coalesce(notificationSpec.sender, getContactDisplayNameByNumber(notificationSpec.phoneNumber))) .putExtra(EXTRA_NOTIFICATION_SUBJECT, notificationSpec.subject) .putExtra(EXTRA_NOTIFICATION_TITLE, notificationSpec.title) .putExtra(EXTRA_NOTIFICATION_BODY, notificationSpec.body) @@ -144,9 +149,9 @@ public class GBDeviceService implements DeviceService { @Override public void onSetCallState(CallSpec callSpec) { - // name is actually ignored and provided by the service itself... Intent intent = createIntent().setAction(ACTION_CALLSTATE) .putExtra(EXTRA_CALL_PHONENUMBER, callSpec.number) + .putExtra(EXTRA_CALL_DISPLAYNAME, coalesce(callSpec.name, getContactDisplayNameByNumber(callSpec.number))) .putExtra(EXTRA_CALL_COMMAND, callSpec.command); invokeService(intent); } @@ -332,4 +337,29 @@ public class GBDeviceService implements DeviceService { .putExtra(EXTRA_WEATHER_TOMORROWCONDITIONCODE, weatherSpec.tomorrowConditionCode); invokeService(intent); } + + /** + * Returns contact DisplayName by call number + * @param number contact number + * @return contact DisplayName, if found it + */ + private String getContactDisplayNameByNumber(String number) { + Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); + String name = number; + + if (number == null || number.equals("")) { + return name; + } + + try (Cursor contactLookup = mContext.getContentResolver().query(uri, null, null, null, null)) { + if (contactLookup != null && contactLookup.getCount() > 0) { + contactLookup.moveToNext(); + name = contactLookup.getString(contactLookup.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); + } + } catch (SecurityException e) { + // ignore, just return name below + } + + return name; + } } 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 a7be20f8..9177b077 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -60,6 +60,7 @@ public interface DeviceService extends EventHandler { String EXTRA_VIBRATION_INTENSITY = "vibration_intensity"; String EXTRA_CALL_COMMAND = "call_command"; String EXTRA_CALL_PHONENUMBER = "call_phonenumber"; + String EXTRA_CALL_DISPLAYNAME = "call_displayname"; String EXTRA_CANNEDMESSAGES = "cannedmessages"; String EXTRA_CANNEDMESSAGES_TYPE = "cannedmessages_type"; String EXTRA_MUSIC_ARTIST = "music_artist"; 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 336e0a92..1dd3934c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -8,10 +8,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.database.Cursor; import android.net.Uri; import android.os.IBinder; -import android.provider.ContactsContract; import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; @@ -97,6 +95,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CAL import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TITLE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TYPE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_DISPLAYNAME; 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; @@ -332,8 +331,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0); if (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null) { - notificationSpec.sender = getContactDisplayNameByNumber(notificationSpec.phoneNumber); - notificationSpec.id = mRandom.nextInt(); // FIXME: add this in external SMS Receiver? GBApplication.getIDSenderLookup().add(notificationSpec.id, notificationSpec.phoneNumber); } @@ -412,18 +409,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere break; } case ACTION_CALLSTATE: - int command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED); - - String phoneNumber = intent.getStringExtra(EXTRA_CALL_PHONENUMBER); - String callerName = null; - if (phoneNumber != null) { - callerName = getContactDisplayNameByNumber(phoneNumber); - } - CallSpec callSpec = new CallSpec(); - callSpec.command = command; - callSpec.number = phoneNumber; - callSpec.name = callerName; + callSpec.command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED); + callSpec.number = intent.getStringExtra(EXTRA_CALL_PHONENUMBER); + callSpec.name = intent.getStringExtra(EXTRA_CALL_DISPLAYNAME); mDeviceSupport.onSetCallState(callSpec); break; case ACTION_SETCANNEDMESSAGES: @@ -687,27 +676,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere return null; } - - private String getContactDisplayNameByNumber(String number) { - Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); - String name = number; - - if (number == null || number.equals("")) { - return name; - } - - try (Cursor contactLookup = getContentResolver().query(uri, null, null, null, null)) { - if (contactLookup != null && contactLookup.getCount() > 0) { - contactLookup.moveToNext(); - name = contactLookup.getString(contactLookup.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); - } - } catch (SecurityException e) { - // ignore, just return name below - } - - return name; - } - @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (GBPrefs.AUTO_RECONNECT.equals(key)) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java new file mode 100644 index 00000000..cd4b37cc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java @@ -0,0 +1,15 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +public class JavaExtensions { + + /** + * Equivalent c# '??' operator + * @param one first value + * @param two second value + * @return first if not null, or second if first is null + */ + public static T coalesce(T one, T two) + { + return one != null ? one : two; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index 59d7aa81..a19d79d9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -23,13 +23,20 @@ public class LanguageUtils { } }; - //check transliterate option status + /** + * Checks the status of transliteration option + * @return true if transliterate option is On, and false, if Off or not exist + */ public static boolean transliterate() { return GBApplication.getPrefs().getBoolean("transliteration", false); } - //replace unsupported symbols to english analog + /** + * Replaces unsupported symbols to english + * @param txt input text + * @return transliterated text + */ public static String transliterate(String txt){ if (txt == null || txt.isEmpty()) { return txt; @@ -47,7 +54,11 @@ public class LanguageUtils { return flattenToAscii(message.toString()); } - //replace unsupported symbol to english analog text + /** + * Replaces unsupported symbol to english by {@code transliterateMap} + * @param c input char + * @return replacement text + */ private static String transliterate(char c){ char lowerChar = Character.toLowerCase(c); @@ -65,7 +76,11 @@ public class LanguageUtils { return String.valueOf(c); } - //convert diacritic + /** + * Converts the diacritics + * @param string input text + * @return converted text + */ private static String flattenToAscii(String string) { string = Normalizer.normalize(string, Normalizer.Form.NFD); return string.replaceAll("\\p{M}", ""); From 4c48b473ac41fb953b49134d2df7046ef4da1f4f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 28 Jan 2017 22:17:41 +0100 Subject: [PATCH 190/244] Show device type in GBDeviceCandidate.toString() --- .../freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 cec517a6..c6d56114 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -127,7 +127,7 @@ public class GBDeviceCandidate implements Parcelable { deviceName = (String) method.invoke(device); } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignore) { - LOG.info("Could not get device alias for " + deviceName); + LOG.info("Could not get device alias for " + device.getName()); } if (deviceName == null || deviceName.length() == 0) { deviceName = device.getName(); @@ -167,6 +167,6 @@ public class GBDeviceCandidate implements Parcelable { @Override public String toString() { - return getName() + ": " + getMacAddress(); + return getName() + ": " + getMacAddress() + " (" + getDeviceType() + ")"; } } From 6c16b4fb15182080369a064482667017b7e8a2d9 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 28 Jan 2017 22:43:48 +0100 Subject: [PATCH 191/244] Updated initial array list size to reflect reality --- .../nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9e2ebb41..82791f62 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -166,7 +166,7 @@ public class DeviceHelper { } private List createCoordinators() { - List result = new ArrayList<>(2); + List result = new ArrayList<>(); result.add(new MiBand2Coordinator()); // Note: MiBand2 must come before MiBand because detection is hacky, atm result.add(new MiBandCoordinator()); result.add(new PebbleCoordinator()); From ec6a8b6743dcda64ac0b26c13ea5c30b3a8bf5a0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 28 Jan 2017 22:52:22 +0100 Subject: [PATCH 192/244] MI: some more logging --- .../gadgetbridge/activities/DiscoveryActivity.java | 2 ++ .../devices/miband/MiBandPairingActivity.java | 8 ++++---- 2 files changed, 6 insertions(+), 4 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 8016041e..e42f1bd8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -293,6 +293,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC DeviceType deviceType = DeviceHelper.getInstance().getSupportedType(candidate); if (deviceType.isSupported()) { candidate.setDeviceType(deviceType); + LOG.info("Recognized supported device: " + candidate); int index = deviceCandidates.indexOf(candidate); if (index >= 0) { deviceCandidates.set(index, candidate); // replace @@ -506,6 +507,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC stopDiscovery(); DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate); + LOG.info("Using device candidate " + deviceCandidate + " with coordinator: " + coordinator.getClass()); Class pairingActivity = coordinator.getPairingActivity(); if (pairingActivity != null) { Intent intent = new Intent(this, pairingActivity); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java index 9f825a7b..1aae8160 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java @@ -33,7 +33,7 @@ public class MiBandPairingActivity extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class); private static final int REQ_CODE_USER_SETTINGS = 52; - private static final String STATE_MIBAND_ADDRESS = "mibandMacAddress"; + private static final String STATE_DEVICE_CANDIDATE = "stateDeviceCandidate"; private static final long DELAY_AFTER_BONDING = 1000; // 1s private TextView message; private boolean isPairing; @@ -103,7 +103,7 @@ public class MiBandPairingActivity extends GBActivity { Intent intent = getIntent(); deviceCandidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE); if (deviceCandidate == null && savedInstanceState != null) { - deviceCandidate = savedInstanceState.getParcelable(STATE_MIBAND_ADDRESS); + deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE); } if (deviceCandidate == null) { Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show(); @@ -125,13 +125,13 @@ public class MiBandPairingActivity extends GBActivity { @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putParcelable(STATE_MIBAND_ADDRESS, deviceCandidate); + outState.putParcelable(STATE_DEVICE_CANDIDATE, deviceCandidate); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - deviceCandidate = savedInstanceState.getParcelable(STATE_MIBAND_ADDRESS); + deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE); } @Override From 2ae4497261b7c919e24b10087c3e1f94c45c80ed Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 28 Jan 2017 23:24:02 +0100 Subject: [PATCH 193/244] Mi2: the only reason I can see for detecting MiBandSupport for Mi2 Fixes #529, hopefully --- .../gadgetbridge/devices/miband/MiBand2Coordinator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 3ba9e543..7fd8ad99 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 @@ -54,12 +54,12 @@ public class MiBand2Coordinator extends MiBandCoordinator { // and a heuristic for now try { BluetoothDevice device = candidate.getDevice(); - if (isHealthWearable(device)) { +// if (isHealthWearable(device)) { String name = device.getName(); if (name != null && name.equalsIgnoreCase(MiBandConst.MI_BAND2_NAME)) { return DeviceType.MIBAND2; } - } +// } } catch (Exception ex) { LOG.error("unable to check device support", ex); } From b157f84b83aae901d54ce4c8fd0e8cb236b1b6cb Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 29 Jan 2017 22:56:56 +0100 Subject: [PATCH 194/244] Log connection attempt when BT is turned on --- .../externalevents/BluetoothStateChangeReceiver.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java index 0e639733..0934d3d7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java @@ -6,11 +6,17 @@ import android.content.Context; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; +import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleIoThread; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class BluetoothStateChangeReceiver extends BroadcastReceiver { + private static final Logger LOG = LoggerFactory.getLogger(BluetoothStateChangeReceiver.class); + @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -26,6 +32,7 @@ public class BluetoothStateChangeReceiver extends BroadcastReceiver { return; } + LOG.info("Bluetooth turned on => connecting..."); GBApplication.deviceService().connect(); } else if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) { GBApplication.quit(); From d030ad940024392beabeaaf7e01029d6738d4598 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 29 Jan 2017 23:06:40 +0100 Subject: [PATCH 195/244] Ups, remove accidental import from C&P --- .../externalevents/BluetoothStateChangeReceiver.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java index 0934d3d7..4d5018cb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java @@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; -import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleIoThread; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class BluetoothStateChangeReceiver extends BroadcastReceiver { From f79e8f88339e559152baa7e6efa3f8703b6f5eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Mon, 30 Jan 2017 23:37:47 +0000 Subject: [PATCH 196/244] HPlus: Working alarms and small cleanup --- .../devices/hplus/HPlusConstants.java | 3 +- .../devices/hplus/HPlusCoordinator.java | 16 +--- .../devices/hplus/MakibesF68Coordinator.java | 6 ++ .../service/devices/hplus/HPlusSupport.java | 86 ++++--------------- 4 files changed, 25 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 73515ee7..892b8c72 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -38,7 +38,8 @@ public final class HPlusConstants { public static final byte[] CMD_SET_PREF_START = new byte[]{0x4f, 0x5a}; public static final byte[] CMD_SET_PREF_START1 = new byte[]{0x4d}; - public static final byte CMD_SET_ALARM = 0x4c; + //public static final byte CMD_SET_ALARM = 0x4c; Unknown + public static final byte CMD_SET_ALARM = 0x0c; public static final byte CMD_SET_LANGUAGE = 0x22; public static final byte CMD_SET_TIMEMODE = 0x47; public static final byte CMD_SET_UNITS = 0x48; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 8495e664..53f35e32 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -138,19 +138,13 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { qb.where(HPlusHealthActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); } - public static int getFitnessGoal(String address) throws IllegalArgumentException { - ActivityUser activityUser = new ActivityUser(); - - return activityUser.getStepsGoal(); - } - public static byte getLanguage(String address) { return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_LANGUAGE + "_" + address, HPlusConstants.ARG_LANGUAGE_EN); } public static byte getTimeMode(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_TIMEMODE + "_" + address, 0); + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_TIMEMODE + "_" + address, HPlusConstants.ARG_TIMEMODE_24H); } public static byte getUnit(String address) { @@ -212,14 +206,6 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10) & 0xFF); } - public static boolean getSWAlertTime(String address) { - return prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); - } - - public static int getAlertTime(String address) { - return prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); - } - public static int getSITStartTime(String address) { return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java index 1b1abe56..63cd7191 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java @@ -30,4 +30,10 @@ public class MakibesF68Coordinator extends HPlusCoordinator { public DeviceType getDeviceType() { return DeviceType.MAKIBESF68; } + + @Override + public String getManufacturer() { + return "Makibes"; + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 4bf02c55..6daec55f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -26,6 +26,7 @@ import java.util.GregorianCalendar; import java.util.List; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -109,11 +110,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //Initialize device sendUserInfo(builder); //Sync preferences - setSIT(builder); //Sync SIT Interval - setCurrentDate(builder); // Sync Current Date - setDayOfWeek(builder); - setCurrentTime(builder); // Sync Current Time - setLanguage(builder); + requestDeviceInfo(builder); @@ -141,68 +138,18 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport syncPreferences(TransactionBuilder transaction) { if(deviceType == DeviceType.HPLUS) { - byte gender = HPlusCoordinator.getUserGender(getDevice().getAddress()); - byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); - byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); - byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); - int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); - byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); - byte country = HPlusCoordinator.getLanguage(getDevice().getAddress()); - byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? - byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); - byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); - byte alertTimeHour = 0; - byte alertTimeMinute = 0; - - if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { - int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); - - alertTimeHour = (byte) ((t / 256) & 0xff); - alertTimeMinute = (byte) (t % 256); - } - - byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); - byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); - - transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.CMD_SET_PREFS, - gender, - age, - bodyHeight, - bodyWeight, - 0, - 0, - (byte) ((goal / 256) & 0xff), - (byte) (goal % 256), - displayTime, - country, - 0, - social, - allDayHeart, - wrist, - 0, - alertTimeHour, - alertTimeMinute, - unit, - timemode - }); - - }else if(deviceType == DeviceType.MAKIBESF68){ - //Makibes doesn't support setting everything at once. - - setGender(transaction); - setAge(transaction); - setWeight(transaction); - setHeight(transaction); - setGoal(transaction); - setLanguage(transaction); - setScreenTime(transaction); - //setAlarm(transaction, t); - setUnit(transaction); - setTimeMode(transaction); + setSIT(transaction); //Sync SIT Interval } - + setGender(transaction); + setAge(transaction); + setWeight(transaction); + setHeight(transaction); + setGoal(transaction); + setLanguage(transaction); + setScreenTime(transaction); + setUnit(transaction); + setTimeMode(transaction); setAllDayHeart(transaction); return this; @@ -404,12 +351,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAlarm(TransactionBuilder transaction, Calendar t) { transaction.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALARM, - (byte) (t.get(Calendar.YEAR) / 256), - (byte) (t.get(Calendar.YEAR) % 256), - (byte) (t.get(Calendar.MONTH) + 1), (byte) t.get(Calendar.HOUR_OF_DAY), - (byte) t.get(Calendar.MINUTE), - (byte) t.get(Calendar.SECOND)}); + (byte) t.get(Calendar.MINUTE)}); return this; } @@ -497,8 +440,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { setAlarm(builder, t); builder.queue(getQueue()); + GB.toast(getContext(), getContext().getString(R.string.user_feedback_miband_set_alarms_ok), Toast.LENGTH_SHORT, GB.INFO); + return; //Only first alarm } + GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO); } From e08a9009787b35ac433602d2cc4b392e19a7caf5 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Wed, 1 Feb 2017 22:55:40 +0100 Subject: [PATCH 197/244] Refactor the MusicPlaybackReceiver and related files Add actions to the filter (this should help with #536) Add "copy" constructors to MusicSpec and MusicStateSpec, and use those when receiving an updated intent, this way partial updates do not disrupt the local information. Iterate over incoming extra keys, explicitly check the incoming type and use only known type. This could help with #533 Possible problem: this code iterates over every key of the incoming bundle. --- .../externalevents/MusicPlaybackReceiver.java | 39 ++++++++++++------- .../gadgetbridge/model/MusicSpec.java | 13 +++++++ .../gadgetbridge/model/MusicStateSpec.java | 12 ++++++ .../service/DeviceCommunicationService.java | 3 +- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index a47c8f4d..9d6ee214 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,17 +27,32 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { value != null ? value.toString() : "null", value != null ? value.getClass().getName() : "no class")); } */ - MusicSpec musicSpec = new MusicSpec(); - musicSpec.artist = intent.getStringExtra("artist"); - musicSpec.album = intent.getStringExtra("album"); - if (intent.hasExtra("track")) { - musicSpec.track = intent.getStringExtra("track"); - } - else if (intent.hasExtra("title")) { - musicSpec.track = intent.getStringExtra("title"); - } + MusicSpec musicSpec = new MusicSpec(lastMusicSpec); + MusicStateSpec stateSpec = new MusicStateSpec(lastStatecSpec); - musicSpec.duration = intent.getIntExtra("duration", 0) / 1000; + Bundle incomingBundle = intent.getExtras(); + for (String key : incomingBundle.keySet()) { + Object incoming = incomingBundle.get(key); + if (incoming instanceof String && "artist".equals(key)) { + musicSpec.artist = (String) incoming; + } else if (incoming instanceof String && "album".equals(key)) { + musicSpec.album = (String) incoming; + } else if (incoming instanceof String && "track".equals(key)) { + musicSpec.track = (String) incoming; + } else if (incoming instanceof String && "title".equals(key) && musicSpec.track == null) { + musicSpec.track = (String) incoming; + } else if (incoming instanceof Integer && "duration".equals(key)) { + musicSpec.duration = (Integer) incoming / 1000; + } else if (incoming instanceof Long && "duration".equals(key)) { + musicSpec.duration = ((Long) incoming).intValue() / 1000; + } else if (incoming instanceof Integer && "position".equals(key)) { + stateSpec.position = (Integer) incoming / 1000; + } else if (incoming instanceof Long && "position".equals(key)) { + stateSpec.position = ((Long) incoming).intValue() / 1000; + } else if (incoming instanceof Boolean && "playing".equals(key)) { + stateSpec.state = (byte) (((Boolean) incoming) ? MusicStateSpec.STATE_PLAYING : MusicStateSpec.STATE_PAUSED); + } + } if (!lastMusicSpec.equals(musicSpec)) { lastMusicSpec = musicSpec; @@ -47,9 +63,6 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { } if (intent.hasExtra("position") && intent.hasExtra("playing")) { - MusicStateSpec stateSpec = new MusicStateSpec(); - stateSpec.position = intent.getIntExtra("position", 0) / 1000; - stateSpec.state = (byte) (intent.getBooleanExtra("playing", true) ? MusicStateSpec.STATE_PLAYING : MusicStateSpec.STATE_PAUSED); if (!lastStatecSpec.equals(stateSpec)) { LOG.info("Update Music State: state=" + stateSpec.state + ", position= " + stateSpec.position); GBApplication.deviceService().onSetMusicState(stateSpec); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java index aa9f67ca..9866a935 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java @@ -17,6 +17,19 @@ public class MusicSpec { public int trackCount; public int trackNr; + public MusicSpec() { + + } + + public MusicSpec(MusicSpec old) { + this.duration = old.duration; + this.trackCount = old.trackCount; + this.trackNr = old.trackNr; + this.track = old.track; + this.album = old.album; + this.artist = old.artist; + } + @Override public boolean equals(Object obj) { if (obj == this) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java index fb15948d..f9d070eb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java @@ -15,6 +15,18 @@ public class MusicStateSpec { public byte shuffle; public byte repeat; + public MusicStateSpec() { + + } + + public MusicStateSpec(MusicStateSpec old) { + this.state = old.state; + this.position = old.position; + this.playRate = old.playRate; + this.shuffle = old.shuffle; + this.repeat = old.repeat; + } + @Override public boolean equals(Object obj) { if (obj == this) { 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 1dd3934c..277e2223 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -599,8 +599,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mMusicPlaybackReceiver = new MusicPlaybackReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("com.android.music.metachanged"); + filter.addAction("com.android.music.playstatechanged"); + filter.addAction("com.android.music.playbackcomplete"); filter.addAction("net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED"); - //filter.addAction("com.android.music.playstatechanged"); registerReceiver(mMusicPlaybackReceiver, filter); } if (mTimeChangeReceiver == null) { From 3fcf4938b97d55a8d283cf83e5b7c15680b4d66f Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Wed, 1 Feb 2017 23:10:23 +0100 Subject: [PATCH 198/244] Changelog for Music receiver refactoring --- CHANGELOG.md | 3 +++ app/src/main/res/xml/changelog_master.xml | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d756f9d..900f61ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version next +* Better integration with android music players + ####Version 0.17.3 * HPlus: Improve display of new messages and phone calls * HPlus: Fix bug related to steps and heart rate diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 2d861355..5187fa13 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,9 @@ + + Better integration with android music players + + HPlus: Improve display of new messages and phone calls HPlus: Fix bug related to steps and heart rate From 006a23dfe8e42427757bc1734dac709a2f18832d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 3 Feb 2017 19:30:59 +0000 Subject: [PATCH 199/244] HPlus: Fix time sync and Time format (12/24) --- .../gadgetbridge/devices/hplus/HPlusConstants.java | 4 ++-- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 892b8c72..2fe12b9d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -18,8 +18,8 @@ public final class HPlusConstants { public static final byte ARG_LANGUAGE_CN = 1; public static final byte ARG_LANGUAGE_EN = 2; - public static final byte ARG_TIMEMODE_24H = 0; - public static final byte ARG_TIMEMODE_12H = 1; + public static final byte ARG_TIMEMODE_24H = 1; + public static final byte ARG_TIMEMODE_12H = 0; public static final byte ARG_UNIT_METRIC = 0; public static final byte ARG_UNIT_IMPERIAL = 1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 6daec55f..6ddffffb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -141,15 +141,20 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { setSIT(transaction); //Sync SIT Interval } + setCurrentDate(transaction); + setCurrentTime(transaction); + setDayOfWeek(transaction); + setTimeMode(transaction); + setGender(transaction); setAge(transaction); setWeight(transaction); setHeight(transaction); + setGoal(transaction); setLanguage(transaction); setScreenTime(transaction); setUnit(transaction); - setTimeMode(transaction); setAllDayHeart(transaction); return this; From fd61dc602fcda0a2dd712f240ebb3bd639bd46a5 Mon Sep 17 00:00:00 2001 From: ivanovlev Date: Sat, 4 Feb 2017 09:53:07 +0300 Subject: [PATCH 200/244] Transliteration: capitalized just the first letter in the replacement text --- .../freeyourgadget/gadgetbridge/util/LanguageUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index a19d79d9..78ae076c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.util; +import org.apache.commons.lang3.text.WordUtils; + import java.util.HashMap; import java.util.Map; import java.text.Normalizer; @@ -67,7 +69,7 @@ public class LanguageUtils { if (lowerChar != c) { - return replace.toUpperCase(); + return WordUtils.capitalize(replace); } return replace; From b894c018223628fe89cd79f79c0ee93444965b7a Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 5 Feb 2017 15:13:26 +0100 Subject: [PATCH 201/244] Make the media notification receiver more robust. Sometimes the media notification does not contain the expected components, hence the code covered by the try/catch has been adjusted. This was reported in #533 for VLC. In the future the whole media handling will probably be refactored. --- .../externalevents/NotificationListener.java | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 745b0b8f..e602dfc0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -355,51 +355,51 @@ public class NotificationListener extends NotificationListenerService { MediaController c; try { c = new MediaController(getApplicationContext(), (MediaSession.Token) extras.get(Notification.EXTRA_MEDIA_SESSION)); + + PlaybackState s = c.getPlaybackState(); + stateSpec.position = (int) (s.getPosition() / 1000); + stateSpec.playRate = Math.round(100 * s.getPlaybackSpeed()); + stateSpec.repeat = 1; + stateSpec.shuffle = 1; + switch (s.getState()) { + case PlaybackState.STATE_PLAYING: + stateSpec.state = MusicStateSpec.STATE_PLAYING; + break; + case PlaybackState.STATE_STOPPED: + stateSpec.state = MusicStateSpec.STATE_STOPPED; + break; + case PlaybackState.STATE_PAUSED: + stateSpec.state = MusicStateSpec.STATE_PAUSED; + break; + default: + stateSpec.state = MusicStateSpec.STATE_UNKNOWN; + break; + } + + MediaMetadata d = c.getMetadata(); + if (d == null) + return false; + if (d.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) + musicSpec.artist = d.getString(MediaMetadata.METADATA_KEY_ARTIST); + if (d.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) + musicSpec.album = d.getString(MediaMetadata.METADATA_KEY_ALBUM); + if (d.containsKey(MediaMetadata.METADATA_KEY_TITLE)) + musicSpec.track = d.getString(MediaMetadata.METADATA_KEY_TITLE); + if (d.containsKey(MediaMetadata.METADATA_KEY_DURATION)) + musicSpec.duration = (int) d.getLong(MediaMetadata.METADATA_KEY_DURATION) / 1000; + if (d.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) + musicSpec.trackCount = (int) d.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS); + if (d.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) + musicSpec.trackNr = (int) d.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); + + // finally, tell the device about it + GBApplication.deviceService().onSetMusicInfo(musicSpec); + GBApplication.deviceService().onSetMusicState(stateSpec); + + return true; } catch (NullPointerException e) { return false; } - - PlaybackState s = c.getPlaybackState(); - stateSpec.position = (int) (s.getPosition() / 1000); - stateSpec.playRate = Math.round(100 * s.getPlaybackSpeed()); - stateSpec.repeat = 1; - stateSpec.shuffle = 1; - switch (s.getState()) { - case PlaybackState.STATE_PLAYING: - stateSpec.state = MusicStateSpec.STATE_PLAYING; - break; - case PlaybackState.STATE_STOPPED: - stateSpec.state = MusicStateSpec.STATE_STOPPED; - break; - case PlaybackState.STATE_PAUSED: - stateSpec.state = MusicStateSpec.STATE_PAUSED; - break; - default: - stateSpec.state = MusicStateSpec.STATE_UNKNOWN; - break; - } - - MediaMetadata d = c.getMetadata(); - if (d == null) - return false; - if (d.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) - musicSpec.artist = d.getString(MediaMetadata.METADATA_KEY_ARTIST); - if (d.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) - musicSpec.album = d.getString(MediaMetadata.METADATA_KEY_ALBUM); - if (d.containsKey(MediaMetadata.METADATA_KEY_TITLE)) - musicSpec.track = d.getString(MediaMetadata.METADATA_KEY_TITLE); - if (d.containsKey(MediaMetadata.METADATA_KEY_DURATION)) - musicSpec.duration = (int)d.getLong(MediaMetadata.METADATA_KEY_DURATION) / 1000; - if (d.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) - musicSpec.trackCount = (int)d.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS); - if (d.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) - musicSpec.trackNr = (int)d.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); - - // finally, tell the device about it - GBApplication.deviceService().onSetMusicInfo(musicSpec); - GBApplication.deviceService().onSetMusicState(stateSpec); - - return true; } @Override From dccd6c1b0642d602d81aea25e32209cbc05e38f2 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 5 Feb 2017 16:37:59 +0100 Subject: [PATCH 202/244] Pebble: implement privacy modes The user can choose whether to completely hide the notification text or push it off-screen. This also effects the incoming call notification. Fixes #370 --- CHANGELOG.md | 1 + .../service/devices/pebble/PebbleSupport.java | 19 +++++++++++++++++++ app/src/main/res/values/arrays.xml | 13 +++++++++++++ app/src/main/res/values/strings.xml | 5 +++++ app/src/main/res/values/values.xml | 4 ++++ app/src/main/res/xml/changelog_master.xml | 1 + app/src/main/res/xml/preferences.xml | 7 +++++++ 7 files changed, 50 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 900f61ad..66037df9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ####Version next * Better integration with android music players +* Pebble: Implement notification and incoming call privacy modes ####Version 0.17.3 * HPlus: Improve display of new messages and phone calls 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 122f6da5..e4f59e85 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 @@ -12,6 +12,7 @@ import java.util.Iterator; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; @@ -113,6 +114,16 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { + String currentPrivacyMode = GBApplication.getPrefs().getString("pebble_pref_privacy_mode", getContext().getString(R.string.p_pebble_privacy_mode_off)); + if (getContext().getString(R.string.p_pebble_privacy_mode_complete).equals(currentPrivacyMode)) { + notificationSpec.body = null; + notificationSpec.sender = null; + notificationSpec.subject = null; + notificationSpec.title = null; + notificationSpec.phoneNumber = null; + } else if (getContext().getString(R.string.p_pebble_privacy_mode_content).equals(currentPrivacyMode)) { + notificationSpec.sender = "\n\n\n\n\n" + notificationSpec.sender; + } if (reconnect()) { super.onNotification(notificationSpec); } @@ -120,6 +131,14 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { @Override public void onSetCallState(CallSpec callSpec) { + String currentPrivacyMode = GBApplication.getPrefs().getString("pebble_pref_privacy_mode", getContext().getString(R.string.p_pebble_privacy_mode_off)); + if (getContext().getString(R.string.p_pebble_privacy_mode_complete).equals(currentPrivacyMode)) { + callSpec.name = null; + callSpec.number = null; + } else if (getContext().getString(R.string.p_pebble_privacy_mode_content).equals(currentPrivacyMode)) { + callSpec.name = null; + } + if (reconnect()) { if ((callSpec.command != CallSpec.CALL_OUTGOING) || GBApplication.getPrefs().getBoolean("pebble_enable_outgoing_call", true)) { super.onSetCallState(callSpec); diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index e2aff8f5..db5a7951 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -113,6 +113,19 @@ 3 1 + + + @string/pref_pebble_privacy_mode_off + @string/pref_pebble_privacy_mode_content + @string/pref_pebble_privacy_mode_complete + + + + @string/p_pebble_privacy_mode_off + @string/p_pebble_privacy_mode_content + @string/p_pebble_privacy_mode_complete + + @string/dateformat_time @string/dateformat_date_time diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 88226d4c..efc636b9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -119,6 +119,11 @@ Autoremove dismissed Notifications Notifications are automatically removed from the Pebble when dismissed from the Android device + Privacy mode + Normal notifications and incoming calls display. + Shift the notification text off-screen. Hide the caller\'s name on incoming calls. + Show only the notification icon. Hide the caller\'s name and number on incoming calls. + Location Acquire Location Latitude diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index df90cb5b..84c560b3 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -12,4 +12,8 @@ dateformat_time dateformat_datetime + off + content + complete + diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 5187fa13..887dd32e 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -2,6 +2,7 @@ Better integration with android music players + Pebble: Implement notification and incoming call privacy modes diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 39c7d9ed..9f262213 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -184,6 +184,13 @@ android:key="autoremove_notifications" android:summary="@string/pref_summary_autoremove_notifications" android:title="@string/pref_title_autoremove_notifications" /> + Date: Sun, 5 Feb 2017 17:21:04 +0100 Subject: [PATCH 203/244] Pebble: support weather for Obsidian Ref: #482 --- CHANGELOG.md | 1 + .../pebble/AppMessageHandlerObsidian.java | 160 ++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 2 + app/src/main/res/xml/changelog_master.xml | 2 +- 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 66037df9..91237f34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ####Version next * Better integration with android music players * Pebble: Implement notification and incoming call privacy modes +* Pebble: Support weather for Obisdian watchface ####Version 0.17.3 * HPlus: Improve display of new messages and phone calls diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java new file mode 100644 index 00000000..0f5f783b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java @@ -0,0 +1,160 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.util.Pair; +import android.widget.Toast; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +class AppMessageHandlerObsidian extends AppMessageHandler { + + /* + "appKeys": { + "CONFIG_WEATHER_REFRESH": 35, + "CONFIG_WEATHER_UNIT_LOCAL": 31, + "MSG_KEY_WEATHER_TEMP": 100, + + "CONFIG_WEATHER_EXPIRATION": 36, + "MSG_KEY_FETCH_WEATHER": 102, + "MSG_KEY_WEATHER_ICON": 101, + "MSG_KEY_WEATHER_FAILED": 104, + "CONFIG_WEATHER_MODE_LOCAL": 30, + "CONFIG_WEATHER_APIKEY_LOCAL": 33, + "CONFIG_WEATHER_LOCAL": 28, + "CONFIG_COLOR_WEATHER": 29, + "CONFIG_WEATHER_LOCATION_LOCAL": 34, + "CONFIG_WEATHER_SOURCE_LOCAL": 32 + } + */ + + + private static final String ICON_01d = "a"; //night icons are just uppercase + private static final String ICON_02d = "b"; + private static final String ICON_03d = "c"; + private static final String ICON_04d = "d"; + private static final String ICON_09d = "e"; + private static final String ICON_10d = "f"; + private static final String ICON_11d = "g"; + private static final String ICON_13d = "h"; + private static final String ICON_50d = "i"; + + + AppMessageHandlerObsidian(UUID uuid, PebbleProtocol pebbleProtocol) { + super(uuid, pebbleProtocol); + messageKeys = new HashMap<>(); + try { + JSONObject appKeys = getAppKeys(); + Iterator appKeysIterator = appKeys.keys(); + while (appKeysIterator.hasNext()) { + String current = appKeysIterator.next(); + switch (current) { + case "CONFIG_WEATHER_REFRESH": + case "CONFIG_WEATHER_UNIT_LOCAL": + case "MSG_KEY_WEATHER_TEMP": + case "MSG_KEY_WEATHER_ICON": + messageKeys.put(current, appKeys.getInt(current)); + break; + } + } + } catch (JSONException e) { + GB.toast("There was an error accessing the timestyle watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (IOException ignore) { + } + } + + private String getIconForConditionCode(int conditionCode, boolean isNight) { + + int generalCondition = conditionCode / 100; + String iconToLoad; + // determine the correct icon + switch (generalCondition) { + case 2: //thunderstorm + iconToLoad = ICON_11d; + break; + case 3: //drizzle + iconToLoad = ICON_09d; + break; + case 5: //rain + if (conditionCode == 500) { + iconToLoad = ICON_09d; + } else if (conditionCode < 505) { + iconToLoad = ICON_10d; + } else if (conditionCode == 511) { + iconToLoad = ICON_10d; + } else { + iconToLoad = ICON_09d; + } + break; + case 6: //snow + if (conditionCode == 600 || conditionCode == 620) { + iconToLoad = ICON_13d; + } else if (conditionCode > 610 && conditionCode < 620) { + iconToLoad = ICON_13d; + } else { + iconToLoad = ICON_13d; + } + break; + case 7: // fog, dust, etc + iconToLoad = ICON_03d; + break; + case 8: // clouds + if (conditionCode == 800) { + iconToLoad = ICON_01d; + } else if (conditionCode < 803) { + iconToLoad = ICON_02d; + } else { + iconToLoad = ICON_04d; + } + break; + default: + iconToLoad = ICON_02d; + break; + } + + return (!isNight) ? iconToLoad : iconToLoad.toUpperCase(); + } + + private byte[] encodeObisdianWeather(WeatherSpec weatherSpec) { + + if (weatherSpec == null) { + return null; + } + + ArrayList> pairs = new ArrayList<>(); + boolean isNight = false; //TODO: use the night icons when night + pairs.add(new Pair<>(messageKeys.get("CONFIG_WEATHER_REFRESH"), (Object) 60)); + pairs.add(new Pair<>(messageKeys.get("CONFIG_WEATHER_UNIT_LOCAL"), (Object) 1)); //celsius + pairs.add(new Pair<>(messageKeys.get("MSG_KEY_WEATHER_ICON"), (Object) getIconForConditionCode(weatherSpec.currentConditionCode, isNight))); //celsius + pairs.add(new Pair<>(messageKeys.get("MSG_KEY_WEATHER_TEMP"), (Object) (weatherSpec.currentTemp - 273))); + + return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); + } + + @Override + public GBDeviceEvent[] onAppStart() { + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeObisdianWeather(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; + } + + @Override + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeObisdianWeather(weatherSpec); + } +} \ No newline at end of file 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 14ae5f9e..4527299a 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 @@ -368,6 +368,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_ZALEWSZCZAK_CROWEX = UUID.fromString("a88b3151-2426-43c6-b1d0-9b288b3ec47e"); private static final UUID UUID_ZALEWSZCZAK_FANCY = UUID.fromString("014e17bf-5878-4781-8be1-8ef998cee1ba"); private static final UUID UUID_ZALEWSZCZAK_TALLY = UUID.fromString("abb51965-52e2-440a-b93c-843eeacb697d"); + private static final UUID UUID_OBSIDIAN = UUID.fromString("ef42caba-0c65-4879-ab23-edd2bde68824"); private static final UUID UUID_ZERO = new UUID(0, 0); @@ -390,6 +391,7 @@ public class PebbleProtocol extends GBDeviceProtocol { mAppMessageHandlers.put(UUID_ZALEWSZCZAK_CROWEX, new AppMessageHandlerZalewszczak(UUID_ZALEWSZCZAK_CROWEX, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_ZALEWSZCZAK_FANCY, new AppMessageHandlerZalewszczak(UUID_ZALEWSZCZAK_FANCY, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_ZALEWSZCZAK_TALLY, new AppMessageHandlerZalewszczak(UUID_ZALEWSZCZAK_TALLY, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_OBSIDIAN, new AppMessageHandlerObsidian(UUID_OBSIDIAN, PebbleProtocol.this)); } private final HashMap mDatalogSessions = new HashMap<>(); diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 887dd32e..abf9e99f 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -3,8 +3,8 @@ Better integration with android music players Pebble: Implement notification and incoming call privacy modes + Pebble: Support weather for Obisdian watchface - HPlus: Improve display of new messages and phone calls HPlus: Fix bug related to steps and heart rate From b3cddebdbbe55eeded37ff34072bf820f4e3d07e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 5 Feb 2017 22:50:42 +0100 Subject: [PATCH 204/244] Pebble: ensure a better error message if someone tries to install a FW 1.x pbw --- .../freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index a045079e..6fbcd480 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -204,6 +204,9 @@ public class PBWReader { } app = new GBDeviceApp(appUUID, appName, appCreator, appVersion, appType); } + else if (!isFirmware) { + isValid = false; + } } } From 644c06df68950f6f502640a302819bed37ddca9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Sun, 5 Feb 2017 23:52:36 +0000 Subject: [PATCH 205/244] HPlus: Clear alarms if no alarm is enabled --- .../service/devices/hplus/HPlusSupport.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 6ddffffb..b399b11f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -355,9 +355,17 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAlarm(TransactionBuilder transaction, Calendar t) { + byte hour = HPlusConstants.ARG_ALARM_DISABLE; + byte minute = HPlusConstants.ARG_ALARM_DISABLE; + + if(t != null){ + hour = (byte) t.get(Calendar.HOUR_OF_DAY); + minute = (byte) t.get(Calendar.MINUTE); + } + transaction.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALARM, - (byte) t.get(Calendar.HOUR_OF_DAY), - (byte) t.get(Calendar.MINUTE)}); + hour, + minute}); return this; } @@ -429,8 +437,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetAlarms(ArrayList alarms) { - if (alarms.size() == 0) - return; + + TransactionBuilder builder = new TransactionBuilder("alarm"); for (Alarm alarm : alarms) { @@ -441,7 +449,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { continue; Calendar t = alarm.getAlarmCal(); - TransactionBuilder builder = new TransactionBuilder("alarm"); setAlarm(builder, t); builder.queue(getQueue()); @@ -449,6 +456,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return; //Only first alarm } + + setAlarm(builder, null); + builder.queue(getQueue()); + GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO); } From bf8ae5d5af3a4ddf186c8f3ec85c183274b36f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Sun, 5 Feb 2017 23:55:48 +0000 Subject: [PATCH 206/244] HPlus: Add constant ARG_ALARM_DISABLE --- .../gadgetbridge/devices/hplus/HPlusConstants.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 2fe12b9d..d158a0af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -36,6 +36,8 @@ public final class HPlusConstants { public static final byte INCOMING_CALL_STATE_DISABLED_THRESHOLD = 0x7B; public static final byte INCOMING_CALL_STATE_ENABLED = (byte) 0xAA; + public static final byte ARG_ALARM_DISABLE = (byte) -1; + public static final byte[] CMD_SET_PREF_START = new byte[]{0x4f, 0x5a}; public static final byte[] CMD_SET_PREF_START1 = new byte[]{0x4d}; //public static final byte CMD_SET_ALARM = 0x4c; Unknown From a3cc84c01d70d37962514ee4652b372081eee4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Mon, 6 Feb 2017 01:33:15 +0000 Subject: [PATCH 207/244] HPlus: add device specific preferences and icon --- .../gadgetbridge/adapter/GBDeviceAdapter.java | 8 +++ .../devices/hplus/HPlusConstants.java | 8 +-- .../devices/hplus/HPlusCoordinator.java | 58 ++++++++++++++---- .../service/devices/hplus/HPlusSupport.java | 13 +--- .../res/drawable-hdpi/ic_device_hplus.png | Bin 0 -> 16273 bytes .../ic_device_hplus_disabled.png | Bin 0 -> 16225 bytes .../res/drawable-mdpi/ic_device_hplus.png | Bin 0 -> 15824 bytes .../ic_device_hplus_disabled.png | Bin 0 -> 16254 bytes .../res/drawable-xhdpi/ic_device_hplus.png | Bin 0 -> 17219 bytes .../ic_device_hplus_disabled.png | Bin 0 -> 17173 bytes .../res/drawable-xxhdpi/ic_device_hplus.png | Bin 0 -> 18437 bytes .../ic_device_hplus_disabled.png | Bin 0 -> 18352 bytes app/src/main/res/values/arrays.xml | 20 ++++++ app/src/main/res/values/strings.xml | 14 +++++ app/src/main/res/values/values.xml | 6 ++ app/src/main/res/xml/preferences.xml | 45 ++++++++++++++ 16 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_device_hplus.png create mode 100644 app/src/main/res/drawable-hdpi/ic_device_hplus_disabled.png create mode 100644 app/src/main/res/drawable-mdpi/ic_device_hplus.png create mode 100644 app/src/main/res/drawable-mdpi/ic_device_hplus_disabled.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_device_hplus.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_device_hplus_disabled.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_device_hplus.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_device_hplus_disabled.png diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java index 3e3a25a2..18fabf09 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java @@ -134,6 +134,14 @@ public class GBDeviceAdapter extends ArrayAdapter { deviceImageView.setImageResource(R.drawable.ic_device_lovetoy_disabled); } break; + case HPLUS: + case MAKIBESF68: + if( device.isConnected()) { + deviceImageView.setImageResource(R.drawable.ic_device_hplus); + } else { + deviceImageView.setImageResource(R.drawable.ic_device_hplus_disabled); + } + break; default: if (device.isConnected()) { deviceImageView.setImageResource(R.drawable.ic_launcher); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 2fe12b9d..6d5dd308 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -14,6 +14,8 @@ public final class HPlusConstants { public static final UUID UUID_CHARACTERISTIC_MEASURE = UUID.fromString("14702853-620a-3973-7c78-9cfff0876abd"); public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); + public static final byte ARG_WRIST_LEFT = 0; //Guess... + public static final byte ARG_WRIST_RIGHT = 1; //Guess... public static final byte ARG_LANGUAGE_CN = 1; public static final byte ARG_LANGUAGE_EN = 2; @@ -104,15 +106,11 @@ public final class HPlusConstants { public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; - public static final String PREF_HPLUS_HR = "hplus_hr_enable"; public static final String PREF_HPLUS_UNIT = "hplus_unit"; - public static final String PREF_HPLUS_TIMEMODE = "hplus_timemode"; + public static final String PREF_HPLUS_TIMEFORMAT = "hplus_timeformat"; public static final String PREF_HPLUS_WRIST = "hplus_wrist"; - public static final String PREF_HPLUS_SWALERT = "hplus_sw_alert"; - public static final String PREF_HPLUS_ALERT_TIME = "hplus_alert_time"; public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time"; public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time"; - public static final String PREF_HPLUS_LANGUAGE = "hplus_language"; public static final Map transliterateMap = new HashMap(){ { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 785905f5..a8656145 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -36,6 +36,9 @@ import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; +import java.util.Locale; + +import static nodomain.freeyourgadget.gadgetbridge.GBApplication.getContext; public class HPlusCoordinator extends AbstractDeviceCoordinator { protected static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); @@ -144,16 +147,41 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getLanguage(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_LANGUAGE + "_" + address, HPlusConstants.ARG_LANGUAGE_EN); + String language = prefs.getString("language", "default"); + Locale locale; + if (language.equals("default")) { + locale = Locale.getDefault(); + } else { + locale = new Locale(language); + } + + if (locale.getLanguage().equals(new Locale("cn").getLanguage())){ + return HPlusConstants.ARG_LANGUAGE_CN; + }else{ + return HPlusConstants.ARG_LANGUAGE_EN; + } } public static byte getTimeMode(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_TIMEMODE + "_" + address, HPlusConstants.ARG_TIMEMODE_24H); + String tmode = prefs.getString(HPlusConstants.PREF_HPLUS_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); + + if(tmode.equals(getContext().getString(R.string.p_timeformat_24h))) { + return HPlusConstants.ARG_TIMEMODE_24H; + }else{ + return HPlusConstants.ARG_TIMEMODE_12H; + } + } public static byte getUnit(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_UNIT + "_" + address, 0); + String units = prefs.getString(HPlusConstants.PREF_HPLUS_UNIT, getContext().getString(R.string.p_unit_metric)); + + if(units.equals(getContext().getString(R.string.p_unit_metric))){ + return HPlusConstants.ARG_UNIT_METRIC; + }else{ + return HPlusConstants.ARG_UNIT_IMPERIAL; + } } public static byte getUserWeight(String address) { @@ -190,15 +218,17 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getScreenTime(String address) { - return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME + "_" + address, 5) & 0xFF); + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME, 5) & 0xFF); } public static byte getAllDayHR(String address) { - return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, HPlusConstants.ARG_HEARTRATE_ALLDAY_ON) & 0xFF); - } + Boolean value = (prefs.getBoolean(HPlusConstants.PREF_HPLUS_ALLDAYHR, true)); - public static byte getHRState(String address) { - return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_HR + "_" + address, HPlusConstants.ARG_HEARTRATE_MEASURE_ON) & 0xFF); + if(value){ + return HPlusConstants.ARG_HEARTRATE_ALLDAY_ON; + }else{ + return HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF; + } } public static byte getSocial(String address) { @@ -208,15 +238,21 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getUserWrist(String address) { - return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10) & 0xFF); + String value = prefs.getString(HPlusConstants.PREF_HPLUS_WRIST, getContext().getString(R.string.left)); + + if(value.equals(getContext().getString(R.string.left))){ + return HPlusConstants.ARG_WRIST_LEFT; + }else{ + return HPlusConstants.ARG_WRIST_RIGHT; + } } public static int getSITStartTime(String address) { - return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME, 0); } public static int getSITEndTime(String address) { - return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME, 0); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 6ddffffb..25513755 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -172,6 +172,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setTimeMode(TransactionBuilder transaction) { byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_TIMEMODE, value @@ -333,15 +334,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAllDayHeart(TransactionBuilder transaction) { - byte value = HPlusCoordinator.getHRState(getDevice().getAddress()); - - transaction.write(ctrlCharacteristic, new byte[]{ - HPlusConstants.CMD_SET_HEARTRATE_STATE, - value - }); - - - value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); transaction.write(ctrlCharacteristic, new byte[]{ HPlusConstants.CMD_SET_ALLDAY_HRM, @@ -481,7 +474,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onEnableRealtimeSteps(boolean enable) { - + onEnableRealtimeHeartRateMeasurement(enable); } @Override diff --git a/app/src/main/res/drawable-hdpi/ic_device_hplus.png b/app/src/main/res/drawable-hdpi/ic_device_hplus.png new file mode 100644 index 0000000000000000000000000000000000000000..7af95bddc8e72e22a0da9fbeb3c6de9d6e8a51a5 GIT binary patch literal 16273 zcmeI3d2rKa7Qhvt5JDhfSt#Ww!XylrEZ-Nl$q5PI07-zr(h^ycV+GrCEQL5MSGZcB zu-PmS&eCaN%YBuB;c7XStI$9xT__8a0t3xbu95{dKw&@GIh-$v%Iq?;|Hv~w-uJ%u zp1$8Ztba6rWZICn5nUn>1ZkU^l9Udy{G~Yp3OYw-@Y25c43vmmnw6JZh(N7>`Bv;J{Zhl%XuLv09tL z=1ruXOv)4=oiQaXlbkY%G*Yrb)(ErT1RS_&omxnX?EwX@L6ZkDG<1#d`g`Z@R z#R~z^EZaylk@eB2R<6THg-(y^jdG<{uh-}Ypelt@i!0Q)T7jw5CXK?RQK8i@nKc6Z zGW#fpDLrX$bvPhdWKN#-rmb1PbTxm2KafJ$3sW7ku;|e`I;m15&ub`6} zKS?x4`WP?IupU(KOW4_b-XfC;fofmX=W=^%19`Y=c7P)8C%m{)uD}~JqR3hsZ@$kJ zxHO8yX&3FLJv;~Ol#SYXoh;9CPPU1Vwdy7VgPf!@IZmFdjo;`s`vQ9!y#(OFW)rt| zyBoFS_~gj|Vs%m)jmu>gdTBhJ=Gc56NhePRFnTq{k7pcp>8ByQSpoxirxJ~zQz88?wb<6cjW8LwNczcW}wAv^)qK$ph5`1-3g3Kt`EugOJt zax5}GM$rx;-^I(UfO9<0`xtw^(2-ag>q%tYZrVpOgbT}X5e3>GlrfWO z(1mUmREN;`E{3E%9L?eOLd-*A9D%DrZ<=wTyIoBE%L%U5zskTd8=S~o+i|8u*2Ve& zc-pEqpFP{!%KG>W!%k@ocHdCsH;|UK*rzTl}4>`k7CY_2> zl14&}8MLGd)996I%&xcVFpWy5Qj-c2Oiz8lb$#;&u9H|YUl=~Xb#P8oED7A#oy1`z z)ubA@SJ{mUOsUipn4PlIn8U8Kt8|o6X;&y~0);qh5NHIT$an}>$R6P=6bNP{4Mq*6 zpfEk9vSS*ZL5Ud^S}mq=5R_U40ueNT3wDFEG~%W~s1$|+ty>6XS4epVomYP~qyz=M zm+*14FpMm+nrT$~4-p&=!B2BwsF?^-C`@ah@My}6*ITW-tXj)zGp1oWUk^dhe~5Y0 zF$$ctr-?}u^b*+6v^~c<_yVEBazJss-UJN#t#1eiXuR{@NESF^^8c2D*K4W&A_t9( z>;GpC1lqy)q!Wx>Fo(cEtiSMjX$vN^eoxi(X~P~g3;)qV|AK-7a~kvwm>Uh&>LLgC zX9<1i3j6_BH3o&jXi(rv1$b54hU#J4alc_3s)udsOhAmXBgOPmj+m2g2o#6=00I3GwW z;evpOixMtzK9E+z1pyHkC0ycsAgzQ80wOL-xWxHDS_u~fL|l|`iSvQ95-tdcxG3Qg z=L2aaTo4d(QNkt82hvKoARywRgiD+cq?K?%K*U8+ToH9o!_pq`2y6j()>VIW(kSqp zEJ~)N+Yn?*EP~9OjUZL8!1q59Bwv9bcd`-0v>ZX+W=kd>AB-T)FQz8NXZo+)y=r5> zOX=L|Vcxagw#4srHr0{5H9OTYbBXK4`1v-QlbwJ1HR90Yk$wNrr}d5Yw_;meOkCgT z>oY52r*<@;Pj*i*3@EkS({0=xS8?~qx|j_+*F-P9TsZjIMDqFPD|T-Ecj1RiO0xI2 zY9Ae5l-BMP-Dgh9-004g1?%h+7e23etD?|wte1nYo#D3&jkn zlw4?2oDlxeuP^?1la9_Ce?98XE^gwTtA`JSb-!nvd+6CowE2ny`Ke6buR9Nq2EzbM43GbSM37Y_sVzM)A1g>5r37hV`9uczJa6&yS)(qpYHXZ+R8(sQ)@20G)T=Q9Z@0Bm8uxn;scrj1$D0}_cftpcS zYxg|u85OtU_@b&&VdH;XI-w-KOVQx=8?=CTv&hWLiRj6@GYm`07cU&qb=sPTCmlaM z&_vzZiF(KlGuuTyA9Tc{{`4Pg)$b}QTQ{rn&DXX+Wn63RRa|*uLEN!{v#0*DsVGif zwS9hE_`fW1J)$@4z0|zLqaCSHJ~X=7BE$JSd~^65^{F0gW%!)9uvVt$;YHn|if3e% z+NQ252s`@DMN6~N<&|M_b39slj+t?{+?p80zx*W|?^mo2FIsJ#;GYv$HgWTyB=kj# zs3M_6x;ZaeV;+Qs4{JZ6Xvgy7h4TsHl0;)>PW-j9=?{w@74O(`CvyGz4W+a8FD@Bg z`Lg`gM`KbQ-waE=JvQdEM^85G`=Uoyw=vVo$Btr$^pBW)^W31z_XdXMo*_=)fp2c*noR{a8m-U=Hq3S6zb6vuzUE>Gb zw|T$pvS;n?y|R@pGp2vIjZNmKVMnWSFHcXm=AxfO?y#h7E;zqyXo@X%nBxBC;+45; z4?KN0;gBv%=C;MXc=y8C*|CvN6h9kZMIjl>*8O?bv8^42hvrk0(~`C&WY78!Xu(z7 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_device_hplus_disabled.png b/app/src/main/res/drawable-hdpi/ic_device_hplus_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..a4a7d7fdbdbc117c5fd932760aaf80cf66a613d8 GIT binary patch literal 16225 zcmeI3dsGu=7Qjb@8fme(K1!{vAzjXbb(qPEJV;^?uz&_Ipar|NP9_sFl4N3L0)cX@ zBCU@@TernpSF7wYQ+dT3Q--B#Ui5+30Lrk-xk z*?%VIR*;5&CLu_whAu4PiY)W>B;!sd?2L^{4!FHwH-Z>u2fPGXN^w#fRYH4=vhJo2 zWK!C0ls%!g;1+K><)pJJeN;hZZXsD&O6u&g*`{biKo1ZlG_#}%T&~7&C8oqtg;KA= z^(uw5?2w&bz!VA;>_FKH z4@U%05Bq4yNxz>AiY0xtm!lbvl=n;67(Zu}$@oA6ufB7+y#s+fY#%#75epDrOrDHm zBN^Gr0UNL1=L%k$ox~^?<)%Cw3+&`0+IgJ}$FNRjl#m1JQ3HdVSm_cc#}33F@tS?X zJtJNM@L;hBTf5yOTC!Yb8GzWAlo8{yg%w^3vr;VM_mNa)8G!NVNc=e3F__*Vo_Niu zr>6q+C}zASWEL!@P>v1Yton2xMR1I-kYQY=P@U%YCqbH?9;{1gqJ2#3!H?HN^{Yr zl(et3NIqqB83nq~&4B9Q8{b8fl!v8Q%vOPVNR%ZoCFo59#&@@i9(p;U)uC4zJm#55+avLu14;1EXSfD&Wk@gU- zus!@)>>wEKAhkF_I#7+)u0mCEHGyhvHWHjlrN&8(Qj4qc{tzR$85SywW)%sh^O85ytiZGY35FP<+w}MlP5`U?2`%xVyB4k~y@e`ulWv4;uLYXg%m(P*7k_ zgT4WCW7yhYiz7Ji&KLC?Ti)(dS9FybV)n^;7hi!|0#5PnA!GNq{1O0@xlmtl*1IzYLKGH5V^S)PdoL<-u#iV+QqM>7ml^cjYd z6?TUk9E71&aQ_T$-LZjt_hB<~V1oUeKL$_qb9#k*i9sMB!lH;vm=B~CaX~P5f=nRSQK#y^MSM?E(nOQ zDB=?418GHE5D;Ne#3jrJ(u%ksAi|=EOPCL&6>&j8ghdgTFds-O;(~w(iy|&zK9E+# z1pyHjMO?ysAgzcC0wOGmxPNK!imRmoOhlE8>EH2#X>vVLp&n#03En76oxd4?Yb`dB7vE z<=|P@;)N$Q;5k_-nPs&gNM!S@oalceY>-{&XBJr_w%m9CC`F9NxA{`~owNF7rDcWtgNX5FRK ztxegI;_T|W$%%{AEsg`lyXqDt?R=+aG=G zqt~MTDy?~&YwGAcuy)eLAAT@QH*Q)oDNk8IPup4-(qYiv|ntTkuP;&0SQV)3ig zi=L+a=7}$UQHe^bKOVdMlS5;c79=%15}&!pIX(Znf63QdKR7Y|3~BrOkM&gEO6}~O!MMlMGF(etPE=6MIJt>3-1$}F#_s>+Z?=3I_{!CY+I(dm=MMqHfk z{OZKai1X1$pKb+doQH4~73<8O`<5?XWtJR7PM$tJC8DKcYG!?0{he#f_CnYGLsf6i z-zSY+-*|YXS$^pBM9Dkn;$tJ?PipQt-ky2&FXp24=Z>{4TPi@L6Z-z5XR}DJsA^v$Q#(=F5q5s+OsyJ+t-NjMmtl zr+VkC&6~6K;k`Xmk=hkcqGy}_{NAidNKVdf|FI&Am&q6Pbf+*$E!+L?>w3{X~V%c@F!L$+;;=tqPr7=)W7)$VvOeVX8s}g?9AMZ JeRGyp{}+X0Jz@X= literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_device_hplus.png b/app/src/main/res/drawable-mdpi/ic_device_hplus.png new file mode 100644 index 0000000000000000000000000000000000000000..88e4c9c13a22d6819f1ee187c216feb0bec464ce GIT binary patch literal 15824 zcmeI3e{dAl9l$rKnly-|Lo^7Ho|mB$Ae-I2+|T9iLQDdQ&4n}x5-LoyxBD(xa<_Zj z?&U72+MuH~bWj0lW2+Tv$No@@Q(L8Dg-m~FhqfavV`qwO6EUqrvJQ~`}KX__x*m~`+VQ`yZg_McC@dZU$(f6qNw?;Elr*9mo+}6H^A==#nWGb zKc9@YY}6>K^}O*Z>RAw{in$=yAAPoRw#)kv)%sXRjCWZLUrZwqsQ1V&(bv?>6Od^q}P1Mz@u|9_LcsvYiV{A4W z&Y-m+MHiB^qE(qrX8be(O^nG=T~-x~;a3Q%aouON8i6ve^u8j|Odv%|vx5|wq!4Ae zT9zqfB#9ZDXgn59Elm;`5C##T=o+-+3fe{cRbAEk)j2|D)N=-gIq8)9`gJW6zu=l< zsW}Cg06hc(c9Pq!f&+bq_@W(+M;+9uqh@UQpvgyt9WN7Bx zJxI!Pm>C+=EOnU79DCtj`NkL!bT!tks$su*PCI6jU} zmW}G=!-CT1vnFW?goJolxB4OH23?QI!MIV8bi1lFs*wnYiLwx;yZVI~Xb8&#z>+>% zqLDIr5Wp&osPJ?cg&&qhplCp2fUfs?61J|M&ffaLFv#X&0bW^v98=bHoqe5<1Af+B*}CO`C1E6Wnk z1%V_uLm}GZWChyp03sc9IBc{V2%IPgT#$9b1;N?*=CfQksbbt{KG3zRQgZb@EH8oxkjf3e=9Xud;nY_{LvND!wGkJD8uMUCfKgWFT7>RzM z%+YB~FR2N0+iPk_PZ$-}2M@>9bwJZ^z9AZ*aOGW-ENH^u|CWQRE2;k?2L;;o|1$>$ zZ8N^u4_hwmL$D#{4_sZ^%w*=*)J&buu0gMHjpkwf!UF~SG^`ugH)f4xBb)Pkj5-Xb zE&zVJn{|8KEW@$zmA1{*qqZAA#x_@v+Gb7HE3)plrNbe^IabE4gn2dSg7?k9?{qlo zVAn#2QHu^a3Bo;1GVH;c!Nl%P+n|bqh8yQH%u%Eb&1B`=;iej)ISTKe;jKH9xp$w{ zBQqWBTK>uI=vsP3e32js5WGlm;rSq1f(rqH7YQyrA4E%VAwcjV!G-68XbCO^2wo(( z@O%(0!G!?9iv$;*527Ww5FmJw;KK7kv;-Fd1TPX?cs_`h;6i}lMS=^@2hkE-2oStT zaN+qNT7nA!f)@!cJRd|$a3MhOBEf~{gJ=mZ1PERvxbS=sEy0BV!HWbJo)4lWxDX(C zk>JAfL9_%H0t7D-TzEc+mf%8w;6;K9&j-;GTnG@nNO0l#AX4T z@FKy5=YwboE(8c(#NsN;J`D>L_y}wQKI>X@DBKL6leLH~odJp(UPe(P4^Y(f75FReeLf!SnU#+32;)ShE4c*CO|2+1)_P19rC^`G5v9YQ0%KLY|#GOg* zteX7&r#f!hl%x(7zp`{ibL5$Su$|Rg_P3O8ePMXpE63-%&tBL3y*DO=B{wf?zp-@L znIBbMQor`I`pM^OCKo+idbsl6D%G`p;z*_Kp2U4O7cHXhqHHA}ZWU^`tz5hR_RoBt z8sFY|>cV}A_{!;*-*0_B@^bybC)RsMfAY}J9n05MIHXGM)6r=2@#80auU(SY+{8>x zy!*yfw0vhtFV(a2WTO0D*UAq*dPKkb(!lpCN0cQ;Z>j(7T^|UWOE(q&x{~UBhPvV6 zfg>FULM!&}>9t&^R6kft?fAqW&V76FLr*&c*PmT<;t{WWYV-KvMXQ(VKfUA4?$ry< z@18vM)i#k79-U!bd+|asA$Nt9pw*TNJu{ z+l$n^WW#vZZ;ETimj>rS3BIdWjsJDtYg<;^Tgr-_{N|Tjhu?m{SO5Ct=+|F;Z0UO6 zs6$Nb-EsTLQ?1hqH#K}4Wm{Xi$u?Nx9zOWa=C^h)?p?U1?TLj?-CF9t*nH={=PK5P zk3IJ2*V`bjiBl4(;4Gp4xpOeDt|_+mm{fsOL`tAwr9nz+Tmr&Q0|;V@J9~S+ zkMVhe8lo89g8y1Yv*4%Y5wSSRCT zX@`?{vAoM+$-b=qa`?@cl45)~ureDCN;TFtT~G21)7jZ+>vY=GR4ZfWIF4Z*jKe|0 z8MM}==t7!Sv_+ z8_Se3lEj=%vLhAGT$&^@APy2h(KTpiFKL%-Q*~8qQ>O@-Q%@Nf<|Hh)w&_|fe#vW2 zW#*K;1n40Y!doX2B`r05#YPA*o0O7qwdSrQV8TFCJ5nN8u@S;pREnQ2$MWed@?>bH zTs=t3QkI77Nr-t!mE>60gt?by9W?81h9%+f@(wm@ zUN8wYhm|J@y6{;J3a1sgkVHPFrV@hgmlHxOV3JC!kI4@f&onnt0Z&LZQ>C?Wy?As& z5tohX<>P|VT4PPq5{L;Maoy^NoNIMGB}Y4qiliG=rCv=WKuVN_INj7Hq(E(4ZU>g^ z))I}B$r}Nz!h{NUhf(-(SppIa+*`nPpB|kuO(q ze7zc1QxH7xJAF*4;Q~0hD&#{#svSuF=2QpBWZo>!iQ!_CP>2sInl30J2nPJnhfS6x zKFZmvJ;39l9jrs3U7R4%QIV^rS;20Ht<&S;JkgBnV)IF^1FG0zG#}{N?9-AeLihQV za6l9}mlyWmD96&!xj;vyD4=6ePt@U&ID3?}PXsD(HYw1zAw^jc;stYzwMa0S+a8PA z#cGLWf#juKg4;t2Zdf4RC=i7h7Xx0-JrSaWn@OP>WDS;X7m{5g*!cN!V2=Vx@Wf&?=V4(< zxq(PW-EIe64FtO=2yml&V2%F=IVfz`cHYgwcKzSXfkE4hFSfy!3;Pgkh{Xf{OKH2a zPWU;~mOW+vFVbe#U~wy)Xs?r7r_cCl#lyA>`v5#%VY`Fve$rTe!JAf zL9_%H0t7D-TzEc+mf%8w;6;K9&j-;GTnG@nNO0l#AX4T@FKy5 z=YwboE(8c(B)IT=5G}!l0Ktm{7oHEICAbhEc#+`3^Fg!(7Xkz?5?pvbh?d|&fZ#=f z3(p795?lxnyokkBo_~lLDDcVUPWXtj_fIcL@bPAg*boj;RQH1v)%zqx-5iIX7b&U( zJ~#bQ3q|oSP}Bl-%MXsPq^N1-!9ZOCj`vr&I`t9j?&u_o7c=YhRPyJV)uG_Hr z%yt*|_akLHBlj;H2)^^m^{pfQ-@IITw5)gj-gocqz3zTuvyFOXf5k&()Z29f{Uc+q zg->jHBl5UoThIQ7fBv$6_?F`DJ8#*&=?jm|I;*_4XZMn;-G@Ea6_5SnFEQufs>^#4%{MoR- zYVX%8Uf%JWiwiBk{IId&o?p$F{^Fh`pT4y9qq60fsNT)ruNpeqyu9Kbrf&0rcP?J7 zf3R2cyXIM*x^bKvkIh})o+ulcJ=*=R#E-{L^{xK}<+?pP(i`j@9G^bFrEI(+dGUAD z?1MYbPFs4^_}hN}!Qr}*FI)EZaI@K)ao5jw_tjSP?DM^M&3E9~mo9zvy}CtJLoJ>i zf2&-!;i0YJwN;Aawa?Ro_z;V%3ngYu2&RnZvi14fjNp!DlWE z4E^Zr!Wpag3A@hCyA4+pxqj>TbKR@=UFzB}^MONuZK(Y7*8=*<7mv&xz0g1KYwdP_ epy`uM)ZxC=f>n<%d&77_KDeSW&|lZ`#J>URAqYqS literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_device_hplus.png b/app/src/main/res/drawable-xhdpi/ic_device_hplus.png new file mode 100644 index 0000000000000000000000000000000000000000..e91f060f82b4fda154d645aa2e618956e407b326 GIT binary patch literal 17219 zcmeI4c~lcu7r-Y72q+bcrz%y55i4>Wvabdd0R=TEiv`pkm`o;QN|Fi500B{P#jUl) z)~#ACE)`o9Yg?Bha;&J;qjha5RX>Xr)VfkZasA#TtOHE@ov-J7=R0R|AesB_yZ6rf z-TUri{s>E^#E$p&^7Dcq$U7!FG9G-UQNJGU;Js!|jskpmTB1{I5ERou{c;Q=ynP`^ zeGgAeCX+RjR48EzL3D%`3$dFlU^dW=wp$Q110%UwEFCw8^6r0kn8(F+p}ZuiMxe1o zUc6h)^n*OXMTDB7soK7l`>{0W1=$Bm$L0#BF}@!o0wh z+N#s5;v>g3hXYb5&p?tE6`!AzlM|974k4`Re4$dQ@?IzL9 z!OqhmB=T1d+D~ieLWipKgw=$QVYmrN$M_a=x|-iM*m0(_h}!W4R2{oC+(>pDZBZC; zYWJ#)h&er!XNPr|9?3G2yfDByoFuKdHjCPka4cbtAWSCAisFb7PB0)=EZm4^VBF@? zLaCJD*%;V`CIVCkwegKOikWSgjjzpx%_wX`_+qd()qHAq8}W{};~edHmyTt2T9LYS z$EhL+BVh&Lu`sck-_>wCIIX)-r9rG2m@X{QnuR$s@7$a%!yQdD8dZ$hMj~bui-`;a zK0zdzyh6C083CKf7Wp#N%>0$3=NBd}Jd#bCWwrWMI_N}*ODbgTgcYUiv|pvizDZbppl z=1^(x;xtmVo^x~M%y}gallR4B5O9#dA_d1}{Z%0FVfUbAmKavF- zlKtQ1K(CNsh)^zs5k2V65`|O-E45lNjOwL&nOKHMC2~Paj{l<^bkVN=mpPzlJL97U z&~iZ^0u8a_!2eU){$3|^u8&O{m3+tP*Rd70wAaq9Q%(J8MYUbf2f*3oyOV< z#yLNkYM(~Oxg91^2oy?%fG-q)SF>$53tFKpZE>9%dAQ_VOTCTb2x8*XPs{RQYe z#1p{d4i+YpO2tw&pFT{tn1b6eV{#-8j!qlJL@8^wp)0yHOzF3Tr?oeB8!We4^7MBq5)wRSzOF~ zXj&E*4G6Qy;$r4Q)3Uf|K$t}q7c(E4mc>N_!Ys15nEBAOEG`-lW|76k%!j6BanXP< zi!3f?J~SdiDs6SDZS~(7a+q+YiLmR7p{<(4RkCo^EuVrzMf04LMC09qi;$){pp6p?~ z;QwLI$W0z8YTY+qzaH-<8X25*d>*#&=%?p-ah^+}zF4*BXm}uJ+p?JF(SvF-YXYm% zw-CNjOKV?B#Od0m+4GPAF8WvVmK8uA1>ulu(5s5-b8&+=Yrc9|wliRqTUF^b%l28Q z#C4Bv!O|#x)dJ4Viw$M(T-cNmy1vh-Y0{7DM%+xla|9y2j(l)>#)_vyvOSqqu=xmVzRWqEk&$m~6% zi&S+hw&MkRh6>?3P1hk&m1s!>I~1*9L2}tLBsEt4}5m}>iOrl zW=?e9##>nP$I68@RnYTWU)BYedY#P-PFIxg->Vz+Y?tAL{0*VB{+Gv5=z}vw2rn_z zSLNpxq!~rINHEv%i2gTfi-k`v95FOyCqDaaMxQac)dLG+Uli7;LoBnJo*%DF0;?oL ziT;nojnfk1H$b)9OYpN>Yx^fzGXGe%E_KF-TjKhl*M85hip!W#{o1B?tHHpE{2rW< zx?x~FlLJEeATeqMqdB9V8@Abv1Kcb*s2g*zR(tMVJ6)z7D7;>#vUFWl4 zEI#Rqdqw`an*6G8kNqX?VXmd_+>#XK4wn!6c|27-@^QIWzcc#g_8yzk(BA9MKRo4M zCFg8pqaaRn&aNID3nET9K0Y+>*4!Dxp{+wh;s!rvzwYjVAhgP=)09IlDk@ zFMn5J1@BL9x#XuLi{4l@+NdfS{;EtKd%6113R%YBUZ(AD(&uVyBziLho&TBpuRp0MBwt4ma9reF&eEi89Gtp!I<$jOzs$QSAT^yH|znycqF9=Uq1wy|NI`#T?_kxWW9FMD zjGs|^@0Nb$qOC8XfI@#N_s1^$Htt!EU>`48_-GWAPW?BzuG{55*Csvn^*sb)d2(_N z+}W8p%Wx=STzS2G)su?@vv2I4BhNX}Z)Da9)G_wCFDWlzJU4I#z|}L z#<)#C5MFYy_N;crfI-C(hYwag>J@S3Md7ltsn*@SWiO6!KcADC^my?=?}o*uBOkaE zGppp2hgUWIEA{x{S^Bh-NZl*@h7-@T-}WoJTXM7aua`o8IDa@O5Wjd~#FsoVen literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_device_hplus_disabled.png b/app/src/main/res/drawable-xhdpi/ic_device_hplus_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..2c3f219cd3329d79df3ce3e109311895ef8d1c3c GIT binary patch literal 17173 zcmeI3dsGuw8o)20)o9qT#1IrBLbgFfRHR*GGMT{Cyfz8)@I_ax z+M;U}K|Khq2x{%BwJrF<`a;`Pe6)&%_y(oQLtPcM?!w+lc!U9_-QAwEXZK9bNiyI4 z?)TlfzwdD~e`Hx~%tT-B7rg-hzR^*tIQX4Ry$271pV!x>hrq92S)yjx0Em7_y**Nc ze4hnC(L%;2*b_8w%5k$X7}J@xL~w@D0%t>AScV0|XAyRemPjE@q1-$BzT$F7T_|_D zSi{p;B8XHnD$7bt$%=``vu5Ek9XCwrt;mo=2S&nFMdg>B3;MH3j9%WHOW|Km`H>&OmILCOeja zm~5k6PCEUl2peuCEq2mu;!u7utvSse%H>iEb-gdeCT?t&PS$Ep7G*vmwNep!B(}Ek4sipmBuFoMy#|39})L(S7l=llt!P_V7e$ z`rSQ|LH1)tXhXY4N6y@#&!mQ6A|IeOHQB$!$}hrnIHv9 zH|oxrZhwRdvlB{~(-MR)K=`6~0ZeF#Tr5g-vv*CRnQP2CQlHsrE=70(geQ)NB_WW? zMf?tPw@Gw!Sa~|kj{TGa_i1h}bhun^wi+?Jk~Cr|1Zpv*DA4Y~o-EAu^#J5$Qw(g6VlYL`+CSgaR!e7wK@P>z?MlT&v7@ z8r6KDYgeDvnQ`d8dnZC3A>@ZhVE@(1cnF^_#SpDdOCWl!L@SW!WPB};-?@;RvtA2L zf)q&;W^kKB?L`L{6Jb1wOosCi8G#EBkwC9UWI{24@Nhm~7$OeABp8GXXT!bp;ilJ8 zQKSu)ZYC|dRLG|gf9yHxmV%Vtf>~_@)r>;9o!zMGgm5(n*QdhS)Z`dWC8pAuc!W-Y z_89HHtuD(BGqQI$e;xvte>d~~*GNw#O#O5kmlx-R{_SmMy*-_(uoRdaKd%G2{Pr}Y zFVJ`8{UTAYVYC0U80bSp1jd*0VIjfpED8}z5SdmhL~y-WFA+*Gu}I46jPbt|1Gjd~ z50MKbuwDN*V?fb%t&gX|mJ9n3Y=}Jv{+Gh`^E#nxfB$aSI#1dEi?F$BuxBgmY_Gjr zr-J%vMYUbn2jKAv+Z}B8y~er|#x*~IYM%z@xvdn1@IquE0+i2#uMS&xJ>7Q3PuRNa z>9*abQ%$5@Dd<>^Htc3a{Q`6y;#1(`4xyBY#X_+Hr4Q3BV#y4`kf0*r(P^WY$Rr&$ zbVZ+rGx{@hQ*;=*g5~yL;yk}xqwui@K5?O4k6gWuwyuNcm%QlS(J$$h=8Ii~283B; zaWV6uX<1w}Aj~3*i3Tr?oeB8!We4^7MBq5)wRSzOF~ zXj&E*4G6Qyh|9bCPt1e~{&P7U{zaKv9rHE(dou@*iqim)^%?-#3jui24!>^!kj4Yx zb}|6+wE*~;SIw-91YqFD(W>$B8MRH9s$v`oBL`hwv@&c}ta{jDa^3q+CZFf+yO{0w zC0kZ5kr&(N-@N~1ODS2m=k4H)WNpH* zrLTCN5gEtipO~7v-fFHth#dU#KXy;_0glcv17-c zZf$K<-f3=@eO=zv(lY>U%y)u}(C@U}D zK+QNV_P>aZn^jv=Qh*wR3j`f#Y>=UU-RIu;0yyk7 zW>j;Bhh8nXLgbWOnkYR4;C*mC$K4&xIWB8o&M)IET3ZcoR(Z71_uz)?RW0{d$iM)l zXP&{)R*^8q6M%Bj^2j4Hg~BipTqu}Sq?EP!!rXDad^pFCACC!h+`D)0^pKtV_B9q1 z7S1o95)cp&Q+#&!uFB@J4TXhe^Co>>(=_Cx8bjND;Me?MYSpDBOO`aW`GAYu?1c-x zHrE)+Zq(P;@2hM%dFYS_*zTG9DtfqciHp;~&_hFj2_$c6=XiCF0+av2vu7te27hl} zv0?={GxT7Zp-TtA?xAi%-7L>+6y~owmWM4i*vot`F}V2 z)q3lms4o-eCGEKI*}?px`2~JxlHzp3{mCWqM`y*}{#533ZgeRu;qNw2TD@)CwwS+6 zI}%>By4-w4PDv$k>+U{Un%20sY4cxs`*-bH^~tntr?;KIou7}+lt;aFAb#8V1=FTY zi#u>?=Nrwd451q>DYavBKHC$w^+7hjb;+>n9~vDpZsPTVc5#mX#@D!s5XcMR3hu^A z?Pt5ksf4h4v-})MiBq5c?*4=A&$K=D3L>h@nln#5nfECF$r^sEC84h6if6;$l?`*J zsn@}@ToV_);P>B-tBtRC>IKvIhE>**m#Z0m)~8`^9$zv&@VT2t$8T$431o8j9+wrt zE>R?GSn^DDK-0?u*OxBOKXGF>Hu4*)F7PqN8P)ZHstXTuq;)U7_hFNyVW`j5jgiKZ zb5UQs0|#J3x;3?6W=iqw^3thy^vL!3-jUq4QSJLD+g1W(K-kg1W94_RaXv_Y9(=sw q*bkFqt~S37>a!M3*nREqVIaCL{5SchLyc5MMNf!PZ4XcW(?0<`XSyQ* literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_device_hplus.png b/app/src/main/res/drawable-xxhdpi/ic_device_hplus.png new file mode 100644 index 0000000000000000000000000000000000000000..01996225f5c768cc1b56fddff5db8bf5d212d759 GIT binary patch literal 18437 zcmeI4cT`i^*1#_yiWEV`aqKaOiW*W#F)@ijK#59`GN7W)O>zT~kU$Cq1{B+j%2=_2 z1r>oXBEu6I6&*)Wv4G-;4Nz<-GGalofXX{bs8_sizV*#oZ@smW6_V_;&))l<-#&Yv z+&{u9U+-x)mV+%J2(saOy8D6m^}0Wcp5XP#)CWhvTklBE*-8lFH|qXO!kldSLlFC= zBp_H7>@|}mlt(zjBDnx@)D>>;1C8XWcLnkJC?4g>&$( zx`4P~FJGLST!G+d&UAv1L}%a_OlLBU!JslG;3yNi#>!N%h9FZq z7@V~EaYvLwg(OlXk;`y8zpy~AR&nrnU7+?y>$)N$+XKmzt?U3rq6Us6lATG!?ueCjUyvcJ)^SgPizDLc>(b_W0divqC?o z+e-i*yu7fjBOv}(Ex@+cl=ZmaYuT)c+zRU zx_U$-`HGoNV=&WiCPR+dV67}S1p=$&iU7G>$~Ej$-?k*++}!m05;xWh7D{Bg%s8=$ zXrqoj)8P+shgAp{>}fiIOd*h|0Ti&I=`0#`PA7ZAB$T&F3q0K?%iC`7{pB!|a*2#rP$P&vH5wMCYiGV{9Vx%mTP3#!#I@7R;I`ITl z^;POBRdpS0Q%EJc>Sak`SttjuA&3w$td^?qT)^2?rBX-)YF$MVyyY@Cc|-)F5K3Sv z!9NUEAg)qLID%{4T0&jQB+&?{!U#Fo9lFAoN`#0^i71JJSb|JQP{KqPP@8O`uDYd? zu9ssN?Ru5^Wqz?Dc6-IK+~iWZ0)R)jE^K0V!=2!?S0T#_R)ixWZh%6K=reC9&bHyM zCSG1FzD%itWkQ7S&ILZ4B@z+Kg#^PQA#);uCKL(?RDpm*fJGDN=wQ9; zuI693c9#p)y5<918~U_JE(Gp7R>DMtLMD{~`mcaVB9O@pm>>`d5Q11h7f|RTCRsot zw*~6t?2AA?fTBbOOFPZctwjWa(a2&kSvXNdAR!_KfeO>;1egX2gdsqLu$U=E7))AQ zh;H0`5z14d1f?5`%C0Wu{zz2U(N0@Xr;mgcN<`O;IQX`1)P6!3nuFn$t#4{9Sg6~W zT>ZvFL~LT0(T>Y%w`?_&e3{O_hrr;!llj+U#DpQTuXGxN7yX2<+biW_RgA8}Lc!+v zdmYf=x2qvKK=+mRk7Pj!NB{qFz@UnlR2o7jFeWnT1S*M4Cct!rN}E6SHK2nq(0jkr)iutg&-Y(6Nh5f=&wwrIqK%?G75;z9wz z7LB;D`Jl8$Tqq#eq7fH1AC%UJ3k3vQG~&YMgVGvtp@3kEMqJo@P+B7{6cB9Dhzpw! zN^8W00)j0Xabfd8X^ps0K(Iw4E^Iz1tq~Ur2)1a%h0O=0HR3`6!4{3Uu=$|0MqDT$ z*rE{^HXoGMhzkV-Tg1d=+3_=GL%#X8g-@(&TLg6HYC$v&b{sqqged^`MN|qn7Z~ zj@9_AWK+J)n8Qis77yH#sOnKy*C1mmixzsU-YZHa7My2W#joRP9xvN|XHf43=GupD zSvN=)7KwJyNN5VQT>n;Yl4asEt#N31e!-B}=QyQ31I#TyS#z}6rz;L8Uc9GiS+ehk zDodF|{y-X2et>cA_o4ouR;HL|nO?ctZ(B*$*iM^S_p0t@X56fV&FeA+r9TY_rwl!;P0d@i z9{95LlhCqlXC*@s=Nuf>3h#NdQja-|ZuI0QWXBaOxMk0`9yY?$y0p(rs#6x(@kep5 zes0CRmV5OLdCG9F-g0RtBz`# z&)4i-77ees{-V#o=kXuo;l^7QpAH?m5ty|hk+p05#JZ%dNkQvEoLR)>I#Iqz|*>E`EABFZAv>*0~|uHhp(VBRm`XIA^c2Hm7D)X{d+o z=knYEWn0`%yKVh$GJQ!V;!+(PwJ1URerw$mk4m>}bKhy#mAk5Q?w)hck3RYS)5YKJ zx;}bbR9tU6JiN!F`%cpM?71_>hi_Yc1zvgkS5t^?(k~9# zHf0&t$AEqD!hAms4*yHak)iU zLStV6``g3Xxzp>+(%wR6)<`LmzUw3|C2sa$aOJD-B?HDq2-ks$%@==e@={LtgQ*7D z>r3~5vN#ZVd}h?n@^p_KgY5e+#rI8)s|pO8;Cpnrlcw*0YHE)0T#rO26Q`W1sb*dII zZ^cxT6kDrQJh|Q)I@~+$2)|c9*8bTkDiEC<@ndh-zA<^hi#Hs!UH{I!;8&BeP>5xmmc1+Uk0(Rv3X!6y8Q}e-GORy(zm-DiDC`fY~Ks_CMv+~6xaUj!GjFXazVn&8cycbE4>!nHA|~!g^Xog zidkG01?48ZE)q`8NqCX8bL52TPgj{CV`U#=M%+189mxZ)w!F&5U(G&OY%W}9~+rZyycYZF-vpv}>ZMP*Zeuar0)cw5wiH_ zpV0%-idO7B6#uSx*qFKs#^i}01>6D?t|sbmU3E-u{NT^;s!S{l%hmT;!$sIZd&JP6 z4L5`)>A|mI*uO_&g^llJzia7Ahe~VS3XXGH6tr*#Plem#pORy?THJB#B+MxLy2ELsBX{zdKt=shAxXorh{_^ zmR;riUXoQ!9cZ@0f9pHPp1gXKm9v>^{z_l1qTfH!;y$zP{*Gfal@sm_*>l1fm$|Pe zZ*5Xo`WwfB-}4(yd{d?HRQl8V_a%PE>%$+K$1Q(*%!m!w0-K7vffgq4!r5M{?d-2M z9-V(S#oFcZ@6DUO+x=&=&zi*PzpN*^)V;Hl%Z`2Mvgpyed8t=5 zk=YNm1Ixbg)UNWO#Xe}dUOt{NG`1jOe#p9DJ%(ez{eVk1i~Z-*Pb_ zyFM{PSa?C+Bh8C=^I2Ky$k>J?r|r!ja~sZ7pPX?*QRG@k1fF6jF`xQ#nm#S+Gye>CUr;F&e=iUV18K|Z&m zUT-S3jvIVEsEUmgU-yp=*>AQ?`*G31nw%_L^8SXDcV>be$^FRM(86m-wF7%ikASHA zth)I}{sY#D16BDW>Ad@z3q$wRQ6nnAngp8f= zEPj+D&1KsZn;HC4>#2@sg%yL&S+9=0+5h>S8=~8HZfsm!Xm)yxd01sL<+67CLjRaE z>BF70K7pCdpwSCz-zQd1SQz|#o26@CP-a^3LxHJDt&#MT|Vp zRPQ-A`plHFArBjV$eAgAy5wempTWLr_rC__>gup)`EZ}@B^ysR4e&_Nr1Z|R#OJDK zK5saBld&{q%M~;K*xc*ivUOkM{2WKW(NunzAO4$ZcH;7ASDPP(54M|PYq<=xL=(G( dS3ggNj?EpmAj)Q??*IBgd=GE;gHyg=`fq`L;Aa2; literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_device_hplus_disabled.png b/app/src/main/res/drawable-xxhdpi/ic_device_hplus_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..a70528df4d263af0690fed12b8ac4313c9ef2cc3 GIT binary patch literal 18352 zcmeI3c~n!^*1#_)lS2h3EJ6*j7NUmCgoF?VqXG&jAOeb*;U)w!UkhVLX{yyAUb-|Jf6`rb-bhP%%`d!KWEdpOA- zSvMze=I|kQLm&tm?&r%12A^AWFH3Xq{%FR7W8lLo-Zw%CL4NJJmq`q6_$UZswTMH* zRp9}%nS8m_g)5NrV3#CmJeUo1o=NdsejKdA@L-Wx=7D{F;xrZ`7I)dk8Cu2^>ga6&I5vmB^ILBoD0KFB4qrh6z}Veu^s21M96@ z5EC9S2Qy8sfH5=|S3IBSO2^O{E+iVAPNh%9kclK3fk+`xhEsjd0?@+g?b)c_a%+*SxBbrVh1P^lDP2%k_(YAkdc7j zV-v4dNc3A1@CmR4mclZX64;Rjw2P0CtK`ZU`6rg_QGaq_5R+iBC`P61S%1JiEA(>) z+yd|r5P)til@4gBR83C+5W9jh;JV7tmoJv-B7?OF7rNXSpJ_;mzR{&hFv< zCIJCVKbca+mGNOe4jcGz5sL*(m`|j0$wE5bjVy4(Q^^c6o+0E3@HCphjp9b43#fd) z-gSTTk6d%)e6_Cm0M~{-Es*np``$ZYzEffSOie+3$pE6;pp z%hx3)Tc3EafJNvx+PkeD%PuqV$Km{I2n_!Fn16bW#28riiB4niqMz_-d!<~cO4L=D z2qec}>wpHo{SA=|3|x7COB9scg#RoCbgF(9t%m&yN8u7xW>}5c?1Oe+t`Q>xBPd*t$;H|BA2~ zYOsGR>~60gw@#Mss}&QpUC;->@e0}lEO7!P8o9ae%W4MtCBqDfp z+4j{VZ6p4TZC^dow%2r?Osry)yOu)?_p#D_12i1sA>ii@%yy;GDB!R`4kIn*h?8JR zI7bYQPNkj+mD**5)C_1D@kz@*nl4L2xcdAw(Vt+3QSieD{N^I`{OJ1VaO*jG{?3cu zTlzb_B77MaK>$G)jkwVHAhbqY2q5U95f?figw}`)0R&w%;zH+x&>C?efS`*;T+WaiQ};XpOiKK+r`aE_6N!tq~Uj2)by*h0X_|HR3`5K^Kj<(D@*=MqCIW z=%Nu9Iv<4AhzkJ(T{PlC=Y!B1aUp=9i>SDU^!|+*mVy5+CxSmI@4#hsfIm26_`bmb z5R@_rg3?w&(3?*1`51!KLM)$fn&Y~A|Brqn@9GwmGbWoe~3M^>o0CJtvv z%e`E-k1wxR@PBOHbo@v6Fz3-OjvqE^;wZ1Uvrl?mCwP^$jH9mcxgX5V*_^c7c1cts z%f9C5USGLFA@itQ|3~YabQ2iUZaTLPTJvJ`kTtW+1GK^GV)OYIc%)|~vTV&L-{lWV zWToL#FBaYS$zMB?mNjI}S?$<#mtnafNyF@KsvmBHC#`daBf=tanF;oyvw|f{UXZUw zoo&s_@L#Yk^Rou`$0f2mUgQ&Vi__nlHh%d$;o*Ndo z3vLkaqgO?W);S-@xHOeO2omQzIh1KIdqz{7j@%%v`sE47*6d1SrDvzTlxi1E+j+Sz zAt9mT*6rIbwyxhUEPNv>y#2oYcA+TRBl+c*Hy*oOpXIgJ-QAtO<8nj!u3ZkNt72X+ zSg@dDvu8*8!k;Bl>6?u+&W*ScD_^?w-3+JgO^YBqntM>;?PbSLo_y;0_1o)pw{Cfv zV}f$n?Zw5#A7D3>OE81EQeASC$}|jJq^Up zwrfGrYvZQNTld%Dazmz{efsoi^a{~o8;_-bK8en6nlAEg8hj<@z|8jNr#m}FOg+E& z(LW}nzRtmM)~??y8uNv_sh}mRsi3*!;pPdRsT-${d%{Y6|GFs8R-Df_zZS8ktkpDj z-OMlN69uJ+%SKwNlZ)OsZ$-cHc>V{6*2jRkfmlnf{X|Yd2)aELWNT z@`MdCA&`U1Z~qb+K0KG9y-uq+DjJT(`kP&T{^A~O>t6nog=rNpGAkcVJSv^3Vc1Zg zk|C;nntho@)G*#ZFU}|Zb8YCwCD~TOz*|QhJd!u;8b9at>&e^8{eVwO&>*nOZ-P^s z+cc)LBSUPp* z&YjTe(x}~@%b(@gSLS$HUob8t# zv`A|?PVCy6VS~u`+!Yo_ii;C}FuQW7^zlRwh?@&M|-9pr4?_!06a7wrYoe=~n& zywS%b)^%sc{rmSdL)G!|_vA9!sY0u}ckW2ue0b(G^U&!^hcs)$tqcYiK88(zi?=4e zxT2Oo2&cX3pEnE|Zj0%T@kxt6N}gqby1T%$Nn2~ZC4*d6kz?g38(EbttttF^kU-z$ z4EZIrC<&RI$yVEoc56IG`czmtewXg7E!vd4+xD^D7EocJ&7}wXq~jdVgEcI`8Y_dF zjs`dJ9nZH1=e7OoMhbbD^Y-#~^Et;C2FB()X_8a;{D-u$T<1-PgEYyz(;N0>%-1CU zuC4{OJ*nsh2?r;pDURO>2aP;*a;7uIsp8vlz;W)q#uCTX$7IhuH$9w9RGvNhH0ED( zj%AeBUuWged`vX#RyyQ{0`b>2O>GP7CyojEK-2s*WYZ747J}cV^IXwR+6)pOahet%yEQ{r=sn=bfht3dqaNcT^>; zuN&%B=5jOf`Q3o91J#F~*PeKl6I=9Flpof*b*RQOuIP7pb*#GnS?!5mM{RzzccPq; z@|p9(yrWa6;IyR&i+&qfm04e3zj-CQT&leevLmqN?az-Y-XFJqkmH+3QsT4~IPEiM zaMm`uQYv@ao(GXzlD#gWx_+y2!y;4Wjrh#q8MC)mwUw{Dd#NaQ?kg)wEzYN0y#=;uy~V3Ze3O8D?eL7U&+`t%4gw-hIjityg4{<*55bfI-Ljt-xU%auN4(f zo#N0IMy_DtS7&h>BCsU|M~R9WplWx|Zm zCPXEc2Iq0sU*Li7BlQY~9|u=na24b`=EFKTLX7p#EU*8P;`Gl8ygQ*gDr1966TR}S z&1&k8No(fjs}F+jE~~yzbE7zIxqDGvdrb2%KDxZMlz!CdEe8!^=q2X+wqi}`H`b*A3Ub#hHMGs`P`M>y?(P~f7&K*9~VxF o>s>qRm8H0>!TJtkPVJlk<%mlq7ng1%L*U^zJ&^OG&*GK;1Af28t^fc4 literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index e2aff8f5..63bbb310 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -122,4 +122,24 @@ @string/p_dateformat_datetime + + @string/unit_metric + @string/unit_imperial + + + + @string/p_unit_metric + @string/p_unit_imperial + + + + @string/timeformat_24h + @string/timeformat_am_pm + + + + @string/p_timeformat_24h + @string/p_timeformat_am_pm + + \ 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 88226d4c..d72def7f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -142,6 +142,13 @@ Reconnection Attempts + //HPlus Preferences + Units + Time format + Screen on duration + All day heart rate measurement + HPlus/Makibes Settings + not connected connecting connected @@ -382,4 +389,11 @@ Make sure that this skin is enabled in the Weather Notification app to get weather information on your Pebble.\n\nNo configuration is needed here.\n\nYou can enable the system weather app of your Pebble from the app management.\n\nSupported watchfaces will show the weather automatically. Enable Bluetooth pairing Deactivate this if you have trouble connecting + + Metric + Imperial + + 24H + AM/PM + diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index df90cb5b..d8a8eff7 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -12,4 +12,10 @@ dateformat_time dateformat_datetime + metric + imperial + + 24h + am/pm + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 39c7d9ed..e9df02ac 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -388,6 +388,51 @@ android:title="Emulator Port" /> + + + + + + + + + + + + + + + + Date: Tue, 7 Feb 2017 10:15:23 +0100 Subject: [PATCH 208/244] made caller privacy pebble setting generic --- .../gadgetbridge/impl/GBDeviceService.java | 19 ++++- .../service/devices/pebble/PebbleSupport.java | 8 -- app/src/main/res/values/arrays.xml | 12 +++ app/src/main/res/values/strings.xml | 12 ++- app/src/main/res/values/values.xml | 3 + app/src/main/res/xml/preferences.xml | 85 +++++++++++-------- 6 files changed, 89 insertions(+), 50 deletions(-) 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 c8f83926..05bec9de 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -11,6 +11,8 @@ import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; @@ -85,7 +87,7 @@ public class GBDeviceService implements DeviceService { connect(device, false); } - @Override + @Override public void connect(@Nullable GBDevice device, boolean performPair) { Intent intent = createIntent().setAction(ACTION_CONNECT) .putExtra(GBDevice.EXTRA_DEVICE, device) @@ -149,9 +151,22 @@ public class GBDeviceService implements DeviceService { @Override public void onSetCallState(CallSpec callSpec) { + Context context = GBApplication.getContext(); + String currentPrivacyMode = GBApplication.getPrefs().getString("pref_call_privacy_mode", GBApplication.getContext().getString(R.string.p_call_privacy_mode_off)); + if (context.getString(R.string.p_call_privacy_mode_name).equals(currentPrivacyMode)) { + callSpec.name = callSpec.number; + } + else if (context.getString(R.string.p_call_privacy_mode_complete).equals(currentPrivacyMode)) { + callSpec.number = null; + callSpec.name = null; + } + else { + callSpec.name = coalesce(callSpec.name, getContactDisplayNameByNumber(callSpec.number)); + } + Intent intent = createIntent().setAction(ACTION_CALLSTATE) .putExtra(EXTRA_CALL_PHONENUMBER, callSpec.number) - .putExtra(EXTRA_CALL_DISPLAYNAME, coalesce(callSpec.name, getContactDisplayNameByNumber(callSpec.number))) + .putExtra(EXTRA_CALL_DISPLAYNAME, callSpec.name) .putExtra(EXTRA_CALL_COMMAND, callSpec.command); invokeService(intent); } 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 e4f59e85..d4afb684 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 @@ -131,14 +131,6 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { @Override public void onSetCallState(CallSpec callSpec) { - String currentPrivacyMode = GBApplication.getPrefs().getString("pebble_pref_privacy_mode", getContext().getString(R.string.p_pebble_privacy_mode_off)); - if (getContext().getString(R.string.p_pebble_privacy_mode_complete).equals(currentPrivacyMode)) { - callSpec.name = null; - callSpec.number = null; - } else if (getContext().getString(R.string.p_pebble_privacy_mode_content).equals(currentPrivacyMode)) { - callSpec.name = null; - } - if (reconnect()) { if ((callSpec.command != CallSpec.CALL_OUTGOING) || GBApplication.getPrefs().getBoolean("pebble_enable_outgoing_call", true)) { super.onSetCallState(callSpec); diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index da77e001..88685f63 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -155,4 +155,16 @@ @string/p_timeformat_am_pm + + @string/pref_call_privacy_mode_off + @string/pref_call_privacy_mode_name + @string/pref_call_privacy_mode_complete + + + + @string/p_call_privacy_mode_off + @string/p_call_privacy_mode_name + @string/p_call_privacy_mode_complete + + \ 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 fbfd13a9..d3661cbd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -88,6 +88,12 @@ when screen is off never + Privacy + Call Privacy Mode + Display name and number + Hide name but display number + Hide name and number + Blacklist Apps Canned Messages @@ -120,9 +126,9 @@ Notifications are automatically removed from the Pebble when dismissed from the Android device Privacy mode - Normal notifications and incoming calls display. - Shift the notification text off-screen. Hide the caller\'s name on incoming calls. - Show only the notification icon. Hide the caller\'s name and number on incoming calls. + Normal notifications + Shift the notification text off-screen + Show only the notification icon Location Acquire Location diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index ef1cd4a3..27745d3e 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -22,4 +22,7 @@ content complete + off + name + complete diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 044d5faa..d558fa4b 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -36,6 +36,44 @@ android:summaryOff="@string/pref_summary_minimize_priority_off" android:summaryOn="@string/pref_summary_minimize_priority_on" android:title="@string/pref_title_minimize_priority" /> + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + From 4f0674d038ac95aab2935b9b95022e8470c4dd36 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 7 Feb 2017 23:49:10 +0100 Subject: [PATCH 209/244] Support for alarm clock notifications for Mi1 + Mi2 #538 No support for Pebble and HPlus for now. Atm relies on the CM deskclock alarm, which nicely broadcasts events about the current alarm. See https://github.com/CyanogenMod/android_packages_apps_DeskClock.git --- .../devices/miband/MiBandConst.java | 1 + .../miband/MiBandPreferencesActivity.java | 2 + .../externalevents/AlarmClockReceiver.java | 67 +++++++++++++++++++ .../gadgetbridge/model/NotificationType.java | 4 +- .../service/DeviceCommunicationService.java | 15 ++++- .../service/devices/miband/MiBandSupport.java | 25 ++++++- .../devices/miband2/MiBand2Support.java | 38 +++++++---- .../actions/StopNotificationAction.java | 28 ++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/miband_preferences.xml | 30 +++++++++ 10 files changed, 194 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java 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 98877a83..b8e6f456 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 @@ -23,6 +23,7 @@ public final class MiBandConst { public static final String ORIGIN_INCOMING_CALL = "incoming_call"; + public static final String ORIGIN_ALARM_CLOCK = "alarm_clock"; public static final String MI_GENERAL_NAME_PREFIX = "MI"; public static final String MI_BAND2_NAME = "MI Band 2"; public static final String MI_1 = "1"; 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 caeb4e2a..129f4afe 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 @@ -17,6 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_ALARM_CLOCK; 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; @@ -146,6 +147,7 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { prefKeys.add(PREF_MIBAND_FITNESS_GOAL); prefKeys.add(PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR); prefKeys.add(PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS); + prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_ALARM_CLOCK)); prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL)); for (NotificationType type : NotificationType.values()) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java new file mode 100644 index 00000000..6753c5ed --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java @@ -0,0 +1,67 @@ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; + +public class AlarmClockReceiver extends BroadcastReceiver { + /** + * AlarmActivity and AlarmService (when unbound) listen for this broadcast intent + * so that other applications can snooze the alarm (after ALARM_ALERT_ACTION and before + * ALARM_DONE_ACTION). + */ + public static final String ALARM_SNOOZE_ACTION = "com.android.deskclock.ALARM_SNOOZE"; + + /** + * AlarmActivity and AlarmService listen for this broadcast intent so that other + * applications can dismiss the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION). + */ + public static final String ALARM_DISMISS_ACTION = "com.android.deskclock.ALARM_DISMISS"; + + /** A public action sent by AlarmService when the alarm has started. */ + public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT"; + + /** A public action sent by AlarmService when the alarm has stopped for any reason. */ + public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE"; + private int lastId; + + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (ALARM_ALERT_ACTION.equals(action)) { + sendAlarm(true); + } else if (ALARM_DONE_ACTION.equals(action)) { + sendAlarm(false); + } + } + + + + private synchronized void sendAlarm(boolean on) { + dismissLastAlarm(); + if (on) { + lastId = generateId(); + NotificationSpec spec = new NotificationSpec(); + spec.type = NotificationType.GENERIC_ALARM_CLOCK; + // can we get the alarm title somehow? + GBApplication.deviceService().onNotification(spec); + } + } + + private void dismissLastAlarm() { + if (lastId != 0) { + GBApplication.deviceService().onDeleteNotification(lastId); + lastId = 0; + } + } + + private int generateId() { + // lacks negative values, but should be sufficient + return (int) (Math.random() * Integer.MAX_VALUE); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java index daae8f4c..2f012825 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java @@ -17,7 +17,8 @@ public enum NotificationType { SIGNAL(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.BlueMoon), TWITTER(PebbleIconID.NOTIFICATION_TWITTER, PebbleColor.BlueMoon), TELEGRAM(PebbleIconID.NOTIFICATION_TELEGRAM, PebbleColor.PictonBlue), - WHATSAPP(PebbleIconID.NOTIFICATION_WHATSAPP, PebbleColor.MayGreen); + WHATSAPP(PebbleIconID.NOTIFICATION_WHATSAPP, PebbleColor.MayGreen), + GENERIC_ALARM_CLOCK(PebbleIconID.ALARM_CLOCK, PebbleColor.Red); public int icon; public byte color; @@ -41,6 +42,7 @@ public enum NotificationType { case GENERIC_EMAIL: case GENERIC_NAVIGATION: case GENERIC_SMS: + case GENERIC_ALARM_CLOCK: return getFixedValue(); case FACEBOOK: case TWITTER: 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 277e2223..019e245f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -27,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.OnboardingActivity; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.K9Receiver; @@ -152,8 +153,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private MusicPlaybackReceiver mMusicPlaybackReceiver = null; private TimeChangeReceiver mTimeChangeReceiver = null; private BluetoothConnectReceiver mBlueToothConnectReceiver = null; - private AlarmReceiver mAlarmReceiver = null; + private AlarmClockReceiver mAlarmClockReceiver = null; + private AlarmReceiver mAlarmReceiver = null; private Random mRandom = new Random(); /** @@ -619,6 +621,13 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mAlarmReceiver = new AlarmReceiver(); registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); } + if (mAlarmClockReceiver == null) { + mAlarmClockReceiver = new AlarmClockReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(AlarmClockReceiver.ALARM_ALERT_ACTION); + filter.addAction(AlarmClockReceiver.ALARM_DONE_ACTION); + registerReceiver(mAlarmClockReceiver, filter); + } } else { if (mPhoneCallReceiver != null) { unregisterReceiver(mPhoneCallReceiver); @@ -652,6 +661,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mAlarmReceiver); mAlarmReceiver = null; } + if (mAlarmClockReceiver != null) { + unregisterReceiver(mAlarmClockReceiver); + mAlarmClockReceiver = null; + } } } 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 be1e32fb..e5ef862a 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 @@ -50,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; @@ -102,6 +103,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); private RealtimeSamplesSupport realtimeSamplesSupport; + private boolean alarmClockRining; + private boolean alarmClockRinging; public MiBandSupport() { super(LOG); @@ -541,13 +544,29 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { + if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) { + onAlarmClock(notificationSpec); + return; + } + String origin = notificationSpec.type.getGenericType(); performPreferredNotification(origin + " received", origin, null); } + private void onAlarmClock(NotificationSpec notificationSpec) { + alarmClockRining = true; + AbortTransactionAction abortAction = new AbortTransactionAction() { + @Override + protected boolean shouldAbort() { + return !isAlarmClockRinging(); + } + }; + performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, abortAction); + } + @Override public void onDeleteNotification(int id) { - + alarmClockRining = false; // we should have the notificationtype at least to check } @Override @@ -616,6 +635,10 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { } + private boolean isAlarmClockRinging() { + // don't synchronize, this is not really important + return alarmClockRinging; + } private boolean isTelephoneRinging() { // don't synchronize, this is not really important return telephoneRinging; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index d068c2c5..09f7e6a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -73,6 +73,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.CheckAuthenti import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions.StopNotificationAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.FetchActivityOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation; @@ -123,6 +124,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); private RealtimeSamplesSupport realtimeSamplesSupport; + private boolean alarmClockRinging; public MiBand2Support() { super(LOG); @@ -586,6 +588,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { + if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) { + onAlarmClock(notificationSpec); + return; + } int alertLevel = MiBand2Service.ALERT_LEVEL_MESSAGE; if (notificationSpec.type == NotificationType.UNKNOWN) { alertLevel = MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY; @@ -594,9 +600,20 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { performPreferredNotification(origin + " received", origin, alertLevel, null); } + private void onAlarmClock(NotificationSpec notificationSpec) { + alarmClockRinging = true; + AbortTransactionAction abortAction = new StopNotificationAction(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL)) { + @Override + protected boolean shouldAbort() { + return !isAlarmClockRinging(); + } + }; + performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, abortAction); + } + @Override public void onDeleteNotification(int id) { - + alarmClockRinging = false; // we should have the notificationtype at least to check } @Override @@ -616,23 +633,11 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void onSetCallState(CallSpec callSpec) { if (callSpec.command == CallSpec.CALL_INCOMING) { telephoneRinging = true; - AbortTransactionAction abortAction = new AbortTransactionAction() { + AbortTransactionAction abortAction = new StopNotificationAction(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL)) { @Override protected boolean shouldAbort() { return !isTelephoneRinging(); } - - @Override - public boolean run(BluetoothGatt gatt) { - if (!super.run(gatt)) { - // send a signal to stop the vibration - BluetoothGattCharacteristic characteristic = MiBand2Support.this.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL); - characteristic.setValue(new byte[] {MiBand2Service.ALERT_LEVEL_NONE}); - gatt.writeCharacteristic(characteristic); - return false; - } - return true; - } }; performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, MiBand2Service.ALERT_LEVEL_PHONE_CALL, abortAction); } else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) { @@ -644,6 +649,11 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { } + private boolean isAlarmClockRinging() { + // don't synchronize, this is not really important + return alarmClockRinging; + } + private boolean isTelephoneRinging() { // don't synchronize, this is not really important return telephoneRinging; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java new file mode 100644 index 00000000..1de8ad6c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java @@ -0,0 +1,28 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; + +public abstract class StopNotificationAction extends AbortTransactionAction { + + private final BluetoothGattCharacteristic alertLevelCharacteristic; + + public StopNotificationAction(BluetoothGattCharacteristic alertLevelCharacteristic) { + this.alertLevelCharacteristic = alertLevelCharacteristic; + } + + @Override + public boolean run(BluetoothGatt gatt) { + if (!super.run(gatt)) { + // send a signal to stop the vibration + alertLevelCharacteristic.setValue(new byte[]{MiBand2Service.ALERT_LEVEL_NONE}); + gatt.writeCharacteristic(alertLevelCharacteristic); + return false; + } + return true; + } +}; + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3661cbd..86095992 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -406,5 +406,6 @@ 24H AM/PM + Alarm Clock diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index ed215a5a..65bb1759 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -210,6 +210,36 @@ android:title="@string/vibration_try"/> + + + + + + + + + + + Date: Thu, 9 Feb 2017 15:07:00 +0100 Subject: [PATCH 210/244] Pebble: fix alarm notifications only working one id -1 means undefined, everything else is liked to a real android notification --- .../gadgetbridge/externalevents/AlarmClockReceiver.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java index 6753c5ed..b01707c9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java @@ -48,6 +48,7 @@ public class AlarmClockReceiver extends BroadcastReceiver { lastId = generateId(); NotificationSpec spec = new NotificationSpec(); spec.type = NotificationType.GENERIC_ALARM_CLOCK; + spec.id = -1; // can we get the alarm title somehow? GBApplication.deviceService().onNotification(spec); } From 5bb1995eb9c176edf19fe289dba67d08da86bb95 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 9 Feb 2017 17:10:39 +0100 Subject: [PATCH 211/244] Pebble: fix privacy mode title being "null" in some cases --- .../service/devices/pebble/PebbleSupport.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 d4afb684..6140b24c 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 @@ -122,7 +122,13 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { notificationSpec.title = null; notificationSpec.phoneNumber = null; } else if (getContext().getString(R.string.p_pebble_privacy_mode_content).equals(currentPrivacyMode)) { - notificationSpec.sender = "\n\n\n\n\n" + notificationSpec.sender; + if (notificationSpec.sender != null) {if (notificationSpec.sender != null) { + notificationSpec.sender = "\n\n\n\n\n" + notificationSpec.sender; + } else if (notificationSpec.title != null) { + notificationSpec.title = "\n\n\n\n\n" + notificationSpec.title; + } else if (notificationSpec.subject != null) { + notificationSpec.subject = "\n\n\n\n\n" + notificationSpec.subject; + } } if (reconnect()) { super.onNotification(notificationSpec); From 2b7162055dfd00e482117ecbfc27028e0a300be3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 9 Feb 2017 17:18:33 +0100 Subject: [PATCH 212/244] bump version, add changelog --- CHANGELOG.md | 9 +++++++-- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 9 +++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91237f34..8a3ce44c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ ###Changelog -####Version next +####Version 0.17.4 (next) * Better integration with android music players -* Pebble: Implement notification and incoming call privacy modes +* Privacy options for calls (hide caller name/number) +* Pebble: Implement notification privacy modes * Pebble: Support weather for Obisdian watchface +* HPlus: Support alarms +* HPlus: Fix time and date sync and time format (12/24) +* HPlus: Add device specific preferences and icon +* HPlus: Support for Makibes F68 ####Version 0.17.3 * HPlus: Improve display of new messages and phone calls diff --git a/app/build.gradle b/app/build.gradle index a86bb12b..985f5ef3 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.17.3" - versionCode 84 + versionName "0.17.4" + versionCode 85 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index abf9e99f..b4d46f05 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,9 +1,14 @@ - + Better integration with android music players - Pebble: Implement notification and incoming call privacy modes + Privacy options for calls (hide caller name/number) + Pebble: Implement notification privacy modes Pebble: Support weather for Obisdian watchface + HPlus: Support alarms + HPlus: Fix time and date sync and time format (12/24) + HPlus: Add device specific preferences and icon + HPlus: Support for Makibes F68 HPlus: Improve display of new messages and phone calls From 083b8db1ecdf087b7a05e409e1b965037544dcb5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 9 Feb 2017 17:20:29 +0100 Subject: [PATCH 213/244] update Japanese and Spanish from Transifex (THANKS!) --- app/src/main/res/values-es/strings.xml | 3 +++ app/src/main/res/values-ja/strings.xml | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index a6de97f2..85061210 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -96,6 +96,7 @@ Enviar las horas de salida y puesta de Sol basándose en la localización a la línea cronológica del Pebble Eliminar automáticamente las notificaciones rechazadas Las notificaciones se borran automáticamente de Pebble cuando son rechazadas en Android + Modo privado Localización Buscar localización Latitud @@ -338,4 +339,6 @@ Por favor, ten en cuenta que puedes importar datos desde Mi Band, Pebble Health Emparejando con Pebble En su dispositivo Android va a aparecer un mensaje para emparejarse. Si no apareciera, mira en el cajón de notificaciones y acepta la propuesta de emparejamiento. Después acepta también en tu Pebble. Asegúrate de que este tema esté activado en la aplicación de notificación del tiempo para obtener la información en tu Pebble.\n\nNo se requiere configuración.\n\nPuedes activar la aplicación del tiempo del sistema desde la configuración de la app.\n\nLas watchfaces soportadas mostrarán la información del tiempo automáticamente. + Activar el emparejamiento Bluetooth + Desactiva esto si tienes problemas de conexión diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6071e09e..a4925204 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -70,10 +70,15 @@ サイレント サイレントモードに基づいて、送信される不要な通知を停止します。 音訳 - デバイスでお使いの言語のフォントがサポートされていない場合に、使用してください (現在はキリル文字のみ) + デバイスでお使いの言語のフォントがサポートされていない場合に、これを有効にしてください (現在はキリル文字のみ) いつも スクリーンがオフのとき なし + プライバシー + 通話プライバシーモード + 名前と番号を表示 + 名前は非表示、番号は表示 + 名前と番号を非表示 アップのブラックリスト 定型のメッセージ 返信 @@ -96,6 +101,10 @@ 場所に基づいて、Pebble のタイムラインに日の出・日の入りの時間を送ります 消去した通知を自動削除 通知は、Androidデバイスから削除されたときに、Pebbleから自動的に削除されます + プライバシーモード + 通常の通知 + 通知テキストを画面外にシフト + 通知アイコンのみを表示 場所 場所の取得 緯度 @@ -115,6 +124,11 @@ ウォッチアプリのログ記録を有効にする Gadgetbridgeがウォッチアプリからログを記録するようにする (再接続が必要です) 再接続の試行 + 単位 + 時刻形式 + 画面オンの期間 + 一日中心拍数測定 + HPlus/Makibes 設定 非接続 接続中 接続 @@ -338,4 +352,11 @@ Mi Band、Pebble Health、Morpheuz からデータをインポートすること Pebbleペアリング お使いのAndroidデバイスでペアリングのダイアログがポップアップすると思います。 起こらない場合は、通知ドロワーを調べて、ペアリング要求を受け入れます。 その後、Pebbleでペアリング要求を受け入れます 天気予報アプリでこのスキンが有効になっていることを確認してください。\n\nここで必要な設定はありません。\n\nアプリ管理からPebbleのシステム天気アプリを有効にすることができます。\n\nサポートされているウォッチフェイスが自動的に天気を表示します。 + Bluetoothペアリングを有効にする + 接続に問題がある場合は、この機能を無効にしてください + メートル + ヤード・ポンド + 24時間 + AM/PM + アラームクロック From c0076b20d3f369376dca364b1499edbc0380a0e6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 9 Feb 2017 17:23:41 +0100 Subject: [PATCH 214/244] fix copy and pasta error --- .../gadgetbridge/service/devices/pebble/PebbleSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6140b24c..426b6512 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 @@ -122,7 +122,7 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { notificationSpec.title = null; notificationSpec.phoneNumber = null; } else if (getContext().getString(R.string.p_pebble_privacy_mode_content).equals(currentPrivacyMode)) { - if (notificationSpec.sender != null) {if (notificationSpec.sender != null) { + if (notificationSpec.sender != null) { notificationSpec.sender = "\n\n\n\n\n" + notificationSpec.sender; } else if (notificationSpec.title != null) { notificationSpec.title = "\n\n\n\n\n" + notificationSpec.title; From f35e3e460d44c4cb8dd44969a79ddd9eccfba316 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 9 Feb 2017 17:35:46 +0100 Subject: [PATCH 215/244] remove K9 receiver as is works better with generic notifications --- app/src/main/AndroidManifest.xml | 1 - .../activities/ControlCenter.java | 2 - .../externalevents/K9Receiver.java | 87 ------------------- .../externalevents/NotificationListener.java | 6 -- .../service/DeviceCommunicationService.java | 13 --- .../devices/pebble/PebbleProtocol.java | 2 +- app/src/main/res/values/strings.xml | 1 - app/src/main/res/xml/preferences.xml | 8 -- 8 files changed, 1 insertion(+), 119 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ceb2ac0b..bdbc7878 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,6 @@ - diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 35f0009e..1a275583 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -425,8 +425,6 @@ public class ControlCenter extends GBActivity { wantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE); if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED) wantedPermissions.add(Manifest.permission.READ_CALENDAR); - if (ContextCompat.checkSelfPermission(this, "com.fsck.k9.permission.READ_MESSAGES") == PackageManager.PERMISSION_DENIED) - wantedPermissions.add("com.fsck.k9.permission.READ_MESSAGES"); if (!wantedPermissions.isEmpty()) ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java deleted file mode 100644 index c9431b43..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java +++ /dev/null @@ -1,87 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.externalevents; - -import android.app.NotificationManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.PowerManager; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; -import nodomain.freeyourgadget.gadgetbridge.util.Prefs; - -public class K9Receiver extends BroadcastReceiver { - - private static final Logger LOG = LoggerFactory.getLogger(K9Receiver.class); - private final Uri k9Uri = Uri.parse("content://com.fsck.k9.messageprovider/inbox_messages"); - - @Override - public void onReceive(Context context, Intent intent) { - - Prefs prefs = GBApplication.getPrefs(); - if ("never".equals(prefs.getString("notification_mode_k9mail", "when_screen_off"))) { - return; - } - if ("when_screen_off".equals(prefs.getString("notification_mode_k9mail", "when_screen_off"))) { - PowerManager powermanager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - if (powermanager.isScreenOn()) { - return; - } - } - switch (GBApplication.getGrantedInterruptionFilter()) { - case NotificationManager.INTERRUPTION_FILTER_ALL: - break; - case NotificationManager.INTERRUPTION_FILTER_ALARMS: - case NotificationManager.INTERRUPTION_FILTER_NONE: - case NotificationManager.INTERRUPTION_FILTER_PRIORITY: - return; - } - - String uriWanted = intent.getData().toString(); - - String[] messagesProjection = { - "senderAddress", - "subject", - "preview", - "uri" - }; - - NotificationSpec notificationSpec = new NotificationSpec(); - notificationSpec.id = -1; - notificationSpec.type = NotificationType.GENERIC_EMAIL; - - /* - * there seems to be no way to specify the uri in the where clause. - * If we do so, we just get the newest message, not the one requested. - * So, we will just search our message and match the uri manually. - * It should be the first one returned by the query in most cases, - */ - - try (Cursor c = context.getContentResolver().query(k9Uri, messagesProjection, null, null, null)) { - if (c != null) { - while (c.moveToNext()) { - String uri = c.getString(c.getColumnIndex("uri")); - if (uri.equals(uriWanted)) { - notificationSpec.sender = c.getString(c.getColumnIndex("senderAddress")); - notificationSpec.subject = c.getString(c.getColumnIndex("subject")); - notificationSpec.body = c.getString(c.getColumnIndex("preview")); - break; - } - } - } - } catch (Exception e) { - e.printStackTrace(); - notificationSpec.sender = "Gadgetbridge"; - notificationSpec.subject = "Permission Error?"; - notificationSpec.body = "Please reinstall Gadgetbridge to enable K-9 Mail notifications"; - } - - GBApplication.deviceService().onNotification(notificationSpec); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index e602dfc0..fc6ecfc6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -214,12 +214,6 @@ public class NotificationListener extends NotificationListenerService { return; } - if (source.equals("com.fsck.k9")) { - if (!"never".equals(prefs.getString("notification_mode_k9mail", "when_screen_off"))) { - return; - } - } - if (source.equals("com.moez.QKSMS") || source.equals("com.android.mms") || source.equals("com.sonyericsson.conversations") || 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 019e245f..5e5315c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -30,7 +30,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; -import nodomain.freeyourgadget.gadgetbridge.externalevents.K9Receiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.MusicPlaybackReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.PebbleReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.PhoneCallReceiver; @@ -148,7 +147,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private PhoneCallReceiver mPhoneCallReceiver = null; private SMSReceiver mSMSReceiver = null; - private K9Receiver mK9Receiver = null; private PebbleReceiver mPebbleReceiver = null; private MusicPlaybackReceiver mMusicPlaybackReceiver = null; private TimeChangeReceiver mTimeChangeReceiver = null; @@ -586,13 +584,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mSMSReceiver = new SMSReceiver(); registerReceiver(mSMSReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED")); } - if (mK9Receiver == null) { - mK9Receiver = new K9Receiver(); - IntentFilter filter = new IntentFilter(); - filter.addDataScheme("email"); - filter.addAction("com.fsck.k9.intent.action.EMAIL_RECEIVED"); - registerReceiver(mK9Receiver, filter); - } if (mPebbleReceiver == null) { mPebbleReceiver = new PebbleReceiver(); registerReceiver(mPebbleReceiver, new IntentFilter("com.getpebble.action.SEND_NOTIFICATION")); @@ -637,10 +628,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mSMSReceiver); mSMSReceiver = null; } - if (mK9Receiver != null) { - unregisterReceiver(mK9Receiver); - mK9Receiver = null; - } if (mPebbleReceiver != null) { unregisterReceiver(mPebbleReceiver); mPebbleReceiver = null; 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 4527299a..772167a2 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 @@ -457,7 +457,7 @@ public class PebbleProtocol extends GBDeviceProtocol { String title; String subtitle = null; - // for SMS and EMAIL that came in though SMS or K9 receiver + // for SMS that came in though the SMS receiver if (notificationSpec.sender != null) { title = notificationSpec.sender; subtitle = notificationSpec.subject; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 86095992..77484bac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -74,7 +74,6 @@ Repetitions Phone Calls SMS - K9-Mail Pebble Messages Support for applications which send Notifications to the Pebble via PebbleKit. Generic notification support diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d558fa4b..b44383c6 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -104,14 +104,6 @@ android:title="@string/pref_title_notifications_sms" android:summary="%s" /> - - Date: Fri, 10 Feb 2017 00:32:03 +0100 Subject: [PATCH 216/244] Implement hashCode() when you implement equals()! --- .../gadgetbridge/model/MusicStateSpec.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java index f9d070eb..c960b149 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java @@ -43,4 +43,15 @@ public class MusicStateSpec { this.shuffle == stateSpec.shuffle && this.repeat == stateSpec.repeat; } + + @Override + public int hashCode() { + int result = (int) state; +// ignore the position -- it is taken into account in equals() +// result = 31 * result + position; + result = 31 * result + playRate; + result = 31 * result + (int) shuffle; + result = 31 * result + (int) repeat; + return result; + } } From 0042ffc5140a4b46bcf95792f803c6b590920a89 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 10 Feb 2017 22:52:55 +0100 Subject: [PATCH 217/244] Set the notification ID on alarm start --- .../gadgetbridge/externalevents/AlarmClockReceiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java index b01707c9..96a07295 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java @@ -48,7 +48,7 @@ public class AlarmClockReceiver extends BroadcastReceiver { lastId = generateId(); NotificationSpec spec = new NotificationSpec(); spec.type = NotificationType.GENERIC_ALARM_CLOCK; - spec.id = -1; + spec.id = lastId; // can we get the alarm title somehow? GBApplication.deviceService().onNotification(spec); } From ee28ccd4fe37bc8190a0f0ac978999a60cbe022e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 10 Feb 2017 23:06:34 +0100 Subject: [PATCH 218/244] Pebble: add a dev option to always and immediately ACK PebbleKit messages to the watch Might help #509 --- .../devices/pebble/PebbleIoThread.java | 1 + .../devices/pebble/PebbleKitSupport.java | 10 ++++++---- .../devices/pebble/PebbleProtocol.java | 19 +++++++++++++------ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/preferences.xml | 5 +++++ 5 files changed, 27 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 f4e6e31e..35c1c256 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 @@ -85,6 +85,7 @@ class PebbleIoThread extends GBDeviceIoThread { mBtAdapter = btAdapter; mPebbleSupport = pebbleSupport; mEnablePebblekit = prefs.getBoolean("pebble_enable_pebblekit", false); + mPebbleProtocol.setAlwaysACKPebbleKit(prefs.getBoolean("pebble_always_ack_pebblekit", false)); } private int readWithException(InputStream inputStream, byte[] buffer, int byteOffset, int byteCount) throws IOException { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index 6d05dc8c..2277d1ca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -64,10 +64,12 @@ class PebbleKitSupport { break; case PEBBLEKIT_ACTION_APP_ACK: transaction_id = intent.getIntExtra("transaction_id", -1); - if (transaction_id >= 0 && transaction_id <= 255) { - mPebbleIoThread.write(mPebbleProtocol.encodeApplicationMessageAck(null, (byte) transaction_id)); - } else { - LOG.warn("illegal transaction id " + transaction_id); + if (!mPebbleProtocol.mAlwaysACKPebbleKit) { + if (transaction_id >= 0 && transaction_id <= 255) { + mPebbleIoThread.write(mPebbleProtocol.encodeApplicationMessageAck(null, (byte) transaction_id)); + } else { + LOG.warn("illegal transaction id " + transaction_id); + } } break; 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 772167a2..6d1e93c1 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 @@ -253,6 +253,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final Random mRandom = new Random(); int mFwMajor = 3; + boolean mAlwaysACKPebbleKit = false; private boolean mForceProtocol = false; private GBDeviceEventScreenshot mDevEventScreenshot = null; private int mScreenshotRemaining = -1; @@ -1833,16 +1834,17 @@ public class PebbleProtocol extends GBDeviceProtocol { jsonArray.put(jsonObject); } - // this is a hack we send an ack to the Pebble immediately because we cannot map the transaction_id from the intent back to a uuid yet - /* - GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); - sendBytesAck.encodedBytes = encodeApplicationMessageAck(uuid, last_id); - */ + GBDeviceEventSendBytes sendBytesAck = null; + if (mAlwaysACKPebbleKit) { + // this is a hack we send an ack to the Pebble immediately because somebody said it helps some PebbleKit apps :P + sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = encodeApplicationMessageAck(uuid, last_id); + } GBDeviceEventAppMessage appMessage = new GBDeviceEventAppMessage(); appMessage.appUUID = uuid; appMessage.id = last_id & 0xff; appMessage.message = jsonArray.toString(); - return new GBDeviceEvent[]{appMessage}; + return new GBDeviceEvent[]{appMessage, sendBytesAck}; } byte[] encodeApplicationMessagePush(short endpoint, UUID uuid, ArrayList> pairs) { @@ -2584,6 +2586,11 @@ public class PebbleProtocol extends GBDeviceProtocol { mForceProtocol = force; } + void setAlwaysACKPebbleKit(boolean alwaysACKPebbleKit) { + LOG.info("setting always ACK Pebbleit to " + alwaysACKPebbleKit); + mAlwaysACKPebbleKit = alwaysACKPebbleKit; + } + private String getFixedString(ByteBuffer buf, int length) { byte[] tmp = new byte[length]; buf.get(tmp, 0, length); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 77484bac..3bed06a3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -149,6 +149,8 @@ If your Pebble 2/Pebble LE does not work as expected, try this setting to limit the MTU (valid range 20–512) Enable Watch App Logging Will cause logs from watch apps to be logged by Gadgetbridge (requires reconnect) + Prematurely ACK PebbleKit + Will cause messages that are sent to external 3rd party apps to be acknowledged always and immediately Reconnection Attempts diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index b44383c6..d324fa3a 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -386,6 +386,11 @@ android:key="pebble_enable_applogs" android:summary="@string/pref_summary_pebble_enable_applogs" android:title="@string/pref_title_pebble_enable_applogs" /> + Date: Fri, 10 Feb 2017 23:11:21 +0100 Subject: [PATCH 219/244] Pebble: make sure to not display "open on phone" and "dismiss" when the source of a notification was our AlarmClockReceiver --- .../gadgetbridge/externalevents/AlarmClockReceiver.java | 1 + .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java index 96a07295..aa102be8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java @@ -49,6 +49,7 @@ public class AlarmClockReceiver extends BroadcastReceiver { NotificationSpec spec = new NotificationSpec(); spec.type = NotificationType.GENERIC_ALARM_CLOCK; spec.id = lastId; + spec.sourceName = "ALARMCLOCKRECEIVER"; // can we get the alarm title somehow? GBApplication.deviceService().onNotification(spec); } 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 6d1e93c1..acc58cf0 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 @@ -574,7 +574,7 @@ public class PebbleProtocol extends GBDeviceProtocol { byte dismiss_action_id; - if (hasHandle) { + if (hasHandle && !"ALARMCLOCKRECEIVER".equals(sourceName)) { actions_count = 3; dismiss_string = "Dismiss"; dismiss_action_id = 0x02; From 5dfd40062fab68b7a390e9f8970e73bf139f97cb Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 10 Feb 2017 23:16:22 +0100 Subject: [PATCH 220/244] fix previous commit --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 acc58cf0..f3ae7825 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 @@ -654,7 +654,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.put(dismiss_string.getBytes()); // open and mute actions - if (hasHandle) { + if (hasHandle && !"ALARMCLOCKRECEIVER".equals(sourceName)) { buf.put((byte) 0x01); buf.put((byte) 0x02); // generic buf.put((byte) 0x01); // number attributes @@ -892,7 +892,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } byte dismiss_action_id; - if (hasHandle) { + if (hasHandle && !"ALARMCLOCKRECEIVER".equals(sourceName)) { actions_count = 3; dismiss_string = "Dismiss"; dismiss_action_id = 0x02; @@ -979,7 +979,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.put(dismiss_string.getBytes()); // open and mute actions - if (hasHandle) { + if (hasHandle && !"ALARMCLOCKRECEIVER".equals(sourceName)) { buf.put((byte) 0x01); buf.put((byte) 0x02); // generic action buf.put((byte) 0x01); // number attributes From fea31924ba0148e72fcccf698fd29ed5e1d49e4a Mon Sep 17 00:00:00 2001 From: Avamander Date: Sat, 11 Feb 2017 10:49:01 +0200 Subject: [PATCH 221/244] Music data handling improvements (#550) * Fixed extracting the track length. * Added current track and total track count. * Few small changes to make sure everything gets updated properly. * Remove unnecessary includes. --- .../externalevents/MusicPlaybackReceiver.java | 28 +++++++++++-------- .../service/DeviceCommunicationService.java | 16 ++++++++--- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index 9d6ee214..6a84e42c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -15,7 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; public class MusicPlaybackReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(MusicPlaybackReceiver.class); private static MusicSpec lastMusicSpec = new MusicSpec(); - private static MusicStateSpec lastStatecSpec = new MusicStateSpec(); + private static MusicStateSpec lastStateSpec = new MusicStateSpec(); @Override public void onReceive(Context context, Intent intent) { @@ -28,7 +28,7 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { } */ MusicSpec musicSpec = new MusicSpec(lastMusicSpec); - MusicStateSpec stateSpec = new MusicStateSpec(lastStatecSpec); + MusicStateSpec stateSpec = new MusicStateSpec(lastStateSpec); Bundle incomingBundle = intent.getExtras(); for (String key : incomingBundle.keySet()) { @@ -51,6 +51,14 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { stateSpec.position = ((Long) incoming).intValue() / 1000; } else if (incoming instanceof Boolean && "playing".equals(key)) { stateSpec.state = (byte) (((Boolean) incoming) ? MusicStateSpec.STATE_PLAYING : MusicStateSpec.STATE_PAUSED); + } else if (incoming instanceof String && "duration".equals(key)) { + musicSpec.duration = Integer.valueOf((String) incoming) / 1000; + } else if (incoming instanceof String && "trackno".equals(key)) { + musicSpec.trackNr = Integer.valueOf((String) incoming); + } else if (incoming instanceof String && "totaltrack".equals(key)) { + musicSpec.trackCount = Integer.valueOf((String) incoming); + } else if (incoming instanceof Integer && "pos".equals(key)) { + stateSpec.position = (Integer) incoming; } } @@ -59,17 +67,15 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { LOG.info("Update Music Info: " + musicSpec.artist + " / " + musicSpec.album + " / " + musicSpec.track); GBApplication.deviceService().onSetMusicInfo(musicSpec); } else { - LOG.info("got metadata changed intent, but nothing changed, ignoring."); + LOG.info("Got metadata changed intent, but nothing changed, ignoring."); } - if (intent.hasExtra("position") && intent.hasExtra("playing")) { - if (!lastStatecSpec.equals(stateSpec)) { - LOG.info("Update Music State: state=" + stateSpec.state + ", position= " + stateSpec.position); - GBApplication.deviceService().onSetMusicState(stateSpec); - } else { - LOG.info("got state changed intent, but not enough has changed, ignoring."); - } - lastStatecSpec = stateSpec; + if (!lastStateSpec.equals(stateSpec)) { + lastStateSpec = stateSpec; + LOG.info("Update Music State: state=" + stateSpec.state + ", position= " + stateSpec.position); + GBApplication.deviceService().onSetMusicState(stateSpec); + } else { + LOG.info("Got state changed intent, but not enough has changed, ignoring."); } } } 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 5e5315c7..04206e8d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -156,6 +156,15 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private AlarmReceiver mAlarmReceiver = null; private Random mRandom = new Random(); + private final String[] mMusicActions = { + "com.android.music.metachanged", + "com.android.music.playstatechanged", + "com.android.music.queuechanged", + "com.android.music.playbackcomplete", + "net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED", + "com.maxmpz.audioplayer.TPOS_SYNC", + "com.maxmpz.audioplayer.STATUS_CHANGED",}; + /** * For testing! * @@ -591,10 +600,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere if (mMusicPlaybackReceiver == null) { mMusicPlaybackReceiver = new MusicPlaybackReceiver(); IntentFilter filter = new IntentFilter(); - filter.addAction("com.android.music.metachanged"); - filter.addAction("com.android.music.playstatechanged"); - filter.addAction("com.android.music.playbackcomplete"); - filter.addAction("net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED"); + for (String action : mMusicActions){ + filter.addAction(action); + } registerReceiver(mMusicPlaybackReceiver, filter); } if (mTimeChangeReceiver == null) { From 3936a7d8a0f6b6cf9ef96ac8ffddaccaebac513a Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 11 Feb 2017 22:34:04 +0100 Subject: [PATCH 222/244] update all languages from transifex (THANKS) French changes merged by a PR are overwritten though this. We didn't have a solution from that - so I will just rely on transifex - sorry. --- app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 19 ++++- app/src/main/res/values-fr/strings.xml | 103 ++++++++++++++++--------- app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 2 - app/src/main/res/values-ja/strings.xml | 3 +- app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - 10 files changed, 88 insertions(+), 45 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 846d85b2..3a714eff 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -56,7 +56,6 @@ Wiederholungen Anrufe SMS - K9-Mail Pebble Nachrichten Unterstützung für Anwendungen die Benachrichtigungen an die Pebble via PebbleKit senden. Andere Benachrichtigungen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 85061210..8ee6275d 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -62,7 +62,6 @@ Repeticiones Llamadas telefónicas SMS - K9-Mail Mensajes de Pebble Soporte para aplicaciones que envían notificaciones a Pebble a través de PebbleKit. Soporte para notificaciones genéricas @@ -74,6 +73,11 @@ siempre cuando la pantalla está apagada nunca + Privacidad + Modo de privacidad de llamada + Mostrar nombre y número + Ocultar nombre pero mostrar número + Ocultar nombre y número Excluir aplicaciones Mensajes predeterminados Respuestas @@ -97,6 +101,9 @@ Eliminar automáticamente las notificaciones rechazadas Las notificaciones se borran automáticamente de Pebble cuando son rechazadas en Android Modo privado + Notificación normal + Desplaza la notificación fuera de la pantalla + Muestra solo el icono de notificación Localización Buscar localización Latitud @@ -116,6 +123,11 @@ Activar crear registros de la App del Reloj Producirá registros de las apps del reloj que Gadgetbridge guardará (necesita reconexión) Intentos de reconexión + Unidades + Formato de hora + Tiempo de pantalla encendida + Medición del ritmo cardíaco todo el día + Ajustes de HPlus/Makibes no conectado conectando conectado @@ -341,4 +353,9 @@ Por favor, ten en cuenta que puedes importar datos desde Mi Band, Pebble Health Asegúrate de que este tema esté activado en la aplicación de notificación del tiempo para obtener la información en tu Pebble.\n\nNo se requiere configuración.\n\nPuedes activar la aplicación del tiempo del sistema desde la configuración de la app.\n\nLas watchfaces soportadas mostrarán la información del tiempo automáticamente. Activar el emparejamiento Bluetooth Desactiva esto si tienes problemas de conexión + Métrica + Imperial + 24H + AM/PM + Despertador diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 46d61389..411ee160 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -27,6 +27,9 @@ Désactiver Activer la mesure du rythme cardiaque Désactiver la mesure du rythme cardiaque + Activer l\'application météo du système + Désactiver l\'application météo du système + Installer l\'application de notification de la météo Configurer Haut de page @@ -37,7 +40,7 @@ Vous êtes sur le point d\'installer les micrologiciels %1$s et %2$s à la place de ceux qui sont actuellement sur votre Mi Band. Ce micrologiciel a été testé et est connu pour être compatible avec Gadgetbridge. Ce micrologiciel n\'a pas été testé et peut ne pas être compatible avec Gadgetbridge.\n\nIl n\'est pas conseillé de le flasher sur votre Mi Band. - Si vous désirez continuer et que tout fonctionne correctement par la suite, SVP informez-en les développeurs de Gadgetbridge pour demander l\'ajout de ce micrologiciel à leur liste: %s + Si vous désirez continuer et que tout fonctionne correctement par la suite, veuillez en informer les développeurs de Gadgetbridge pour demander l\'ajout de ce micrologiciel à leur liste: %s Paramètres Paramètres généraux @@ -52,20 +55,29 @@ Clair Sombre Langue + Masquer la notification de Gadgetbridge + L\'icône de la barre d\'état et la notification de l\'écran de verrouillage sont affichées + L\'icône de la barre d\'état et la notification de l\'écran de verrouillage sont masquées Notifications Répétitions Appels téléphoniques Textos - K9-Mail Messages Pebble Support des applications qui envoient des notification à Pebble par PebbleKit. Support des notifications génériques ... y compris quand l\'écran est allumé Ne Pas Déranger Arrêter l’envoi des notifications non-désirées en mode Ne Pas Déranger. + Transcription + À utiliser si l\'appareil ne supporte pas la police de caractères (pour l\'instant seulement le Cyrillique) toujours quand l\'écran est éteint jamais + Confidentialité + Mode de confidentialité d\'appel + Afficher le nom et le numéro + Masquer le nom mais afficher le numéro + Masquer le nom et le numéro Applications bloquées Modèles de messages Réponses @@ -80,10 +92,18 @@ Synchroniser Pebble Health Synchroniser Misfit Synchroniser Morpheuz + Support des appels sortants + Désactiver ceci empêchera la Pebble 2/LE de vibrer lors des appels sortants Permettre l\'accès aux applications tierces Android Activer le support expérimental pour les applications Android utilisant PebbleKit Lever et coucher de soleil Envoyer les heures de lever et coucher du soleil dans l’historique Pebble en fonction de l\'emplacement + Effacer automatiquement les notifications rejetées + Les notifications sont automatiquement effacées de la Pebble lorsqu\'elles sont rejetées sur l\'appareil Android + Mode privé + Notifications normales + Déplace les notifications hors de l\'écran + Ne montre que l\'icône de notification Emplacement Obtenir l\'emplacement Latitude @@ -97,12 +117,17 @@ Activer les fonctionnalités non-testées Activer les fonctionnalités non-testées. ACTIVEZ UNIQUEMENT SI VOUS SAVEZ CE QUE VOUS FAITES! Toujours préférer le BLE - Utiliser le support expérimental du LE pour toutes les Pebble au lieu du Bluetooth classique ; cela requiert l’appairage d\'une \"Pebble LE\" après qu’une non-LE ai déjà été connectée + Utiliser le support expérimental du LE pour toutes les Pebble au lieu du Bluetooth classique ; cela requiert l’appairage d\'une \"Pebble LE\" après qu’une non-LE ait déjà été connectée Limite du GATT MTU de Pebble 2/LE Si votre Pebble 2/LE ne fonctionne pas correctement, essayez d\'activer cette option pour limiter le MTU (plage valide 20-512) Activer les logs des Watch App Ceci permettra à Gadgetbridge de conserver les logs des Watch App (requiert une reconnexion) Tentatives de reconnexion + Unités + Format de l\'heure + Durée d\'écran allumé + Mesure de la fréquence cardiaque toute la journée + Paramètres HPlus/Makibes non connecté connexion en cours connecté @@ -118,13 +143,13 @@ Cliquez sur l\'appareil pour ouvrir le gestionnaire d\'application Cliquez sur l\'appareil pour ouvrir le gestionnaire d’activité Cliquez sur connecter pour envoyer une vibration - Tapotter sur le périphérique pour le connecter. + Tapottez sur le périphérique pour le connecter. Connexion impossible. L’adresse Bluetooth est-elle valide? Gadgetbridge est en fonctionnement Installation du fichier %1$d/%2$d échec de l\'installation ! Installation réalisée avec succès - VOUS TENTEZ D\'INSTALLER UN MICROLOGICIEL, PROCÉDEZ À VOS PROPRES RISQUES.\n\n\nCe micrologiciel est pour la version de matériel: %s + VOUS TENTEZ D\'INSTALLER UN MICROLOGICIEL, PROCÉDEZ À VOS RISQUES ET PÉRILS.\n\n\nCe micrologiciel est pour la version de matériel: %s Vous êtes sur le point d\'installer l\'application suivante:\n\n\n%1$s Version %2$s par %3$s\n N.D. Initialisé @@ -236,13 +261,13 @@ 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. - Utilisez le mode low-latency pour les mises à jour FW - Cela peu aider sur les appareils où les mises à jour du firmware échouent + Utilisez le mode basse latence pour les mises à jour du micrologiciel + Cela peut aider sur les appareils où les mises à jour du micrologiciel échouent Historique de pas Pas/minute actuel Nombre total de pas Historique de pas/minute - Démarrer votre activité + Démarrez votre activité Activité Sommeil léger Sommeil profond @@ -253,15 +278,15 @@ Micrologiciel non compatible Ce micrologiciel n\'est pas compatible avec l\'appareil 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) + Utiliser le capteur cardiaque pour améliorer la détection du sommeil + La compensation de temps en heure (pour travailleurs en rotation, par exemple) Mi2 : Format de la date Heure Allumer l\'écran lors d\'un mouvement Sur le point de transférer des données depuis %1$s en attente de reconnexion - A propos de vous + À propos de vous Année de naissance Genre Taille en cm @@ -276,50 +301,50 @@ Micrologiciel: %1$s Erreur à la création de votre fichier log : %1$s HR: - Le firmware se met à jour + Le micrologiciel se met à jour Échec lors de l\'écriture du micrologiciel Rythme cardiaque Rythme cardiaque Importer la base de donnée - Importer des données d\'activité ancienne + Importer des données d\'activité anciennes Depuis Gadgetbridge 0.12.0, nous utilisons un nouveau format de base de données. -Vous êtes en mesure d\'importer des données d\'activité ancienne et l\'associer à l\'appareil que vous vous connectez à (%1$s).\n +Vous êtes en mesure d\'importer des données d\'activité anciennes et de les associer à l\'appareil auquel vous vous connectez (%1$s).\n \n -Si vous ne voulez pas importer les anciennes données maintenant, vous pouvez toujours le faire plus tard en appuyant sur le bouton FUSIONNER LES ANCIENNES DONNÉES D’ACTIVITÉ » dans le Gestionnaire de base de données\n +Si vous ne voulez pas importer les anciennes données maintenant, vous pouvez toujours le faire plus tard en appuyant sur le bouton \"FUSIONNER LES ANCIENNES DONNÉES D’ACTIVITÉ\" dans le gestionnaire de base de données\n \n Notez que vous pouvez importer des données de Mi Band, Pebble Health et Morpheuz mais PAS de Pebble Misfit. - Stockez les enregistrement brut dans la base de données - Si coché, les données sont stockées \"tel quel\" et seront disponible pour interprétation ultérieurement (lorsque-nous serons en mesure de mieux les comprendre). -NOTE : la base de données sera bien évidement plus grande ! + Stockez les enregistrements brut dans la base de données + Si coché, les données sont stockées \"telles quelles\" et seront disponibles pour une interprétation ultérieure. +NOTE: la base de données sera bien évidement plus grande ! Gestion de base de données Gestion de base de données - Les opérations sur la base de donnée ont utilisé le chemin suivant sur votre appareil.\n Ce chemin n\'est pas accessible par d\'autres applications Android ou par votre ordinateur./nBase de donnée à attendu (Vous pouvez mettre aussi la base de donnée que vous souhaitez importer ici) : - Les données d\'activité enregistrées avec les versions antérieures à la 0.12 de Gadgetbridge doivent être convertis en un nouveau format.\n + Les opérations sur la base de donnée ont utilisé le chemin suivant sur votre appareil.\n Ce chemin n\'est pas accessible par d\'autres applications Android ou par votre ordinateur./nVous trouverez votre base de données (ou celle que vous souhaitez importer) ici: + Les données d\'activité enregistrées avec les versions antérieures à la 0.12 de Gadgetbridge doivent être converties en un nouveau format.\n Vous pouvez le faire en utilisant le bouton ci-dessous. Soyez conscient que vous devez être connecté à l\'appareil que vous souhaitez associer avec les anciennes données d\'activité !\n Si vous avez déjà importé vos données et êtes satisfait du résultat, vous pouvez supprimer l\'ancienne base de données. - Import / Suppression des anciennes Base de Données. + Import / Suppression des anciennes bases de données. Impossible d\'accéder au fichier d\'export. Merci de contacter les développeurs. Exporter vers : %1$s Erreur d\'exportation BD: %1$s Importer des données ? - Voulez vous vraiment effacer la base de donnée actuel ? Toutes vos données (si vous en avez) seront perdu. - Importation réussit. + Voulez-vous vraiment effacer la base de données actuelle ? Toutes vos données (si vous en avez) seront perdues. + Importation réussie. Erreur lors de l\'importation BD: %1$s - Aucune ancienne base de donnée trouvé, rien à importer. - Pas d\'appareil connecté à associer avec l\'ancienne base de donnée. - Fusion des données d\'activités + Aucune ancienne base de données trouvée, rien à importer. + Pas d\'appareil connecté à associer avec l\'ancienne base de données. + Fusion des données d\'activité Merci d\'attendre pendant la fusion de vos données. - Échec de l\'import des anciennes données d\'activité dans la nouvelle base de donnée. - Association des ancienne donnée avec le nouvel appareil. - Détruire les ancienne données ? - Voulez vous vraiment détruire entièrement la base de donnée ? Toutes vos données d\'activités et vos informations issus de vos appareils seront perdu. + Échec de l\'import des anciennes données d\'activité dans la nouvelle base de données. + Association des anciennes données avec le nouvel appareil. + Détruire les anciennes données ? + Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données d\'activité et vos informations issues de vos appareils seront perdues. Les données ont été effacées. - Échec de la destruction de la base de donnée. - Voulez vous détruire les anciennes activités de la base ? - Voulez vous vraiment détruire entièrement la base de donnée ? Toutes vos données non importé seront perdu. - Les anciennes données d\'activité ont été effacées correctement. - Échec de la destruction de l\'ancienne base de donnée. + Échec de la destruction de la base de données. + Voulez-vous détruire les anciennes activités de la base de données ? + Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données non importées seront perdues. + Les anciennes données d\'activité ont été correctement effacées. + Échec de la destruction de l\'ancienne base de données. Écraser Annuler Supprimer @@ -328,4 +353,12 @@ Si vous avez déjà importé vos données et êtes satisfait du résultat, vous Appairage avec une Pebble Une fenêtre d’appairage va s’afficher sur votre téléphone. Si cela ne se produit pas, regardez dans vos notifications et acceptez la demande d’appairage. Acceptez ensuite la demande d’appairage sur votre Pebble. + Assurez vous que ce thème soit activé dans l\'application de notification de la météo pour recevoir les informations sur votre Pebble.\n\nAucune configuration n\'est requise.\n\nVous pouvez activer l\'application météo système de votre Pebble depuis la configuration de l\'application.\n\nLes watchfaces supportées afficheront la météo automatiquement. + Activer l\'appairage Bluetooth + Désactivez ceci si vous avez des problèmes de connexion + Mesure + Impériale + 24H + AM/PM + Réveil diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 6c9eec16..7a3e39ea 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -56,7 +56,6 @@ Ismétlések Telefonhívások SMS - K9-Mail Pebble üzenetek Támogatás az alkalmazásoknak, amik értesítéseket küldenek a Pebble-nek a PebbleKit-en keresztül. Általános értesítési támogatás diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 80fe8241..17d93a81 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -54,14 +54,12 @@ Chiaro Scuro Lingua - Nascondi la notifica di gadgetbridge L\'icona nella barra di stato e la notifica nella schermata di blocco vengono mostrate L\'icona nella barra di stato e la notifica nella schermata di blocco non vengono mostrate Notifiche Ripetizioni Chiamate telefoniche SMS - K9-Mail Messaggi Pebble Supporto per applicazioni che inviano le notifiche a Pebble usando PebbleKit. Notifiche generiche diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a4925204..2572526a 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -62,7 +62,6 @@ 繰り返し 電話通知 SMS - K9メール Pebbleメッセージ PebbleKit経由でPebbleに通知を送信するアプリケーションをサポートします。 一般ステータス通知対応 @@ -123,6 +122,8 @@ Pebble 2/Pebble LE が期待どおりに機能しない場合は、この設定を試して MTU を制限してください (有効範囲 20-512) ウォッチアプリのログ記録を有効にする Gadgetbridgeがウォッチアプリからログを記録するようにする (再接続が必要です) + 早期 ACK PebbleKit + 外部のサードパーティアプリケーションに送信されたメッセージは、常に即座に承認されます 再接続の試行 単位 時刻形式 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2f42f669..5873f5b2 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -44,7 +44,6 @@ 반복 전화 SMS - K-9 메일 Pebble 메세지 일반적인 알림 지원 … 화면이 켜져있을 때도 diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 2a4484ce..5364165a 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -55,7 +55,6 @@ Powtórzenia Połączenia SMS - K9-Mail Wiadomości Pebble Obsługa ogólnych powiadomień ... także gdy ekran jest włączony diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2ed467a6..fe2315c5 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -46,7 +46,6 @@ Повторы Вызовы СМС-сообщения - K9-Mail Сообщения Pebble Поддержка обычных уведомлений … даже когда экран включён diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 1d966d84..57f1da2e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -44,7 +44,6 @@ Повтори Виклики SMS—повідомлення - K9-Mail Повідомлення Pebble Підтримка звичайних сповіщень … навіть коли екран увімкнено From c851f73265894af2c506fe9bf27f475f175abbd7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 11 Feb 2017 22:42:39 +0100 Subject: [PATCH 223/244] update CHANGELOG --- CHANGELOG.md | 5 ++++- app/src/main/res/xml/changelog_master.xml | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a3ce44c..25eb027e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ ###Changelog -####Version 0.17.4 (next) +####Version 0.17.4 * Better integration with android music players * Privacy options for calls (hide caller name/number) +* Send a notification to the connected if the Android Alarm Clock rings (com.android.deskclock) +* Fixes for cyrillic transliteration * Pebble: Implement notification privacy modes * Pebble: Support weather for Obisdian watchface +* Pebble: add a dev option to always and immediately ACK PebbleKit messages to the watch * HPlus: Support alarms * HPlus: Fix time and date sync and time format (12/24) * HPlus: Add device specific preferences and icon diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index b4d46f05..7ab0440c 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -3,8 +3,11 @@ Better integration with android music players Privacy options for calls (hide caller name/number) + Send a notification to the connected if the Android Alarm Clock rings (com.android.deskclock) + Fixes for cyrillic transliteration Pebble: Implement notification privacy modes Pebble: Support weather for Obisdian watchface + Pebble: add a dev option to always and immediately ACK PebbleKit messages to the watch HPlus: Support alarms HPlus: Fix time and date sync and time format (12/24) HPlus: Add device specific preferences and icon From b31dd9b2fa3758baf89d3a00df20866ff9521c15 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 11 Feb 2017 22:52:33 +0100 Subject: [PATCH 224/244] translate some strings to German --- app/src/main/res/values-de/strings.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3a714eff..06054038 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -62,9 +62,15 @@ … auch wenn der Bildschirm an ist Bitte nicht stören Stoppe unerwünschte Nachrichten, wenn im \"Nicht Stören\"-Modus + Transliteration immer wenn der Bildschirm aus ist niemals + Privatsphäre + Privatsphäre-Modus für Anrufe + Zeige Name und Telefonnumer + Verstecke den Namen aber zeige die Telefonnummer an + Verstecke Name und Telefonnummer Sperre für Apps Vorgefertigte Nachrichten Antworten @@ -83,6 +89,10 @@ Experimentelle Unterstützung für Android Apps, die PebbleKit benutzen Sonnenauf- und -untergang Sende Sonnenauf- und -untergangszeiten abhänging vom Standort auf die Pebble Timeline + Privatsphäre-Modus + Normale Benachrichtigungen + Verschiebe den Benachrichttigungstext außerhalb des Bildschirms + Zeige nur das Benachrichtigungs-Symbol Standort Standort Bestimmen Breitengrad @@ -102,6 +112,8 @@ Watch App Logging einschalten Schreibt logs von Watch Apps in Gadgetbridge logs (Pebble muss nach Ändern der Option erneut verbunden werden) Neuverbindungsversuche + Einheiten + Zeitformat nicht verbunden verbinde verbunden @@ -308,4 +320,5 @@ Wenn Du schon deine Daten importiert hast und mit dem Ergebnis zufrieden bist, k Vibration + Wecker From 1d1edd41d755cda7039e3dbc89ef68df8bd7e0b9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 12 Feb 2017 23:12:42 +0100 Subject: [PATCH 225/244] Pebble 2/LE: remove a sleep which might be no longer necessary Will speedup data transfer --- .../service/devices/pebble/ble/PebbleGATTServer.java | 5 ----- .../service/devices/pebble/ble/PebbleLESupport.java | 2 +- 2 files changed, 1 insertion(+), 6 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 fd8637bb..fba11462 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 @@ -65,11 +65,6 @@ class PebbleGATTServer extends BluetoothGattServerCallback { 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) { 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 e13552d9..07ed20a5 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 @@ -24,7 +24,7 @@ public class PebbleLESupport { private int mMTU = 20; private int mMTULimit = Integer.MAX_VALUE; boolean mIsConnected = false; - public CountDownLatch mPPAck; + CountDownLatch mPPAck; public PebbleLESupport(Context context, final BluetoothDevice btDevice, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) throws IOException { mBtDevice = btDevice; From a26563d6c7bc94cf91fb439ba99f57ad5e4d78a7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 13 Feb 2017 22:27:37 +0100 Subject: [PATCH 226/244] Pebble: also acknowledge PebbleKit intents with transaction_id -1 I don't understand why this should be necessary but for some 3rd party apps it helps (#509) --- .../service/devices/pebble/PebbleKitSupport.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index 2277d1ca..44117004 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -55,9 +55,9 @@ class PebbleKitSupport { try { JSONArray jsonArray = new JSONArray(jsonString); mPebbleIoThread.write(mPebbleProtocol.encodeApplicationMessageFromJSON(uuid, jsonArray)); - if (transaction_id >= 0 && transaction_id <= 255) { - sendAppMessageAck(transaction_id); - } + // if (transaction_id >= 0 && transaction_id <= 255) { + sendAppMessageAck(transaction_id); + // } } catch (JSONException e) { e.printStackTrace(); } From 23f2dd35d44cda43439ea5e50b11f9c243100ca9 Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 17 Feb 2017 10:01:37 +0200 Subject: [PATCH 227/244] Extract music shuffle and repeat states and set the song progress to auto-update. (#554) --- .../externalevents/MusicPlaybackReceiver.java | 13 +++++++++++++ .../gadgetbridge/model/MusicStateSpec.java | 8 ++++---- .../service/DeviceCommunicationService.java | 3 ++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index 6a84e42c..5afa6791 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -51,6 +51,7 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { stateSpec.position = ((Long) incoming).intValue() / 1000; } else if (incoming instanceof Boolean && "playing".equals(key)) { stateSpec.state = (byte) (((Boolean) incoming) ? MusicStateSpec.STATE_PLAYING : MusicStateSpec.STATE_PAUSED); + stateSpec.playRate = (byte) (((Boolean) incoming) ? 100 : 0); } else if (incoming instanceof String && "duration".equals(key)) { musicSpec.duration = Integer.valueOf((String) incoming) / 1000; } else if (incoming instanceof String && "trackno".equals(key)) { @@ -59,6 +60,18 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { musicSpec.trackCount = Integer.valueOf((String) incoming); } else if (incoming instanceof Integer && "pos".equals(key)) { stateSpec.position = (Integer) incoming; + } else if (incoming instanceof Integer && "repeat".equals(key)) { + if ((Integer) incoming > 0) { + stateSpec.repeat = 1; + } else { + stateSpec.repeat = 0; + } + } else if (incoming instanceof Integer && "shuffle".equals(key)) { + if ((Integer) incoming > 0) { + stateSpec.shuffle = 1; + } else { + stateSpec.shuffle = 0; + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java index c960b149..eac51d93 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java @@ -10,8 +10,8 @@ public class MusicStateSpec { public static final int STATE_UNKNOWN = 3; public byte state; - public int position; - public int playRate; + public int position; // Position of the current media in seconds + public int playRate; // Speed of playback, usually 0 or 100 (full speed) public byte shuffle; public byte repeat; @@ -47,8 +47,8 @@ public class MusicStateSpec { @Override public int hashCode() { int result = (int) state; -// ignore the position -- it is taken into account in equals() -// result = 31 * result + position; +//ignore the position -- it is taken into account in equals() +//result = 31 * result + position; result = 31 * result + playRate; result = 31 * result + (int) shuffle; result = 31 * result + (int) repeat; 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 04206e8d..d88594e9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -163,7 +163,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere "com.android.music.playbackcomplete", "net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED", "com.maxmpz.audioplayer.TPOS_SYNC", - "com.maxmpz.audioplayer.STATUS_CHANGED",}; + "com.maxmpz.audioplayer.STATUS_CHANGED", + "com.maxmpz.audioplayer.PLAYING_MODE_CHANGED"}; /** * For testing! From e5d09b9fa2d358a66a897e84cbb3621934436c3b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 17 Feb 2017 23:11:44 +0100 Subject: [PATCH 228/244] Automatically start the service on boot (can be turned off) Fixes #9 --- app/src/main/AndroidManifest.xml | 9 +++++++++ .../externalevents/AutoStartReceiver.java | 20 +++++++++++++++++++ .../gadgetbridge/util/GBPrefs.java | 6 ++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 4 ++++ 5 files changed, 40 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bdbc7878..b4132de8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ + + + + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java new file mode 100644 index 00000000..c6c194d0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java @@ -0,0 +1,20 @@ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; + +public class AutoStartReceiver extends BroadcastReceiver { + private static final String TAG = AutoStartReceiver.class.getName(); + + @Override + public void onReceive(Context context, Intent intent) { + if (GBApplication.getGBPrefs().getAutoStart()) { + Log.i(TAG, "Boot completed, starting Gadgetbridge"); + GBApplication.deviceService().start(); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java index 024fb442..4fc42f0f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java @@ -6,6 +6,8 @@ import java.util.Date; public class GBPrefs { public static final String AUTO_RECONNECT = "general_autocreconnect"; + private static final String AUTO_START = "general_autostartonboot"; + private static final boolean AUTO_START_DEFAULT = true; public static boolean AUTO_RECONNECT_DEFAULT = true; public static final String USER_NAME = "mi_user_alias"; @@ -22,6 +24,10 @@ public class GBPrefs { return mPrefs.getBoolean(AUTO_RECONNECT, AUTO_RECONNECT_DEFAULT); } + public boolean getAutoStart() { + return mPrefs.getBoolean(AUTO_START, AUTO_START_DEFAULT); + } + public String getUserName() { return mPrefs.getString(USER_NAME, USER_NAME_DEFAULT); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3bed06a3..a3619175 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -52,6 +52,7 @@ General Settings Connect to device when Bluetooth turned on + Start automatically Reconnect automatically Preferred Audioplayer Default diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d324fa3a..df84c17c 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -3,6 +3,10 @@ + Date: Sun, 19 Feb 2017 22:59:37 +0100 Subject: [PATCH 229/244] Pebble: First shot at implementing dataloggin for PebbleKit apps Closes #497 Could help #316 --- .../pebble/GBDeviceEventDataLogging.java | 16 ++++++ .../devices/pebble/DatalogSession.java | 42 ++++++++++++++++ .../devices/pebble/PebbleIoThread.java | 9 ++++ .../devices/pebble/PebbleKitSupport.java | 50 +++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 40 +++++++++++---- 5 files changed, 148 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java new file mode 100644 index 00000000..971d99cc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java @@ -0,0 +1,16 @@ +package nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble; + +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; + +public class GBDeviceEventDataLogging extends GBDeviceEvent { + public static final int COMMAND_RECEIVE_DATA = 1; + public static final int COMMAND_FINISH_SESSION = 2; + + public int command; + public UUID appUUID; + public long tag; + public byte pebbleDataType; + public Object data; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java index 2b07d944..fc729143 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -1,9 +1,17 @@ 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.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; + class DatalogSession { + private static final Logger LOG = LoggerFactory.getLogger(DatalogSession.class); + final byte id; final int tag; final UUID uuid; @@ -26,4 +34,38 @@ class DatalogSession { String getTaginfo() { return taginfo; } + + GBDeviceEvent[] handleMessageForPebbleKit(ByteBuffer buf, int length) { + if (0 != (length % itemSize)) { + LOG.warn("invalid length"); + return null; + } + int packetCount = length / itemSize; + GBDeviceEvent[] gbDeviceEvents = new GBDeviceEvent[packetCount + 1]; // pad for ack + for (int i = 0; i < packetCount; i++) { + GBDeviceEventDataLogging dataLogging = new GBDeviceEventDataLogging(); + switch (itemType) { + case PebbleProtocol.TYPE_BYTEARRAY: + byte[] itemData = new byte[itemSize]; + buf.get(itemData); + dataLogging.data = itemData; + break; + + case PebbleProtocol.TYPE_UINT: + dataLogging.data = buf.getInt() & 0xffffffffL; + break; + + case PebbleProtocol.TYPE_INT: + dataLogging.data = buf.getInt(); + break; + } + + dataLogging.command = GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA; + dataLogging.appUUID = uuid; + dataLogging.tag = tag; + dataLogging.pebbleDataType = itemType; + gbDeviceEvents[i] = dataLogging; + } + return gbDeviceEvents; + } } \ No newline at end of file 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 35c1c256..4ec9cacb 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 @@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppMessage; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PBWReader; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleInstallable; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -86,6 +87,7 @@ class PebbleIoThread extends GBDeviceIoThread { mPebbleSupport = pebbleSupport; mEnablePebblekit = prefs.getBoolean("pebble_enable_pebblekit", false); mPebbleProtocol.setAlwaysACKPebbleKit(prefs.getBoolean("pebble_always_ack_pebblekit", false)); + mPebbleProtocol.setEnablePebbleKit(mEnablePebblekit); } private int readWithException(InputStream inputStream, byte[] buffer, int byteOffset, int byteCount) throws IOException { @@ -493,6 +495,13 @@ class PebbleIoThread extends GBDeviceIoThread { mPebbleKitSupport.sendAppMessageIntent((GBDeviceEventAppMessage) deviceEvent); } } + } else if (deviceEvent instanceof GBDeviceEventDataLogging) { + if (mEnablePebblekit) { + LOG.info("Got Datalogging event"); + if (mPebbleKitSupport != null) { + mPebbleKitSupport.sendDataLoggingIntent((GBDeviceEventDataLogging) deviceEvent); + } + } } return false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index 44117004..9f285de8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.util.Base64; import org.json.JSONArray; import org.json.JSONException; @@ -13,6 +14,7 @@ import org.slf4j.LoggerFactory; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppMessage; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; class PebbleKitSupport { //private static final String PEBBLEKIT_ACTION_PEBBLE_CONNECTED = "com.getpebble.action.PEBBLE_CONNECTED"; @@ -26,12 +28,20 @@ class PebbleKitSupport { private static final String PEBBLEKIT_ACTION_APP_START = "com.getpebble.action.app.START"; private static final String PEBBLEKIT_ACTION_APP_STOP = "com.getpebble.action.app.STOP"; + private static final String PEBBLEKIT_ACTION_DL_RECEIVE_DATA_NEW = "com.getpebble.action.dl.RECEIVE_DATA_NEW"; + private static final String PEBBLEKIT_ACTION_DL_RECEIVE_DATA = "com.getpebble.action.dl.RECEIVE_DATA"; + private static final String PEBBLEKIT_ACTION_DL_ACK_DATA = "com.getpebble.action.dl.ACK_DATA"; + private static final String PEBBLEKIT_ACTION_DL_REQUEST_DATA = "com.getpebble.action.dl.REQUEST_DATA"; + private static final String PEBBLEKIT_ACTION_DL_FINISH_SESSION = "com.getpebble.action.dl.FINISH_SESSION_NEW"; + private static final Logger LOG = LoggerFactory.getLogger(PebbleKitSupport.class); private final PebbleProtocol mPebbleProtocol; private final Context mContext; private final PebbleIoThread mPebbleIoThread; + private int dataLogTransactionId = 1; + private final BroadcastReceiver mPebbleKitReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -72,6 +82,9 @@ class PebbleKitSupport { } } break; + case PEBBLEKIT_ACTION_DL_ACK_DATA: + LOG.info("GOT DL DATA ACK"); + break; } } @@ -88,6 +101,7 @@ class PebbleKitSupport { intentFilter.addAction(PEBBLEKIT_ACTION_APP_SEND); intentFilter.addAction(PEBBLEKIT_ACTION_APP_START); intentFilter.addAction(PEBBLEKIT_ACTION_APP_STOP); + intentFilter.addAction(PEBBLEKIT_ACTION_DL_ACK_DATA); mContext.registerReceiver(mPebbleKitReceiver, intentFilter); } @@ -116,4 +130,40 @@ class PebbleKitSupport { } } + void sendDataLoggingIntent(GBDeviceEventDataLogging dataLogging) { + Intent intent = new Intent(); + intent.putExtra("data_log_timestamp", System.currentTimeMillis() / 1000); // is this data really not present in data from watch?! + intent.putExtra("uuid", dataLogging.appUUID); + intent.putExtra("data_log_uuid", dataLogging.appUUID); // Is that really the same? + intent.putExtra("data_log_tag", dataLogging.tag); + + switch (dataLogging.command) { + case GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA: + intent.setAction(PEBBLEKIT_ACTION_DL_RECEIVE_DATA_NEW); + intent.putExtra("pbl_data_id", dataLogTransactionId++); + intent.putExtra("pbl_data_type", dataLogging.pebbleDataType); + switch (dataLogging.pebbleDataType) { + case PebbleProtocol.TYPE_BYTEARRAY: + intent.putExtra("pbl_data_object", Base64.encodeToString((byte[]) dataLogging.data, Base64.NO_WRAP)); + break; + case PebbleProtocol.TYPE_UINT: + intent.putExtra("pbl_data_object", (Long) dataLogging.data); + break; + case PebbleProtocol.TYPE_INT: + intent.putExtra("pbl_data_object", (Integer) dataLogging.data); + break; + } + LOG.info("broadcasting datalogging to uuid " + dataLogging.appUUID + " tag: " + dataLogging.tag + "transaction id: " + dataLogTransactionId + " type: " + dataLogging.pebbleDataType); + break; + case GBDeviceEventDataLogging.COMMAND_FINISH_SESSION: + intent.setAction(PEBBLEKIT_ACTION_DL_FINISH_SESSION); + LOG.info("broadcasting datalogging finish session to uuid " + dataLogging.appUUID + " tag: " + dataLogging.tag); + + break; + default: + LOG.warn("invalid datalog command"); + return; + } + mContext.sendBroadcast(intent); + } } 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 f3ae7825..ce252975 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 @@ -28,6 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificati import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; @@ -224,10 +225,10 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final byte PHONEVERSION_REMOTE_OS_LINUX = 4; private static final byte PHONEVERSION_REMOTE_OS_WINDOWS = 5; - private static final byte TYPE_BYTEARRAY = 0; + static final byte TYPE_BYTEARRAY = 0; private static final byte TYPE_CSTRING = 1; - private static final byte TYPE_UINT = 2; - private static final byte TYPE_INT = 3; + static final byte TYPE_UINT = 2; + static final byte TYPE_INT = 3; private final short LENGTH_PREFIX = 4; @@ -253,6 +254,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final Random mRandom = new Random(); int mFwMajor = 3; + boolean mEnablePebbleKit = false; boolean mAlwaysACKPebbleKit = false; private boolean mForceProtocol = false; private GBDeviceEventScreenshot mDevEventScreenshot = null; @@ -2212,10 +2214,11 @@ public class PebbleProtocol extends GBDeviceProtocol { return null; } - private GBDeviceEventSendBytes decodeDatalog(ByteBuffer buf, short length) { + private GBDeviceEvent[] decodeDatalog(ByteBuffer buf, short length) { boolean ack = true; byte command = buf.get(); byte id = buf.get(); + GBDeviceEvent[] devEvts = new GBDeviceEvent[1]; switch (command) { case DATALOG_TIMEOUT: LOG.info("DATALOG TIMEOUT. id=" + (id & 0xff) + " - ignoring"); @@ -2228,7 +2231,11 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("DATALOG SENDDATA. id=" + (id & 0xff) + ", items_left=" + items_left + ", total length=" + (length - 10)); if (datalogSession != null) { LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + datalogSession.getTaginfo() + ", itemSize=" + datalogSession.itemSize + ", itemType=" + datalogSession.itemType); - ack = datalogSession.handleMessage(buf, length - 10); + if (!datalogSession.uuid.equals(UUID_ZERO) && datalogSession.getClass().equals(DatalogSession.class) && mEnablePebbleKit) { + devEvts = datalogSession.handleMessageForPebbleKit(buf, length - 10); + } else { + ack = datalogSession.handleMessage(buf, length - 10); + } } break; case DATALOG_OPENSESSION: @@ -2255,7 +2262,15 @@ public class PebbleProtocol extends GBDeviceProtocol { break; case DATALOG_CLOSE: LOG.info("DATALOG_CLOSE. id=" + (id & 0xff)); - if (mDatalogSessions.containsKey(id)) { + datalogSession = mDatalogSessions.get(id); + if (datalogSession != null) { + if (!datalogSession.uuid.equals(UUID_ZERO) && datalogSession.getClass().equals(DatalogSession.class) && mEnablePebbleKit) { + GBDeviceEventDataLogging dataLogging = new GBDeviceEventDataLogging(); + dataLogging.command = GBDeviceEventDataLogging.COMMAND_FINISH_SESSION; + dataLogging.appUUID = datalogSession.uuid; + dataLogging.tag = datalogSession.tag; + devEvts = new GBDeviceEvent[]{dataLogging, null}; + } mDatalogSessions.remove(id); } break; @@ -2271,7 +2286,9 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("sending NACK (0x86)"); sendBytes.encodedBytes = encodeDatalog(id, DATALOG_NACK); } - return sendBytes; + // append ack/nack + devEvts[devEvts.length - 1] = sendBytes; + return devEvts; } private GBDeviceEvent decodeAppReorder(ByteBuffer buf) { @@ -2539,7 +2556,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } break; case ENDPOINT_DATALOG: - devEvts = new GBDeviceEvent[]{decodeDatalog(buf, length)}; + devEvts = decodeDatalog(buf, length); break; case ENDPOINT_SCREENSHOT: devEvts = new GBDeviceEvent[]{decodeScreenshot(buf, length)}; @@ -2587,10 +2604,15 @@ public class PebbleProtocol extends GBDeviceProtocol { } void setAlwaysACKPebbleKit(boolean alwaysACKPebbleKit) { - LOG.info("setting always ACK Pebbleit to " + alwaysACKPebbleKit); + LOG.info("setting always ACK PebbleKit to " + alwaysACKPebbleKit); mAlwaysACKPebbleKit = alwaysACKPebbleKit; } + void setEnablePebbleKit(boolean enablePebbleKit) { + LOG.info("setting enable PebbleKit support to " + enablePebbleKit); + mEnablePebbleKit = enablePebbleKit; + } + private String getFixedString(ByteBuffer buf, int length) { byte[] tmp = new byte[length]; buf.get(tmp, 0, length); From ad9cfae6f972c2b61af9af337966f5896ab69f91 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 20 Feb 2017 08:47:42 +0100 Subject: [PATCH 230/244] Pebble: Pass datalog creation timestamp to PebbleKit, properly announce PebbleKit datalogging support --- .../contentprovider/PebbleContentProvider.java | 6 +++--- .../deviceevents/pebble/GBDeviceEventDataLogging.java | 1 + .../service/devices/pebble/DatalogSession.java | 5 ++++- .../service/devices/pebble/DatalogSessionHealthHR.java | 4 ++-- .../pebble/DatalogSessionHealthOverlayData.java | 4 ++-- .../devices/pebble/DatalogSessionHealthSleep.java | 4 ++-- .../devices/pebble/DatalogSessionHealthSteps.java | 4 ++-- .../devices/pebble/DatalogSessionPebbleHealth.java | 4 ++-- .../service/devices/pebble/PebbleKitSupport.java | 2 +- .../service/devices/pebble/PebbleProtocol.java | 10 +++++----- 10 files changed, 24 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java index e16c1bdc..384e2874 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java @@ -58,17 +58,17 @@ public class PebbleContentProvider extends ContentProvider { if (uri.equals(CONTENT_URI)) { MatrixCursor mc = new MatrixCursor(columnNames); int connected = 0; - int appMessage = 0; + int pebbleKit = 0; Prefs prefs = GBApplication.getPrefs(); if (prefs.getBoolean("pebble_enable_pebblekit", false)) { - appMessage = 1; + pebbleKit = 1; } String fwString = "unknown"; if (mGBDevice != null && mGBDevice.getType() == DeviceType.PEBBLE && mGBDevice.isInitialized()) { connected = 1; fwString = mGBDevice.getFirmwareVersion(); } - mc.addRow(new Object[]{connected, appMessage, 0, 3, 8, 2, fwString}); + mc.addRow(new Object[]{connected, pebbleKit, pebbleKit, 3, 8, 2, fwString}); return mc; } else { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java index 971d99cc..83c88af0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java @@ -10,6 +10,7 @@ public class GBDeviceEventDataLogging extends GBDeviceEvent { public int command; public UUID appUUID; + public long timestamp; public long tag; public byte pebbleDataType; public Object data; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java index fc729143..08409e75 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -17,12 +17,14 @@ class DatalogSession { final UUID uuid; final byte itemType; final short itemSize; + final int timestamp; String taginfo = "(unknown)"; - DatalogSession(byte id, UUID uuid, int tag, byte itemType, short itemSize) { + DatalogSession(byte id, UUID uuid, int timestamp, int tag, byte itemType, short itemSize) { this.id = id; this.tag = tag; this.uuid = uuid; + this.timestamp = timestamp; this.itemType = itemType; this.itemSize = itemSize; } @@ -62,6 +64,7 @@ class DatalogSession { dataLogging.command = GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA; dataLogging.appUUID = uuid; + dataLogging.timestamp = timestamp & 0xffffffffL; dataLogging.tag = tag; dataLogging.pebbleDataType = itemType; gbDeviceEvents[i] = dataLogging; 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 index 2f70e1f4..61400aa4 100644 --- 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 @@ -13,8 +13,8 @@ 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); + DatalogSessionHealthHR(byte id, UUID uuid, int timestamp, int tag, byte item_type, short item_size, GBDevice device) { + super(id, uuid, timestamp, tag, item_type, item_size, device); taginfo = "(Health - HR " + tag + " )"; } 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 e41d8f32..463c5749 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 @@ -22,8 +22,8 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthOverlayData.class); - 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); + DatalogSessionHealthOverlayData(byte id, UUID uuid, int timestamp, int tag, byte item_type, short item_size, GBDevice device) { + super(id, uuid, timestamp, tag, item_type, item_size, device); taginfo = "(Health - overlay data " + tag + " )"; } 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 d6534ef7..1cb77082 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 @@ -22,8 +22,8 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSleep.class); - 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); + DatalogSessionHealthSleep(byte id, UUID uuid, int timestamp, int tag, byte item_type, short item_size, GBDevice device) { + super(id, uuid, timestamp, tag, item_type, item_size, device); taginfo = "(Health - sleep " + tag + " )"; } 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 1f69dcd1..725c8924 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 @@ -20,8 +20,8 @@ class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSteps.class); - 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); + DatalogSessionHealthSteps(byte id, UUID uuid, int timestamp, int tag, byte item_type, short item_size, GBDevice device) { + super(id, uuid, timestamp, tag, item_type, item_size, device); taginfo = "(Health - steps)"; } 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 2aae2cb9..e54780aa 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 @@ -10,8 +10,8 @@ abstract class DatalogSessionPebbleHealth extends DatalogSession { private final GBDevice mDevice; - DatalogSessionPebbleHealth(byte id, UUID uuid, int tag, byte itemType, short itemSize, GBDevice device) { - super(id, uuid, tag, itemType, itemSize); + DatalogSessionPebbleHealth(byte id, UUID uuid, int timestamp, int tag, byte itemType, short itemSize, GBDevice device) { + super(id, uuid, timestamp, tag, itemType, itemSize); mDevice = device; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index 9f285de8..ed28ff01 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -132,7 +132,7 @@ class PebbleKitSupport { void sendDataLoggingIntent(GBDeviceEventDataLogging dataLogging) { Intent intent = new Intent(); - intent.putExtra("data_log_timestamp", System.currentTimeMillis() / 1000); // is this data really not present in data from watch?! + intent.putExtra("data_log_timestamp", dataLogging.timestamp); intent.putExtra("uuid", dataLogging.appUUID); intent.putExtra("data_log_uuid", dataLogging.appUUID); // Is that really the same? intent.putExtra("data_log_tag", dataLogging.tag); 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 ce252975..dc028440 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 @@ -2248,15 +2248,15 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", itemSize=" + item_size); if (!mDatalogSessions.containsKey(id)) { if (uuid.equals(UUID_ZERO) && log_tag == 81) { - mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size, getDevice())); + mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, timestamp, log_tag, item_type, item_size, getDevice())); } else if (uuid.equals(UUID_ZERO) && log_tag == 83) { - mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size, getDevice())); + mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, timestamp, 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())); + mDatalogSessions.put(id, new DatalogSessionHealthOverlayData(id, uuid, timestamp, 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())); + mDatalogSessions.put(id, new DatalogSessionHealthHR(id, uuid, timestamp, log_tag, item_type, item_size, getDevice())); } else { - mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); + mDatalogSessions.put(id, new DatalogSession(id, uuid, timestamp, log_tag, item_type, item_size)); } } break; From 2dbda6138bf130438aea9577c838edf0188c2fa7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 20 Feb 2017 22:09:00 +0100 Subject: [PATCH 231/244] Pebble: some cleanups and simplifications for datalogging via PebbleKit --- .../pebble/GBDeviceEventDataLogging.java | 2 +- .../devices/pebble/DatalogSession.java | 34 +++++++++++-------- .../devices/pebble/PebbleKitSupport.java | 31 +++++++++-------- .../devices/pebble/PebbleProtocol.java | 12 ++++--- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java index 83c88af0..1dd4f543 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java @@ -13,5 +13,5 @@ public class GBDeviceEventDataLogging extends GBDeviceEvent { public long timestamp; public long tag; public byte pebbleDataType; - public Object data; + public Object[] data; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java index 08409e75..ed03b054 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; class DatalogSession { @@ -37,38 +36,43 @@ class DatalogSession { return taginfo; } - GBDeviceEvent[] handleMessageForPebbleKit(ByteBuffer buf, int length) { + GBDeviceEventDataLogging handleMessageForPebbleKit(ByteBuffer buf, int length) { if (0 != (length % itemSize)) { LOG.warn("invalid length"); return null; } int packetCount = length / itemSize; - GBDeviceEvent[] gbDeviceEvents = new GBDeviceEvent[packetCount + 1]; // pad for ack + + if (packetCount <= 0) { + LOG.warn("invalid number of datalog elements"); + return null; + } + + GBDeviceEventDataLogging dataLogging = new GBDeviceEventDataLogging(); + dataLogging.command = GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA; + dataLogging.appUUID = uuid; + dataLogging.timestamp = timestamp & 0xffffffffL; + dataLogging.tag = tag; + dataLogging.pebbleDataType = itemType; + dataLogging.data = new Object[packetCount]; + for (int i = 0; i < packetCount; i++) { - GBDeviceEventDataLogging dataLogging = new GBDeviceEventDataLogging(); switch (itemType) { case PebbleProtocol.TYPE_BYTEARRAY: byte[] itemData = new byte[itemSize]; buf.get(itemData); - dataLogging.data = itemData; + dataLogging.data[i] = itemData; break; case PebbleProtocol.TYPE_UINT: - dataLogging.data = buf.getInt() & 0xffffffffL; + dataLogging.data[i] = buf.getInt() & 0xffffffffL; break; case PebbleProtocol.TYPE_INT: - dataLogging.data = buf.getInt(); + dataLogging.data[i] = buf.getInt(); break; } - - dataLogging.command = GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA; - dataLogging.appUUID = uuid; - dataLogging.timestamp = timestamp & 0xffffffffL; - dataLogging.tag = tag; - dataLogging.pebbleDataType = itemType; - gbDeviceEvents[i] = dataLogging; } - return gbDeviceEvents; + return dataLogging; } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index ed28ff01..fbccba86 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -140,30 +140,31 @@ class PebbleKitSupport { switch (dataLogging.command) { case GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA: intent.setAction(PEBBLEKIT_ACTION_DL_RECEIVE_DATA_NEW); - intent.putExtra("pbl_data_id", dataLogTransactionId++); intent.putExtra("pbl_data_type", dataLogging.pebbleDataType); - switch (dataLogging.pebbleDataType) { - case PebbleProtocol.TYPE_BYTEARRAY: - intent.putExtra("pbl_data_object", Base64.encodeToString((byte[]) dataLogging.data, Base64.NO_WRAP)); - break; - case PebbleProtocol.TYPE_UINT: - intent.putExtra("pbl_data_object", (Long) dataLogging.data); - break; - case PebbleProtocol.TYPE_INT: - intent.putExtra("pbl_data_object", (Integer) dataLogging.data); - break; + for (Object dataObject : dataLogging.data) { + intent.putExtra("pbl_data_id", dataLogTransactionId++); + switch (dataLogging.pebbleDataType) { + case PebbleProtocol.TYPE_BYTEARRAY: + intent.putExtra("pbl_data_object", Base64.encodeToString((byte[]) dataObject, Base64.NO_WRAP)); + break; + case PebbleProtocol.TYPE_UINT: + intent.putExtra("pbl_data_object", (Long) dataObject); + break; + case PebbleProtocol.TYPE_INT: + intent.putExtra("pbl_data_object", (Integer) dataObject); + break; + } + LOG.info("broadcasting datalogging to uuid " + dataLogging.appUUID + " tag: " + dataLogging.tag + "transaction id: " + dataLogTransactionId + " type: " + dataLogging.pebbleDataType); + mContext.sendBroadcast(intent); } - LOG.info("broadcasting datalogging to uuid " + dataLogging.appUUID + " tag: " + dataLogging.tag + "transaction id: " + dataLogTransactionId + " type: " + dataLogging.pebbleDataType); break; case GBDeviceEventDataLogging.COMMAND_FINISH_SESSION: intent.setAction(PEBBLEKIT_ACTION_DL_FINISH_SESSION); LOG.info("broadcasting datalogging finish session to uuid " + dataLogging.appUUID + " tag: " + dataLogging.tag); - + mContext.sendBroadcast(intent); break; default: LOG.warn("invalid datalog command"); - return; } - mContext.sendBroadcast(intent); } } 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 dc028440..8e6ed14d 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 @@ -2218,7 +2218,7 @@ public class PebbleProtocol extends GBDeviceProtocol { boolean ack = true; byte command = buf.get(); byte id = buf.get(); - GBDeviceEvent[] devEvts = new GBDeviceEvent[1]; + GBDeviceEventDataLogging devEvtDataLogging = null; switch (command) { case DATALOG_TIMEOUT: LOG.info("DATALOG TIMEOUT. id=" + (id & 0xff) + " - ignoring"); @@ -2232,7 +2232,10 @@ public class PebbleProtocol extends GBDeviceProtocol { if (datalogSession != null) { LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + datalogSession.getTaginfo() + ", itemSize=" + datalogSession.itemSize + ", itemType=" + datalogSession.itemType); if (!datalogSession.uuid.equals(UUID_ZERO) && datalogSession.getClass().equals(DatalogSession.class) && mEnablePebbleKit) { - devEvts = datalogSession.handleMessageForPebbleKit(buf, length - 10); + devEvtDataLogging = datalogSession.handleMessageForPebbleKit(buf, length - 10); + if (devEvtDataLogging == null) { + ack = false; + } } else { ack = datalogSession.handleMessage(buf, length - 10); } @@ -2269,7 +2272,7 @@ public class PebbleProtocol extends GBDeviceProtocol { dataLogging.command = GBDeviceEventDataLogging.COMMAND_FINISH_SESSION; dataLogging.appUUID = datalogSession.uuid; dataLogging.tag = datalogSession.tag; - devEvts = new GBDeviceEvent[]{dataLogging, null}; + devEvtDataLogging = dataLogging; } mDatalogSessions.remove(id); } @@ -2287,8 +2290,7 @@ public class PebbleProtocol extends GBDeviceProtocol { sendBytes.encodedBytes = encodeDatalog(id, DATALOG_NACK); } // append ack/nack - devEvts[devEvts.length - 1] = sendBytes; - return devEvts; + return new GBDeviceEvent[]{devEvtDataLogging, sendBytes}; } private GBDeviceEvent decodeAppReorder(ByteBuffer buf) { From c23e496db6d16424279fc3c39cd1e3268143711a Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 20 Feb 2017 22:19:16 +0100 Subject: [PATCH 232/244] bump version, update changelog --- CHANGELOG.md | 6 ++++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25eb027e..4aa956f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ###Changelog +####Version 0.17.5 +* Automatically start the service on boot (can be turned off) +* Pebble: PebbleKit compatibility improvements (Datalogging) +* Pebble: Display music shuffle and repeat states for some players +* Pebble 2/LE: Speed up data transfer + ####Version 0.17.4 * Better integration with android music players * Privacy options for calls (hide caller name/number) diff --git a/app/build.gradle b/app/build.gradle index 985f5ef3..efd0ed85 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.17.4" - versionCode 85 + versionName "0.17.5" + versionCode 86 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 7ab0440c..469c9e74 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,11 @@ + + Automatically start the service on boot (can be turned off) + Pebble: PebbleKit compatibility improvements (Datalogging) + Pebble: Display music shuffle and repeat states for some players + Pebble 2/LE: Speed up data transfer + Better integration with android music players Privacy options for calls (hide caller name/number) From 24794c46b1960cf3b929bcfcd579fbe9541aba20 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 20 Feb 2017 22:20:22 +0100 Subject: [PATCH 233/244] update translations from transifex (THANKS) --- app/src/main/res/values-es/strings.xml | 3 +++ app/src/main/res/values-fr/strings.xml | 3 +++ app/src/main/res/values-ja/strings.xml | 1 + 3 files changed, 7 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 8ee6275d..aa21fdae 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -45,6 +45,7 @@ Ajustes Ajustes generales Conectarse al dispositivo cuando el Bluetooth esté activado + Iniciar automáticamente Reconectar automáticamente Reproductor de audio favorito Predeterminado @@ -122,6 +123,8 @@ Si tu Pebble 2/Pebble LE no funciona correctamente, prueba esta opción para limitar el MTU (rango válido 20–512) Activar crear registros de la App del Reloj Producirá registros de las apps del reloj que Gadgetbridge guardará (necesita reconexión) + ACK antes de tiempo de PebbleKit + Permitirá a los mensajes enviados a apps de terceros ser reconocidos siempre e inmediatamente Intentos de reconexión Unidades Formato de hora diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 411ee160..2ab02777 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -45,6 +45,7 @@ Paramètres Paramètres généraux Connecter votre appareil quand le Bluetooth est mis en marche + Démarrer automatiquement Reconnexion automatique Lecteur audio préféré Par défaut @@ -122,6 +123,8 @@ Si votre Pebble 2/LE ne fonctionne pas correctement, essayez d\'activer cette option pour limiter le MTU (plage valide 20-512) Activer les logs des Watch App Ceci permettra à Gadgetbridge de conserver les logs des Watch App (requiert une reconnexion) + ACK à l\'avance du PebbleKit + Ceci permettra aux messages envoyés à des apps tierces d\'être toujours reconnus immédiatement Tentatives de reconnexion Unités Format de l\'heure diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 2572526a..967a7965 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -45,6 +45,7 @@ 設定 一般設定 Bluetoothがオンになったときにデバイスに接続 + 自動的に開始 自動的に再接続 お好みのオーディオプレイヤー デフォルト From db58b32b6f00a1aac233e1f57f6334a520faa59d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 20 Feb 2017 22:23:44 +0100 Subject: [PATCH 234/244] Update two german strings --- app/src/main/res/values-de/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 06054038..e458991e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -42,6 +42,7 @@ Einstellungen Allgemeine Einstellungen Verbinde, wenn Bluetooth eingeschaltet wird + Automatisch starten Verbindungen automatisch wiederherstellen Bevorzugter Audioplayer Standard @@ -85,6 +86,7 @@ Pebble Health synchronisieren Misfit synchronisieren Morpheuz synchronisieren + Unterstützung für ausgehende Anrufe Erlaube Zugriff von anderen Android Apps Experimentelle Unterstützung für Android Apps, die PebbleKit benutzen Sonnenauf- und -untergang From e9cb5fd3740ede7b535544bd81e89c736afee1e7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 21 Feb 2017 16:20:42 +0100 Subject: [PATCH 235/244] WIP sleep in a week chart Displays minutes which is confusing Only displays deeps sleep (no idea why) Is green (which is also confusing) --- .../charts/AbstractWeekChartFragment.java | 263 ++++++++++++++++++ .../activities/charts/ActivityAnalysis.java | 6 +- .../activities/charts/ChartsActivity.java | 16 +- .../charts/WeekSleepChartFragment.java | 34 +++ .../charts/WeekStepsChartFragment.java | 259 +---------------- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 322 insertions(+), 258 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java new file mode 100644 index 00000000..478437bd --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java @@ -0,0 +1,263 @@ +package nodomain.freeyourgadget.gadgetbridge.activities.charts; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; + + +public abstract class AbstractWeekChartFragment extends AbstractChartFragment { + protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class); + + private Locale mLocale; + private int mTargetValue = 0; + + private PieChart mTodayPieChart; + private BarChart mWeekChart; + + @Override + protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) { + Calendar day = Calendar.getInstance(); + day.setTime(chartsHost.getEndDate()); + //NB: we could have omitted the day, but this way we can move things to the past easily + DayData dayData = refreshDayPie(db, day, device); + DefaultChartsData weekBeforeData = refreshWeekBeforeData(db, mWeekChart, day, device); + + return new MyChartsData(dayData, weekBeforeData); + } + + @Override + protected void updateChartsnUIThread(ChartsData chartsData) { + MyChartsData mcd = (MyChartsData) chartsData; + +// setupLegend(mWeekChart); + mTodayPieChart.setCenterText(NumberFormat.getNumberInstance(mLocale).format(mcd.getDayData().totalValue)); + mTodayPieChart.setData(mcd.getDayData().data); + + mWeekChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317 + mWeekChart.setData(mcd.getWeekBeforeData().getData()); + mWeekChart.getLegend().setEnabled(false); + mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter()); + } + + @Override + protected void renderCharts() { + mWeekChart.invalidate(); + mTodayPieChart.invalidate(); + } + + private DefaultChartsData refreshWeekBeforeData(DBHandler db, BarChart barChart, Calendar day, GBDevice device) { + + ActivityAnalysis analysis = new ActivityAnalysis(); + + day = (Calendar) day.clone(); // do not modify the caller's argument + day.add(Calendar.DATE, -7); + List entries = new ArrayList<>(); + ArrayList labels = new ArrayList(); + + for (int counter = 0; counter < 7; counter++) { + entries.add(new BarEntry(counter, getTotalForSamples(getSamplesOfDay(db, day, device)))); + labels.add(day.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, mLocale)); + day.add(Calendar.DATE, 1); + } + + BarDataSet set = new BarDataSet(entries, ""); + set.setColor(akActivity.color); + + BarData barData = new BarData(set); + barData.setValueTextColor(Color.GRAY); //prevent tearing other graph elements with the black text. Another approach would be to hide the values cmpletely with data.setDrawValues(false); + + LimitLine target = new LimitLine(mTargetValue); + barChart.getAxisLeft().removeAllLimitLines(); + barChart.getAxisLeft().addLimitLine(target); + + return new DefaultChartsData(barData, new PreformattedXIndexLabelFormatter(labels)); + } + + + private DayData refreshDayPie(DBHandler db, Calendar day, GBDevice device) { + + int totalValue = getTotalForSamples(getSamplesOfDay(db, day, device)); + + PieData data = new PieData(); + List entries = new ArrayList<>(); + List colors = new ArrayList<>(); + + entries.add(new PieEntry(totalValue, "")); //we don't want labels on the pie chart + colors.add(akActivity.color); + + if (totalValue < mTargetValue) { + entries.add(new PieEntry((mTargetValue - totalValue))); //we don't want labels on the pie chart + colors.add(Color.GRAY); + } + + PieDataSet set = new PieDataSet(entries, ""); + set.setColors(colors); + data.setDataSet(set); + //this hides the values (numeric) added to the set. These would be shown aside the strings set with addXValue above + data.setDrawValues(false); + + return new DayData(data, totalValue); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mLocale = getResources().getConfiguration().locale; + + View rootView = inflater.inflate(R.layout.fragment_weeksteps_chart, container, false); + + int goal = getGoal(); + if (goal >= 0) { + mTargetValue = goal; + } + + mTodayPieChart = (PieChart) rootView.findViewById(R.id.todaystepschart); + mWeekChart = (BarChart) rootView.findViewById(R.id.weekstepschart); + + setupWeekChart(); + setupTodayPieChart(); + + // refresh immediately instead of use refreshIfVisible(), for perceived performance + refresh(); + + return rootView; + } + + private void setupTodayPieChart() { + mTodayPieChart.setBackgroundColor(BACKGROUND_COLOR); + mTodayPieChart.getDescription().setTextColor(DESCRIPTION_COLOR); + mTodayPieChart.getDescription().setText(getContext().getString(R.string.weeksteps_today_steps_description, String.valueOf(mTargetValue))); +// mTodayPieChart.setNoDataTextDescription(""); + mTodayPieChart.setNoDataText(""); + mTodayPieChart.getLegend().setEnabled(false); +// setupLegend(mTodayPieChart); + } + + private void setupWeekChart() { + mWeekChart.setBackgroundColor(BACKGROUND_COLOR); + mWeekChart.getDescription().setTextColor(DESCRIPTION_COLOR); + mWeekChart.getDescription().setText(""); + mWeekChart.setFitBars(true); + + configureBarLineChartDefaults(mWeekChart); + + XAxis x = mWeekChart.getXAxis(); + x.setDrawLabels(true); + x.setDrawGridLines(false); + x.setEnabled(true); + x.setTextColor(CHART_TEXT_COLOR); + x.setDrawLimitLinesBehindData(true); + x.setPosition(XAxis.XAxisPosition.BOTTOM); + + YAxis y = mWeekChart.getAxisLeft(); + y.setDrawGridLines(false); + y.setDrawTopYLabelEntry(false); + y.setTextColor(CHART_TEXT_COLOR); + y.setDrawZeroLine(true); + y.setSpaceBottom(0); + y.setAxisMinimum(0); + + y.setEnabled(true); + + YAxis yAxisRight = mWeekChart.getAxisRight(); + yAxisRight.setDrawGridLines(false); + yAxisRight.setEnabled(false); + yAxisRight.setDrawLabels(false); + yAxisRight.setDrawTopYLabelEntry(false); + yAxisRight.setTextColor(CHART_TEXT_COLOR); + } + + @Override + protected void setupLegend(Chart chart) { +// List legendColors = new ArrayList<>(1); +// List legendLabels = new ArrayList<>(1); +// legendColors.add(akActivity.color); +// legendLabels.add(getContext().getString(R.string.chart_steps)); +// chart.getLegend().setCustom(legendColors, legendLabels); +// chart.getLegend().setTextColor(LEGEND_TEXT_COLOR); + } + + private List getSamplesOfDay(DBHandler db, Calendar day, GBDevice device) { + int startTs; + int endTs; + + day = (Calendar) day.clone(); // do not modify the caller's argument + day.set(Calendar.HOUR_OF_DAY, 0); + day.set(Calendar.MINUTE, 0); + day.set(Calendar.SECOND, 0); + startTs = (int) (day.getTimeInMillis() / 1000); + + day.set(Calendar.HOUR_OF_DAY, 23); + day.set(Calendar.MINUTE, 59); + day.set(Calendar.SECOND, 59); + endTs = (int) (day.getTimeInMillis() / 1000); + + return getSamples(db, device, startTs, endTs); + } + + @Override + protected List getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) { + return super.getAllSamples(db, device, tsFrom, tsTo); + } + + private static class DayData { + private final PieData data; + private final int totalValue; + + DayData(PieData data, int totalValue) { + this.data = data; + this.totalValue = totalValue; + } + } + + private static class MyChartsData extends ChartsData { + private final DefaultChartsData weekBeforeData; + private final DayData dayData; + + MyChartsData(DayData dayData, DefaultChartsData weekBeforeData) { + this.dayData = dayData; + this.weekBeforeData = weekBeforeData; + } + + DayData getDayData() { + return dayData; + } + + DefaultChartsData getWeekBeforeData() { + return weekBeforeData; + } + } + + abstract int getGoal(); + + abstract int getTotalForSamples(List activitySamples); +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java index 8d0719f2..1d3d3669 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java @@ -7,8 +7,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; -public class ActivityAnalysis { - public ActivityAmounts calculateActivityAmounts(List samples) { +class ActivityAnalysis { + ActivityAmounts calculateActivityAmounts(List samples) { ActivityAmount deepSleep = new ActivityAmount(ActivityKind.TYPE_DEEP_SLEEP); ActivityAmount lightSleep = new ActivityAmount(ActivityKind.TYPE_LIGHT_SLEEP); ActivityAmount notWorn = new ActivityAmount(ActivityKind.TYPE_NOT_WORN); @@ -64,7 +64,7 @@ public class ActivityAnalysis { return result; } - public int calculateTotalSteps(List samples) { + int calculateTotalSteps(List samples) { int totalSteps = 0; for (ActivitySample sample : samples) { int steps = sample.getSteps(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java index 292202cc..2b897d51 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java @@ -56,7 +56,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts private final String mDuration; private TextView durationLabel; - public ShowDurationDialog(String duration, Context context) { + ShowDurationDialog(String duration, Context context) { super(context); mDuration = duration; } @@ -298,7 +298,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts */ public class SectionsPagerAdapter extends AbstractFragmentPagerAdapter { - public SectionsPagerAdapter(FragmentManager fm) { + SectionsPagerAdapter(FragmentManager fm) { super(fm); } @@ -311,8 +311,10 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts case 1: return new SleepChartFragment(); case 2: - return new WeekStepsChartFragment(); + return new WeekSleepChartFragment(); case 3: + return new WeekStepsChartFragment(); + case 4: return new LiveActivityFragment(); } @@ -321,8 +323,8 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts @Override public int getCount() { - // Show 3 total pages. - return 4; + // Show 5 total pages. + return 5; } @Override @@ -333,8 +335,10 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts case 1: return getString(R.string.sleepchart_your_sleep); case 2: - return getString(R.string.weekstepschart_steps_a_week); + return getString(R.string.weeksleepchart_sleep_a_week); case 3: + return getString(R.string.weekstepschart_steps_a_week); + case 4: return getString(R.string.liveactivity_live_activity); } return super.getPageTitle(position); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java new file mode 100644 index 00000000..4582d2b0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java @@ -0,0 +1,34 @@ +package nodomain.freeyourgadget.gadgetbridge.activities.charts; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; + +public class WeekSleepChartFragment extends AbstractWeekChartFragment { + @Override + public String getTitle() { + return getString(R.string.weeksleepchart_sleep_a_week); + } + + @Override + int getGoal() { + return 8 * 60; // FIXME + } + + @Override + int getTotalForSamples(List activitySamples) { + ActivityAnalysis analysis = new ActivityAnalysis(); + ActivityAmounts amounts = analysis.calculateActivityAmounts(activitySamples); + long totalSeconds = 0; + for (ActivityAmount amount : amounts.getAmounts()) { + if ((amount.getActivityKind() & ActivityKind.TYPE_SLEEP) != 0) { + totalSeconds += amount.getTotalSeconds(); + } + } + return (int) (totalSeconds / 60); + } +} 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 3d08ed5e..d4dd6ce8 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 @@ -1,267 +1,30 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts; -import android.graphics.Color; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.charts.Chart; -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.PieDataSet; -import com.github.mikephil.charting.data.PieEntry; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.Calendar; import java.util.List; -import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; - -public class WeekStepsChartFragment extends AbstractChartFragment { - protected static final Logger LOG = LoggerFactory.getLogger(WeekStepsChartFragment.class); - - private Locale mLocale; - private int mTargetSteps = 10000; - - private PieChart mTodayStepsChart; - private BarChart mWeekStepsChart; - - @Override - protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) { - Calendar day = Calendar.getInstance(); - day.setTime(chartsHost.getEndDate()); - //NB: we could have omitted the day, but this way we can move things to the past easily - DaySteps daySteps = refreshDaySteps(db, day, device); - DefaultChartsData weekBeforeStepsData = refreshWeekBeforeSteps(db, mWeekStepsChart, day, device); - - return new MyChartsData(daySteps, weekBeforeStepsData); - } - - @Override - protected void updateChartsnUIThread(ChartsData chartsData) { - MyChartsData mcd = (MyChartsData) chartsData; - -// setupLegend(mWeekStepsChart); - mTodayStepsChart.setCenterText(NumberFormat.getNumberInstance(mLocale).format(mcd.getDaySteps().totalSteps)); - mTodayStepsChart.setData(mcd.getDaySteps().data); - - mWeekStepsChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317 - mWeekStepsChart.setData(mcd.getWeekBeforeStepsData().getData()); - mWeekStepsChart.getLegend().setEnabled(false); - mWeekStepsChart.getXAxis().setValueFormatter(mcd.getWeekBeforeStepsData().getXValueFormatter()); - } - - @Override - protected void renderCharts() { - mWeekStepsChart.invalidate(); - mTodayStepsChart.invalidate(); - } - - private DefaultChartsData refreshWeekBeforeSteps(DBHandler db, BarChart barChart, Calendar day, GBDevice device) { - - ActivityAnalysis analysis = new ActivityAnalysis(); - - day = (Calendar) day.clone(); // do not modify the caller's argument - day.add(Calendar.DATE, -7); - List entries = new ArrayList<>(); - ArrayList labels = new ArrayList(); - - for (int counter = 0; counter < 7; counter++) { - entries.add(new BarEntry(counter, analysis.calculateTotalSteps(getSamplesOfDay(db, day, device)))); - labels.add(day.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, mLocale)); - day.add(Calendar.DATE, 1); - } - - BarDataSet set = new BarDataSet(entries, ""); - set.setColor(akActivity.color); - - BarData barData = new BarData(set); - barData.setValueTextColor(Color.GRAY); //prevent tearing other graph elements with the black text. Another approach would be to hide the values cmpletely with data.setDrawValues(false); - - LimitLine target = new LimitLine(mTargetSteps); - barChart.getAxisLeft().removeAllLimitLines(); - barChart.getAxisLeft().addLimitLine(target); - - return new DefaultChartsData(barData, new PreformattedXIndexLabelFormatter(labels)); - } - - - private DaySteps refreshDaySteps(DBHandler db, Calendar day, GBDevice device) { - ActivityAnalysis analysis = new ActivityAnalysis(); - - int totalSteps = analysis.calculateTotalSteps(getSamplesOfDay(db, day, device)); - - PieData data = new PieData(); - List entries = new ArrayList<>(); - List colors = new ArrayList<>(); - - entries.add(new PieEntry(totalSteps, "")); //we don't want labels on the pie chart - colors.add(akActivity.color); - - if (totalSteps < mTargetSteps) { - entries.add(new PieEntry((mTargetSteps - totalSteps))); //we don't want labels on the pie chart - colors.add(Color.GRAY); - } - - PieDataSet set = new PieDataSet(entries, ""); - set.setColors(colors); - data.setDataSet(set); - //this hides the values (numeric) added to the set. These would be shown aside the strings set with addXValue above - data.setDrawValues(false); - - return new DaySteps(data, totalSteps); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - mLocale = getResources().getConfiguration().locale; - - View rootView = inflater.inflate(R.layout.fragment_weeksteps_chart, container, false); - - GBDevice device = getChartsHost().getDevice(); - if (device != null) { - // TODO: eek, this is device specific! - mTargetSteps = MiBandCoordinator.getFitnessGoal(device.getAddress()); - } - - mTodayStepsChart = (PieChart) rootView.findViewById(R.id.todaystepschart); - mWeekStepsChart = (BarChart) rootView.findViewById(R.id.weekstepschart); - - setupWeekStepsChart(); - setupTodayStepsChart(); - - // refresh immediately instead of use refreshIfVisible(), for perceived performance - refresh(); - - return rootView; - } - +public class WeekStepsChartFragment extends AbstractWeekChartFragment { @Override public String getTitle() { return getString(R.string.weekstepschart_steps_a_week); } - private void setupTodayStepsChart() { - mTodayStepsChart.setBackgroundColor(BACKGROUND_COLOR); - mTodayStepsChart.getDescription().setTextColor(DESCRIPTION_COLOR); - mTodayStepsChart.getDescription().setText(getContext().getString(R.string.weeksteps_today_steps_description, String.valueOf(mTargetSteps))); -// mTodayStepsChart.setNoDataTextDescription(""); - mTodayStepsChart.setNoDataText(""); - mTodayStepsChart.getLegend().setEnabled(false); -// setupLegend(mTodayStepsChart); - } - - private void setupWeekStepsChart() { - mWeekStepsChart.setBackgroundColor(BACKGROUND_COLOR); - mWeekStepsChart.getDescription().setTextColor(DESCRIPTION_COLOR); - mWeekStepsChart.getDescription().setText(""); - mWeekStepsChart.setFitBars(true); - - configureBarLineChartDefaults(mWeekStepsChart); - - XAxis x = mWeekStepsChart.getXAxis(); - x.setDrawLabels(true); - x.setDrawGridLines(false); - x.setEnabled(true); - x.setTextColor(CHART_TEXT_COLOR); - x.setDrawLimitLinesBehindData(true); - x.setPosition(XAxis.XAxisPosition.BOTTOM); - - YAxis y = mWeekStepsChart.getAxisLeft(); - y.setDrawGridLines(false); - y.setDrawTopYLabelEntry(false); - y.setTextColor(CHART_TEXT_COLOR); - y.setDrawZeroLine(true); - y.setSpaceBottom(0); - y.setAxisMinimum(0); - - y.setEnabled(true); - - YAxis yAxisRight = mWeekStepsChart.getAxisRight(); - yAxisRight.setDrawGridLines(false); - yAxisRight.setEnabled(false); - yAxisRight.setDrawLabels(false); - yAxisRight.setDrawTopYLabelEntry(false); - yAxisRight.setTextColor(CHART_TEXT_COLOR); + @Override + int getGoal() { + GBDevice device = getChartsHost().getDevice(); + if (device != null) { + return MiBandCoordinator.getFitnessGoal(device.getAddress()); + } + return -1; } @Override - protected void setupLegend(Chart chart) { -// List legendColors = new ArrayList<>(1); -// List legendLabels = new ArrayList<>(1); -// legendColors.add(akActivity.color); -// legendLabels.add(getContext().getString(R.string.chart_steps)); -// chart.getLegend().setCustom(legendColors, legendLabels); -// chart.getLegend().setTextColor(LEGEND_TEXT_COLOR); - } - - private List getSamplesOfDay(DBHandler db, Calendar day, GBDevice device) { - int startTs; - int endTs; - - day = (Calendar) day.clone(); // do not modify the caller's argument - day.set(Calendar.HOUR_OF_DAY, 0); - day.set(Calendar.MINUTE, 0); - day.set(Calendar.SECOND, 0); - startTs = (int) (day.getTimeInMillis() / 1000); - - day.set(Calendar.HOUR_OF_DAY, 23); - day.set(Calendar.MINUTE, 59); - day.set(Calendar.SECOND, 59); - endTs = (int) (day.getTimeInMillis() / 1000); - - return getSamples(db, device, startTs, endTs); - } - - @Override - protected List getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) { - return super.getAllSamples(db, device, tsFrom, tsTo); - } - - private static class DaySteps { - private final PieData data; - private final int totalSteps; - - public DaySteps(PieData data, int totalSteps) { - this.data = data; - this.totalSteps = totalSteps; - } - } - - private static class MyChartsData extends ChartsData { - private final DefaultChartsData weekBeforeStepsData; - private final DaySteps daySteps; - - public MyChartsData(DaySteps daySteps, DefaultChartsData weekBeforeStepsData) { - this.daySteps = daySteps; - this.weekBeforeStepsData = weekBeforeStepsData; - } - - public DaySteps getDaySteps() { - return daySteps; - } - - public DefaultChartsData getWeekBeforeStepsData() { - return weekBeforeStepsData; - } + int getTotalForSamples(List activitySamples) { + ActivityAnalysis analysis = new ActivityAnalysis(); + return analysis.calculateTotalSteps(activitySamples); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a3619175..efc254db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -277,6 +277,7 @@ Last charge: %s \n Number of charges: %s Your Sleep + Sleep a week Steps a week Your Activity and Sleep Updating Firmware… @@ -409,5 +410,4 @@ 24H AM/PM Alarm Clock - From 337bfa19384486843347f59d2010339e363deb1b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 21 Feb 2017 21:41:21 +0100 Subject: [PATCH 236/244] Handle button presses and log them See #556 --- .../devices/miband/MiBand2Service.java | 2 +- .../devices/miband2/MiBand2Support.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) 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 0b8f5623..fb3e9a10 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 @@ -31,7 +31,7 @@ public class MiBand2Service { public static final UUID UUID_UNKNOWN_CHARACTERISTIC8 = UUID.fromString("00000008-0000-3512-2118-0009af100700"); // service uuid fee1 public static final UUID UUID_CHARACTERISTIC_AUTH = UUID.fromString("00000009-0000-3512-2118-0009af100700"); - public static final UUID UUID_UNKNOWN_CHARACTERISTIC10 = UUID.fromString("00000010-0000-3512-2118-0009af100700"); + public static final UUID UUID_CHARACTERISTIC_10_BUTTON = UUID.fromString("00000010-0000-3512-2118-0009af100700"); public static final int ALERT_LEVEL_NONE = 0; public static final int ALERT_LEVEL_MESSAGE = 1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index 09f7e6a4..6d9a83fc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -273,6 +273,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), enable); builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO), enable); + builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON), enable); BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT); if (heartrateCharacteristic != null) { builder.notify(heartrateCharacteristic, enable); @@ -858,6 +859,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { LOG.info("AUTHENTICATION?? " + characteristicUUID); logMessageContent(characteristic.getValue()); return true; + } else if (MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON.equals(characteristicUUID)) { + handleButtonPressed(characteristic.getValue()); + return true; } else { LOG.info("Unhandled characteristic changed: " + characteristicUUID); logMessageContent(characteristic.getValue()); @@ -865,6 +869,11 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return false; } + private void handleButtonPressed(byte[] value) { + LOG.info("Button pressed: " + value); + logMessageContent(value); + } + private void handleUnknownCharacteristic(byte[] value) { } @@ -887,6 +896,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) { logDate(characteristic.getValue(), status); return true; + } else if (MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON.equals(characteristicUUID)) { + handleButtonPressed(characteristic.getValue()); + return true; } else { LOG.info("Unhandled characteristic read: " + characteristicUUID); logMessageContent(characteristic.getValue()); @@ -1260,6 +1272,13 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { + try { + performInitialized("read characteristic 10") + .read(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON)) + .queue(getQueue()); + } catch (IOException e) { + e.printStackTrace(); + } } @Override From 6834f36ec7103f75db7f4fa93c9be019359b93ff Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 21 Feb 2017 22:52:06 +0100 Subject: [PATCH 237/244] Updated contributors list --- CONTRIBUTORS.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index ca95966b..62fedfc5 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -25,8 +25,9 @@ * Andreas Shimokawa * Carsten Pfeiffer * Daniele Gobbetti -* Julien Pivotto * João Paulo Barraca +* ivanovlev +* Julien Pivotto * Steffen Liebergeld * Lem Dulfo * Sergey Trofimov @@ -35,9 +36,11 @@ * 0nse <0nse@users.noreply.github.com> * Gergely Peidl * Christian Fischer -* Normano64 * 6arms1leg +* Normano64 +* Avamander * Ⲇⲁⲛⲓ Φi +* Yar * xzovy * xphnx * Tarik Sekmen @@ -51,6 +54,7 @@ * Kevin Richter * Kasha * Ivan +* Hasan Ammar * Gilles MOREL * Gilles Émilien MOREL * Chris Perelstein From 437ec6c9b7938513f9fe4b5bb52f9ca586de8c40 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 23 Feb 2017 08:49:55 +0100 Subject: [PATCH 238/244] Minor improvements to the WIP week sleep chart --- .../charts/AbstractWeekChartFragment.java | 12 +++++++++-- .../charts/WeekSleepChartFragment.java | 21 +++++++++++++++++++ .../charts/WeekStepsChartFragment.java | 12 +++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java index 478437bd..4dec8275 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java @@ -18,6 +18,7 @@ import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.IValueFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,7 +91,8 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { } BarDataSet set = new BarDataSet(entries, ""); - set.setColor(akActivity.color); + set.setColor(getMainColor()); + set.setValueFormatter(getFormatter()); BarData barData = new BarData(set); barData.setValueTextColor(Color.GRAY); //prevent tearing other graph elements with the black text. Another approach would be to hide the values cmpletely with data.setDrawValues(false); @@ -112,7 +114,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { List colors = new ArrayList<>(); entries.add(new PieEntry(totalValue, "")); //we don't want labels on the pie chart - colors.add(akActivity.color); + colors.add(getMainColor()); if (totalValue < mTargetValue) { entries.add(new PieEntry((mTargetValue - totalValue))); //we don't want labels on the pie chart @@ -120,6 +122,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { } PieDataSet set = new PieDataSet(entries, ""); + set.setValueFormatter(getFormatter()); set.setColors(colors); data.setDataSet(set); //this hides the values (numeric) added to the set. These would be shown aside the strings set with addXValue above @@ -260,4 +263,9 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { abstract int getGoal(); abstract int getTotalForSamples(List activitySamples); + + abstract IValueFormatter getFormatter(); + + abstract Integer getMainColor(); } + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java index 4582d2b0..98cddf1f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java @@ -1,12 +1,18 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; + import java.util.List; +import java.util.concurrent.TimeUnit; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount; import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; public class WeekSleepChartFragment extends AbstractWeekChartFragment { @Override @@ -31,4 +37,19 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment { } return (int) (totalSeconds / 60); } + + @Override + IValueFormatter getFormatter() { + return new IValueFormatter() { + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return DateTimeUtils.formatDurationHoursMinutes((long) value, TimeUnit.MINUTES); + } + }; + } + + @Override + Integer getMainColor() { + return akLightSleep.color; + } } 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 d4dd6ce8..97a1a9c9 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 @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts; +import com.github.mikephil.charting.formatter.IValueFormatter; + import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; @@ -27,4 +29,14 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment { ActivityAnalysis analysis = new ActivityAnalysis(); return analysis.calculateTotalSteps(activitySamples); } + + @Override + IValueFormatter getFormatter() { + return null; + } + + @Override + Integer getMainColor() { + return akActivity.color; + } } From 49e1b55ad873ca5bae835628c0e55a9501fd3935 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 23 Feb 2017 21:15:57 +0100 Subject: [PATCH 239/244] Format center-text for week-sleep-pie --- .../charts/AbstractWeekChartFragment.java | 13 +++++++------ .../activities/charts/WeekSleepChartFragment.java | 7 ++++++- .../activities/charts/WeekStepsChartFragment.java | 5 +++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java index 4dec8275..57203694 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java @@ -23,7 +23,6 @@ import com.github.mikephil.charting.formatter.IValueFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.text.NumberFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -60,7 +59,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { MyChartsData mcd = (MyChartsData) chartsData; // setupLegend(mWeekChart); - mTodayPieChart.setCenterText(NumberFormat.getNumberInstance(mLocale).format(mcd.getDayData().totalValue)); + mTodayPieChart.setCenterText(mcd.getDayData().centerText); mTodayPieChart.setData(mcd.getDayData().data); mWeekChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317 @@ -128,9 +127,11 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { //this hides the values (numeric) added to the set. These would be shown aside the strings set with addXValue above data.setDrawValues(false); - return new DayData(data, totalValue); + return new DayData(data, formatPieValue(totalValue)); } + protected abstract String formatPieValue(int value); + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -234,11 +235,11 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { private static class DayData { private final PieData data; - private final int totalValue; + private final CharSequence centerText; - DayData(PieData data, int totalValue) { + DayData(PieData data, String centerText) { this.data = data; - this.totalValue = totalValue; + this.centerText = centerText; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java index 98cddf1f..9e85d144 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java @@ -38,12 +38,17 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment { return (int) (totalSeconds / 60); } + @Override + protected String formatPieValue(int value) { + return DateTimeUtils.formatDurationHoursMinutes((long) value, TimeUnit.MINUTES); + } + @Override IValueFormatter getFormatter() { return new IValueFormatter() { @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return DateTimeUtils.formatDurationHoursMinutes((long) value, TimeUnit.MINUTES); + return formatPieValue((int) value); } }; } 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 97a1a9c9..5439f253 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 @@ -30,6 +30,11 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment { return analysis.calculateTotalSteps(activitySamples); } + @Override + protected String formatPieValue(int value) { + return String.valueOf(value); + } + @Override IValueFormatter getFormatter() { return null; From 08080b02bbffb25a2ee4a7b4f6664dae9bf29517 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 23 Feb 2017 22:44:44 +0100 Subject: [PATCH 240/244] Drop support for legacy (pre 0.12.0) database --- .../gadgetbridge/GBApplication.java | 11 +- .../activities/DbManagementActivity.java | 92 +--------- .../activities/OnboardingActivity.java | 78 --------- .../database/ActivityDatabaseHandler.java | 105 ------------ .../gadgetbridge/database/DBConstants.java | 18 -- .../gadgetbridge/database/DBHelper.java | 160 ------------------ .../schema/ActivityDBCreationScript.java | 27 --- .../database/schema/ActivityDBUpdate_4.java | 31 ---- .../database/schema/ActivityDBUpdate_6.java | 27 --- .../database/schema/ActivityDBUpdate_7.java | 8 - .../service/DeviceCommunicationService.java | 15 -- .../res/layout/activity_db_management.xml | 16 -- .../main/res/layout/activity_onboarding.xml | 31 ---- app/src/main/res/values/strings.xml | 19 +-- 14 files changed, 6 insertions(+), 632 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/OnboardingActivity.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_4.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java delete mode 100644 app/src/main/res/layout/activity_onboarding.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index ea3abd19..1973e4e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import nodomain.freeyourgadget.gadgetbridge.database.DBConstants; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper; @@ -335,11 +334,7 @@ public class GBApplication extends Application { if (lockHandler != null) { lockHandler.closeDb(); } - DBHelper dbHelper = new DBHelper(context); - boolean result = true; - if (dbHelper.existsDB(DBConstants.DATABASE_NAME)) { - result = getContext().deleteDatabase(DBConstants.DATABASE_NAME); - } + boolean result = deleteOldActivityDatabase(context); result &= getContext().deleteDatabase(DATABASE_NAME); return result; } @@ -352,8 +347,8 @@ public class GBApplication extends Application { public static synchronized boolean deleteOldActivityDatabase(Context context) { DBHelper dbHelper = new DBHelper(context); boolean result = true; - if (dbHelper.existsDB(DBConstants.DATABASE_NAME)) { - result = getContext().deleteDatabase(DBConstants.DATABASE_NAME); + if (dbHelper.existsDB("ActivityDatabase")) { + result = getContext().deleteDatabase("ActivityDatabase"); } return result; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java index 07e0d22a..a155c730 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java @@ -1,11 +1,9 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.app.AlertDialog; -import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.IntentFilter; import android.database.sqlite.SQLiteOpenHelper; -import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.view.MenuItem; @@ -18,16 +16,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.util.Collections; -import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAdapter; -import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -37,7 +30,6 @@ public class DbManagementActivity extends GBActivity { private Button exportDBButton; private Button importDBButton; - private Button importOldActivityDataButton; private Button deleteOldActivityDBButton; private Button deleteDBButton; private TextView dbPath; @@ -68,22 +60,7 @@ public class DbManagementActivity extends GBActivity { } }); - boolean hasOldDB = hasOldActivityDatabase(); - int oldDBVisibility = hasOldDB ? View.VISIBLE : View.GONE; - - View oldDBTitle = findViewById(R.id.mergeOldActivityDataTitle); - oldDBTitle.setVisibility(oldDBVisibility); - View oldDBText = findViewById(R.id.mergeOldActivityDataText); - oldDBText.setVisibility(oldDBVisibility); - - importOldActivityDataButton = (Button) findViewById(R.id.mergeOldActivityData); - importOldActivityDataButton.setVisibility(oldDBVisibility); - importOldActivityDataButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mergeOldActivityDbContents(); - } - }); + int oldDBVisibility = hasOldActivityDatabase() ? View.VISIBLE : View.GONE; deleteOldActivityDBButton = (Button) findViewById(R.id.deleteOldActivityDB); deleteOldActivityDBButton.setVisibility(oldDBVisibility); @@ -104,7 +81,7 @@ public class DbManagementActivity extends GBActivity { } private boolean hasOldActivityDatabase() { - return new DBHelper(this).getOldActivityDatabaseHandler() != null; + return new DBHelper(this).existsDB("ActivityDatabase"); } private String getExternalPath() { @@ -156,67 +133,6 @@ public class DbManagementActivity extends GBActivity { .show(); } - private void mergeOldActivityDbContents() { - final DBHelper helper = new DBHelper(getBaseContext()); - final ActivityDatabaseHandler oldHandler = helper.getOldActivityDatabaseHandler(); - if (oldHandler == null) { - GB.toast(this, getString(R.string.dbmanagementactivity_no_old_activitydatabase_found), Toast.LENGTH_LONG, GB.ERROR); - return; - } - selectDeviceForMergingActivityDatabaseInto(new DeviceSelectionCallback() { - @Override - public void invoke(final GBDevice device) { - if (device == null) { - GB.toast(DbManagementActivity.this, getString(R.string.dbmanagementactivity_no_connected_device), Toast.LENGTH_LONG, GB.ERROR); - return; - } - try (DBHandler targetHandler = GBApplication.acquireDB()) { - final ProgressDialog progress = ProgressDialog.show(DbManagementActivity.this, getString(R.string.dbmanagementactivity_merging_activity_data_title), getString(R.string.dbmanagementactivity_please_wait_while_merging), true, false); - new AsyncTask() { - @Override - protected Object doInBackground(Object[] params) { - helper.importOldDb(oldHandler, device, targetHandler); - if (!isFinishing() && !isDestroyed()) { - progress.dismiss(); - } - return null; - } - }.execute((Object[]) null); - } catch (Exception ex) { - GB.toast(DbManagementActivity.this, getString(R.string.dbmanagementactivity_error_importing_old_activity_data), Toast.LENGTH_LONG, GB.ERROR, ex); - } - } - }); - } - - private void selectDeviceForMergingActivityDatabaseInto(final DeviceSelectionCallback callback) { - GBDevice connectedDevice = ((GBApplication)getApplication()).getDeviceManager().getSelectedDevice(); - if (connectedDevice == null) { - callback.invoke(null); - return; - } - final List availableDevices = Collections.singletonList(connectedDevice); - GBDeviceAdapter adapter = new GBDeviceAdapter(getBaseContext(), availableDevices); - - new AlertDialog.Builder(this) - .setCancelable(true) - .setTitle(R.string.dbmanagementactivity_associate_old_data_with_device) - .setAdapter(adapter, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - GBDevice device = availableDevices.get(which); - callback.invoke(device); - } - }) - .setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // ignore, just return - } - }) - .show(); - } - private void deleteActivityDatabase() { new AlertDialog.Builder(this) .setCancelable(true) @@ -271,8 +187,4 @@ public class DbManagementActivity extends GBActivity { } return super.onOptionsItemSelected(item); } - - public interface DeviceSelectionCallback { - void invoke(GBDevice device); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/OnboardingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/OnboardingActivity.java deleted file mode 100644 index 043c3741..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/OnboardingActivity.java +++ /dev/null @@ -1,78 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.activities; - -import android.app.ProgressDialog; -import android.os.AsyncTask; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; - -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.util.GB; - -public class OnboardingActivity extends GBActivity { - - private Button importOldActivityDataButton; - private TextView importOldActivityDataText; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_onboarding); - - Bundle extras = getIntent().getExtras(); - - GBDevice device; - if (extras != null) { - device = extras.getParcelable(GBDevice.EXTRA_DEVICE); - } else { - throw new IllegalArgumentException("Must provide a device when invoking this activity"); - } - - importOldActivityDataText = (TextView) findViewById(R.id.textview_import_old_activitydata); - importOldActivityDataText.setText(String.format(getString(R.string.import_old_db_information), device.getName())); - importOldActivityDataButton = (Button) findViewById(R.id.button_import_old_activitydata); - final GBDevice finalDevice = device; - importOldActivityDataButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mergeOldActivityDbContents(finalDevice); - } - }); - } - - private void mergeOldActivityDbContents(final GBDevice device) { - if (device == null) { - return; - } - - final DBHelper helper = new DBHelper(getBaseContext()); - final ActivityDatabaseHandler oldHandler = helper.getOldActivityDatabaseHandler(); - if (oldHandler == null) { - GB.toast(this, "No old activity database found, nothing to import.", Toast.LENGTH_LONG, GB.ERROR); - return; - } - - try (DBHandler targetHandler = GBApplication.acquireDB()) { - final ProgressDialog progress = ProgressDialog.show(OnboardingActivity.this, "Merging Activity Data", "Please wait while merging your activity data...", true, false); - new AsyncTask() { - @Override - protected Object doInBackground(Object[] params) { - helper.importOldDb(oldHandler, device, targetHandler); - progress.dismiss(); - finish(); - return null; - } - }.execute((Object[]) null); - } catch (Exception ex) { - GB.toast(OnboardingActivity.this, "Error importing old activity data into new database.", Toast.LENGTH_LONG, GB.ERROR, ex); - } - } - -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java deleted file mode 100644 index 221cf410..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ /dev/null @@ -1,105 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.database; - -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.widget.Toast; - -import java.io.File; - -import nodomain.freeyourgadget.gadgetbridge.database.schema.ActivityDBCreationScript; -import nodomain.freeyourgadget.gadgetbridge.database.schema.SchemaMigration; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.util.GB; - -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.DATABASE_NAME; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; - -/** - * @deprecated can be removed entirely, only used for backwards compatibility - */ -public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandler { - - private static final int DATABASE_VERSION = 7; - private static final String UPDATER_CLASS_NAME_PREFIX = "ActivityDBUpdate_"; - private final Context context; - - public ActivityDatabaseHandler(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - this.context = context; - } - - @Override - public void onCreate(SQLiteDatabase db) { - try { - ActivityDBCreationScript script = new ActivityDBCreationScript(); - script.createSchema(db); - } catch (RuntimeException ex) { - GB.toast("Error creating database.", Toast.LENGTH_SHORT, GB.ERROR, ex); - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - new SchemaMigration(UPDATER_CLASS_NAME_PREFIX).onUpgrade(db, oldVersion, newVersion); - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - new SchemaMigration(UPDATER_CLASS_NAME_PREFIX).onDowngrade(db, oldVersion, newVersion); - } - - @Override - public SQLiteDatabase getDatabase() { - return super.getWritableDatabase(); - } - - @Override - public void closeDb() { - } - - @Override - public void openDb() { - } - - @Override - public SQLiteOpenHelper getHelper() { - return this; - } - - public Context getContext() { - return context; - } - - public boolean hasContent() { - File dbFile = getContext().getDatabasePath(getDatabaseName()); - if (dbFile == null || !dbFile.exists()) { - return false; - } - - try { - try (SQLiteDatabase db = this.getReadableDatabase()) { - try (Cursor cursor = db.query(TABLE_GBACTIVITYSAMPLES, new String[]{KEY_TIMESTAMP}, null, null, null, null, null, "1")) { - return cursor.moveToFirst(); - } - } - } catch (Exception ex) { - // can't expect anything - GB.log("Error looking for old activity data: " + ex.getMessage(), GB.ERROR, ex); - return false; - } - } - - @Override - public DaoSession getDaoSession() { - throw new UnsupportedOperationException(); - } - - @Override - public DaoMaster getDaoMaster() { - throw new UnsupportedOperationException(); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java deleted file mode 100644 index 671b5af3..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java +++ /dev/null @@ -1,18 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.database; - -/** - * TODO: Legacy, can be removed once migration support for old ActivityDatabase is removed - * @deprecated only for backwards compatibility - */ -public class DBConstants { - public static final String DATABASE_NAME = "ActivityDatabase"; - - public static final String TABLE_GBACTIVITYSAMPLES = "GBActivitySamples"; - - public static final String KEY_TIMESTAMP = "timestamp"; - public static final String KEY_PROVIDER = "provider"; - public static final String KEY_INTENSITY = "intensity"; - public static final String KEY_STEPS = "steps"; - public static final String KEY_CUSTOM_SHORT = "customShort"; - public static final String KEY_TYPE = "type"; -} 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 35f0efbf..c6d894f9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -6,7 +6,6 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,7 +13,6 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -26,11 +24,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.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleMisfitSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.ActivityDescription; import nodomain.freeyourgadget.gadgetbridge.entities.ActivityDescriptionDao; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; @@ -38,29 +32,18 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributesDao; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceDao; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlay; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.entities.Tag; import nodomain.freeyourgadget.gadgetbridge.entities.TagDao; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.entities.UserDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; -import nodomain.freeyourgadget.gadgetbridge.util.GB; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_INTENSITY; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TYPE; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; /** * Provides utiliy access to some common entities, so you won't need to use @@ -547,149 +530,6 @@ public class DBHelper { return tag; } - /** - * Returns the old activity database handler if there is any content in that - * db, or null otherwise. - * - * @return the old activity db handler or null - */ - @Nullable - public ActivityDatabaseHandler getOldActivityDatabaseHandler() { - ActivityDatabaseHandler handler = new ActivityDatabaseHandler(context); - if (handler.hasContent()) { - return handler; - } - return null; - } - - public void importOldDb(ActivityDatabaseHandler oldDb, GBDevice targetDevice, DBHandler targetDBHandler) { - DaoSession tempSession = targetDBHandler.getDaoMaster().newSession(); - try { - importActivityDatabase(oldDb, targetDevice, tempSession); - } finally { - tempSession.clear(); - } - } - - private boolean isEmpty(DaoSession session) { - long totalSamplesCount = session.getMiBandActivitySampleDao().count(); - totalSamplesCount += session.getPebbleHealthActivitySampleDao().count(); - return totalSamplesCount == 0; - } - - private void importActivityDatabase(ActivityDatabaseHandler oldDbHandler, GBDevice targetDevice, DaoSession session) { - try (SQLiteDatabase oldDB = oldDbHandler.getReadableDatabase()) { - User user = DBHelper.getUser(session); - for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) { - if (coordinator.supports(targetDevice)) { - AbstractSampleProvider sampleProvider = (AbstractSampleProvider) coordinator.getSampleProvider(targetDevice, session); - importActivitySamples(oldDB, targetDevice, session, sampleProvider, user); - break; - } - } - } - } - - private void importActivitySamples(SQLiteDatabase fromDb, GBDevice targetDevice, DaoSession targetSession, AbstractSampleProvider sampleProvider, User user) { - if (sampleProvider instanceof PebbleMisfitSampleProvider) { - GB.toast(context, "Migration of old Misfit data is not supported!", Toast.LENGTH_LONG, GB.WARN); - return; - } - - String order = "timestamp"; - final String where = "provider=" + sampleProvider.getID(); - - boolean convertActivityTypeToRange = false; - int currentTypeRun, previousTypeRun, currentTimeStamp, currentTypeStartTimeStamp, currentTypeEndTimeStamp; - List overlayList = new ArrayList<>(); - - final int BATCH_SIZE = 100000; // 100.000 samples = rougly 20 MB per batch - List newSamples; - if (sampleProvider instanceof PebbleHealthSampleProvider) { - convertActivityTypeToRange = true; - previousTypeRun = ActivitySample.NOT_MEASURED; - currentTypeStartTimeStamp = -1; - currentTypeEndTimeStamp = -1; - - } else { - previousTypeRun = currentTypeStartTimeStamp = currentTypeEndTimeStamp = 0; - } - try (Cursor cursor = fromDb.query(TABLE_GBACTIVITYSAMPLES, null, where, null, null, null, order)) { - int colTimeStamp = cursor.getColumnIndex(KEY_TIMESTAMP); - int colIntensity = cursor.getColumnIndex(KEY_INTENSITY); - int colSteps = cursor.getColumnIndex(KEY_STEPS); - int colType = cursor.getColumnIndex(KEY_TYPE); - int colCustomShort = cursor.getColumnIndex(KEY_CUSTOM_SHORT); - long deviceId = DBHelper.getDevice(targetDevice, targetSession).getId(); - long userId = user.getId(); - newSamples = new ArrayList<>(Math.min(BATCH_SIZE, cursor.getCount())); - while (cursor.moveToNext()) { - T newSample = sampleProvider.createActivitySample(); - newSample.setProvider(sampleProvider); - newSample.setUserId(userId); - newSample.setDeviceId(deviceId); - currentTimeStamp = cursor.getInt(colTimeStamp); - newSample.setTimestamp(currentTimeStamp); - newSample.setRawIntensity(getNullableInt(cursor, colIntensity, ActivitySample.NOT_MEASURED)); - currentTypeRun = getNullableInt(cursor, colType, ActivitySample.NOT_MEASURED); - newSample.setRawKind(currentTypeRun); - if (convertActivityTypeToRange) { - //at the beginning there is no start timestamp - if (currentTypeStartTimeStamp == -1) { - currentTypeStartTimeStamp = currentTypeEndTimeStamp = currentTimeStamp; - previousTypeRun = currentTypeRun; - } - - if (currentTypeRun != previousTypeRun) { - //we used not to store the last sample, now we do the opposite and we need to round up - currentTypeEndTimeStamp = currentTimeStamp; - //if the Type has changed, the run has ended. Only store light and deep sleep data - if (previousTypeRun == 4) { - overlayList.add(new PebbleHealthActivityOverlay(currentTypeStartTimeStamp, currentTypeEndTimeStamp, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), deviceId, userId, null)); - } else if (previousTypeRun == 5) { - overlayList.add(new PebbleHealthActivityOverlay(currentTypeStartTimeStamp, currentTypeEndTimeStamp, sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP), deviceId, userId, null)); - } - currentTypeStartTimeStamp = currentTimeStamp; - previousTypeRun = currentTypeRun; - } else { - //just expand the run - currentTypeEndTimeStamp = currentTimeStamp; - } - - } - newSample.setSteps(getNullableInt(cursor, colSteps, ActivitySample.NOT_MEASURED)); - if (colCustomShort > -1) { - newSample.setHeartRate(getNullableInt(cursor, colCustomShort, ActivitySample.NOT_MEASURED)); - } else { - newSample.setHeartRate(ActivitySample.NOT_MEASURED); - } - newSamples.add(newSample); - - if ((newSamples.size() % BATCH_SIZE) == 0) { - sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); - targetSession.clear(); - newSamples.clear(); - } - } - // and insert the remaining samples - if (!newSamples.isEmpty()) { - sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); - } - // store the overlay records - if (!overlayList.isEmpty()) { - PebbleHealthActivityOverlayDao overlayDao = targetSession.getPebbleHealthActivityOverlayDao(); - overlayDao.insertOrReplaceInTx(overlayList); - } - } - } - - private int getNullableInt(Cursor cursor, int columnIndex, int defaultValue) { - if (cursor.isNull(columnIndex)) { - return defaultValue; - } - return cursor.getInt(columnIndex); - } - public static void clearSession() { try (DBHandler dbHandler = GBApplication.acquireDB()) { DaoSession session = dbHandler.getDaoSession(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java deleted file mode 100644 index 20c4ac5b..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java +++ /dev/null @@ -1,27 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.database.schema; - -import android.database.sqlite.SQLiteDatabase; - -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; - -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_INTENSITY; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_PROVIDER; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TYPE; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; - -public class ActivityDBCreationScript { - public void createSchema(SQLiteDatabase db) { - String CREATE_GBACTIVITYSAMPLES_TABLE = "CREATE TABLE " + TABLE_GBACTIVITYSAMPLES + " (" - + KEY_TIMESTAMP + " INT," - + KEY_PROVIDER + " TINYINT," - + KEY_INTENSITY + " SMALLINT," - + KEY_STEPS + " TINYINT," - + KEY_TYPE + " TINYINT," - + KEY_CUSTOM_SHORT + " INT," - + " PRIMARY KEY (" + KEY_TIMESTAMP + "," + KEY_PROVIDER + ") ON CONFLICT REPLACE)" + DBHelper.getWithoutRowId(); - db.execSQL(CREATE_GBACTIVITYSAMPLES_TABLE); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_4.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_4.java deleted file mode 100644 index c27b3dc7..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_4.java +++ /dev/null @@ -1,31 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.database.schema; - -import android.database.sqlite.SQLiteDatabase; - -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; - -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; - -/** - * Upgrade and downgrade with DB versions <= 5 is not supported. - * Just recreates the default schema. Those GB versions may or may not - * work with that, but this code will probably not create a DB for them - * anyway. - */ -public class ActivityDBUpdate_4 extends ActivityDBCreationScript implements DBUpdateScript { - @Override - public void upgradeSchema(SQLiteDatabase db) { - recreateSchema(db); - } - - @Override - public void downgradeSchema(SQLiteDatabase db) { - recreateSchema(db); - } - - private void recreateSchema(SQLiteDatabase db) { - DBHelper.dropTable(TABLE_GBACTIVITYSAMPLES, db); - createSchema(db); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java deleted file mode 100644 index b6d157c9..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java +++ /dev/null @@ -1,27 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.database.schema; - -import android.database.sqlite.SQLiteDatabase; - -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; - -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; - -/** - * Adds a column "customShort" to the table "GBActivitySamples" - */ -public class ActivityDBUpdate_6 implements DBUpdateScript { - @Override - public void upgradeSchema(SQLiteDatabase db) { - if (!DBHelper.existsColumn(TABLE_GBACTIVITYSAMPLES, KEY_CUSTOM_SHORT, db)) { - String ADD_COLUMN_CUSTOM_SHORT = "ALTER TABLE " + TABLE_GBACTIVITYSAMPLES + " ADD COLUMN " - + KEY_CUSTOM_SHORT + " INT;"; - db.execSQL(ADD_COLUMN_CUSTOM_SHORT); - } - } - - @Override - public void downgradeSchema(SQLiteDatabase db) { - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java deleted file mode 100644 index c7077451..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java +++ /dev/null @@ -1,8 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.database.schema; - -/** - * Bugfix for users who installed 0.8.1 cleanly, i.e. without any previous - * database. Perform Update script 6 again. - */ -public class ActivityDBUpdate_7 extends ActivityDBUpdate_6 { -} 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 d88594e9..cae037f7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -23,7 +23,6 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.activities.OnboardingActivity; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; @@ -40,7 +39,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; @@ -195,20 +193,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere if (device.isInitialized()) { try (DBHandler dbHandler = GBApplication.acquireDB()) { DaoSession session = dbHandler.getDaoSession(); - boolean askForDBMigration = false; - if (DBHelper.findDevice(device, session) == null && device.getType() != DeviceType.VIBRATISSIMO && (device.getType() != DeviceType.LIVEVIEW)) { - 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); - startIntent.putExtra(GBDevice.EXTRA_DEVICE, device); - startActivity(startIntent); - } - } } catch (Exception ignore) { } } diff --git a/app/src/main/res/layout/activity_db_management.xml b/app/src/main/res/layout/activity_db_management.xml index feb005a4..0bf641cc 100644 --- a/app/src/main/res/layout/activity_db_management.xml +++ b/app/src/main/res/layout/activity_db_management.xml @@ -31,7 +31,6 @@ android:layout_height="wrap_content" android:layout_weight="1" - android:singleLine="false" android:text="Export DB" />