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 001/100] 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 f69bb4536..8cfe6a90d 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 d1cf8e251..b8848ebe0 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 c3704ffec..a944fa493 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 002/100] 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 8cfe6a90d..d0f450fa5 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 a944fa493..c93a4f654 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 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 003/100] 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 e6cb8ddf8..2905d45fc 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 d0f450fa5..9f82d13db 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 b8848ebe0..3de594b62 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 000000000..eec2fee8d --- /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 a26f6be2c..000000000 --- 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 000000000..cc3afc5fc --- /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 000000000..1c42a1d8f --- /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 000000000..5e363d557 --- /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 000000000..839c08825 --- /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 000000000..cdc18307d --- /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 000000000..a197bd4c4 --- /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 bed5d581e..000000000 --- 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 c93a4f654..653503050 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 004/100] 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 eec2fee8d..61ce6a2ea 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 005/100] 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 b033c7411..130ea7d58 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 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 006/100] 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 9f82d13db..70546e87b 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 61ce6a2ea..3fe16e41f 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 cdc18307d..33901ddd4 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 a197bd4c4..fe8ebffcb 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 653503050..1deda41de 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 007/100] 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 33901ddd4..a1bd3e4e7 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 008/100] 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 b0481101c..c6a8603f3 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 4c9eb9eee..8a3750ec3 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 9a898f6f6..b7dbdd42a 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 a1bd3e4e7..997a09edc 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 fe8ebffcb..fcb7f6a49 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 1deda41de..54fd18c5a 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 009/100] 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 7e854d68e..d2c96471e 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 c6a8603f3..3e3b9ad15 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 8a3750ec3..d827976d6 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 b7dbdd42a..97ea0294f 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 997a09edc..0d8e6c34e 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 fcb7f6a49..5f9603929 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 010/100] 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 54fd18c5a..da76d7c94 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 011/100] 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 0d8e6c34e..0bb6609c6 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 5f9603929..caf9435fd 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 012/100] 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 caf9435fd..e564b4b19 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 da76d7c94..17e9e666b 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 013/100] 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 3e3b9ad15..5f9e310dc 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 e564b4b19..c40abfaea 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 014/100] 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 c40abfaea..93715fce7 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 015/100] 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 000000000..d58aaf041 --- /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 407b79fe4..8223657d8 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 016/100] 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 000000000..756c7e529 --- /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 8223657d8..e8c16908b 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 017/100] 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 a349a59d7..8da545b70 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 018/100] 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 b06593832..ea3abd190 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 8da545b70..51e821aaa 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 6430085cc..522d0bade 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 b721a88f7..92a17e129 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 019/100] 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 d3f54ee7e..fd8637bb4 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 883cc13d8..e13552d92 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 020/100] 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 042c7e509..0e0ae701b 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 178b499ed..29dad5938 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 91b105e48..118e56e01 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 d58aaf041..bcb776795 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 16c96afc7..25d995ab1 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 b3cfce66e..d926e8d8d 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 756c7e529..f26b0704b 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 021/100] 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 812454a48..1767c15ee 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 022/100] 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 000000000..9c4997600 --- /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 023/100] 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 5d5fe957f..a22fcc189 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 024/100] 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 aa1ae6d9c..b076d0e8b 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 e985816d1..b6b68af65 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 d9e4591af..51089ee0d 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 025/100] 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 51089ee0d..ee4bc2cb1 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 026/100] 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 ee4bc2cb1..5f0774fc1 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 027/100] 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 b0a40f605..1c613394a 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 ddddedfa8..09222a914 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 028/100] 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 b076d0e8b..8285da4d1 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 f88f5f77e..56087f238 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 522d0bade..522883d9f 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 5f0774fc1..ee5f87fd0 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 92a17e129..43985431d 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 029/100] 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 fe53c32ca..c5919fe9c 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 e8c16908b..fb0016d73 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 030/100] 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 00374072c..6cfaa1ef2 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 130ea7d58..4ad5fa48b 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 361dbe260..949791c7d 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 b536fefbc..f03a58d50 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 f4417fb70..0edf91718 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 437bf5c7f..e46d03dbd 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 be083b070..c92c8736b 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 1971c0735..be1e32fb2 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 b89e9e089..d068c2c55 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 fb0016d73..ede3da7ab 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 b19b4329d..cca5825d1 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 216f2c517..d3df5c13a 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 77a5bc2e3..ca3e3ee90 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 160b855b9..45ba12177 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 031/100] 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 4ad5fa48b..70c026c15 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 032/100] 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 70c026c15..80942dd94 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 033/100] 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 8285da4d1..9980af045 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 ee5f87fd0..8895eea3f 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 034/100] 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 70546e87b..5feaf6d7d 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 3de594b62..8a596d4ce 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 3fe16e41f..9a7fa8c1c 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 0bb6609c6..e28960f98 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 d827976d6..8b39f84f7 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 97ea0294f..f74db8fb9 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 93715fce7..d8e5de5a3 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 17e9e666b..dd229b819 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 035/100] 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 2905d45fc..d70679a1f 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 d8e5de5a3..7b05775db 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 036/100] 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 56087f238..122f6da59 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 037/100] 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 ede3da7ab..25c5cf2a9 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 038/100] 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 d02355211..e16c1bdc6 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 039/100] 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 9980af045..c2ae741c8 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 8895eea3f..814d7e279 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 040/100] 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 53585a88f..91b7f3efc 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 041/100] 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 c2ae741c8..1ee0d0da8 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 814d7e279..c54077e4c 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 042/100] 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 25c5cf2a9..0a2d7cde1 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 043/100] 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 0a2d7cde1..eff90ef32 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 044/100] 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 923ee95cc..83e269124 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 eff90ef32..121cbb44c 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 045/100] 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 e1b691f81..ff2b210f5 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 000000000..49574bed7 --- /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 a2b325e35..000000000 --- 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 000000000..bbb5a7377 --- /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 522883d9f..64b880f4c 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 046/100] 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 b6b68af65..7a3175252 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 4689155d7..a674d6b74 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 c54077e4c..34e61a413 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 047/100] 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 64b880f4c..300c6b747 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 048/100] 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 1ee0d0da8..806a2f204 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 c54077e4c..01795f090 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 049/100] 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 300c6b747..e51eea292 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 050/100] 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 3e68b2dfa..946d72ae3 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 766f6c5a2..2f281ce82 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 e51eea292..cabf031ab 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 051/100] 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 a674d6b74..aa1836876 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 052/100] 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 806a2f204..35f3d5123 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 80942dd94..81a417951 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 cabf031ab..0667156fd 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 01795f090..d5f774bdf 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 43985431d..84bba8c31 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 053/100] 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 84bba8c31..c8f1964e1 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 054/100] 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 7a3175252..b6b68af65 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 aa1836876..319303dbc 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 000000000..ae41ed3f0 --- /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 7284db28f..2ed467a6b 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 522883d9f..765cc4612 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 34e61a413..672517f60 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 43985431d..48eb91d87 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 055/100] 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 6dac6eb9a..8544188a5 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 056/100] 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 ff2b210f5..e1b691f81 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 49574bed7..9d965de1f 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 057/100] 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 e1b691f81..ceb2ac0bc 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 9d965de1f..2901a15eb 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 058/100] 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 ae41ed3f0..a9a80aae4 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 8544188a5..d4b9c14bd 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 059/100] 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 a9a80aae4..735dfd1fe 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 d4b9c14bd..8e71c9a0c 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 060/100] 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 1c613394a..1acab3349 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 288bd93d8..80fe82417 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 09222a914..c5daf71f8 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 061/100] 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 4689155d7..3575e8c55 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 062/100] 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 0edf91718..da4fa9a06 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 319303dbc..8335830f7 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 735dfd1fe..48359d09f 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 063/100] 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 8e71c9a0c..6f3a0a33f 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 064/100] 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 50f8ef9d2..a045079ed 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 065/100] 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 121cbb44c..b271a0bf9 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 066/100] 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 35f3d5123..86b321c0a 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 b6b68af65..23646f821 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 d5f774bdf..1d0c0a2bc 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 067/100] 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 48359d09f..59d7aa817 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 068/100] 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 91b7f3efc..dac25b360 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 069/100] 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 86b321c0a..c69264c8f 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 0e0ae701b..8705cb237 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 25d995ab1..6ca629758 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 b271a0bf9..6ef013bb7 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 fe8e91799..99edf2ac1 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 070/100] 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 511775585..3d46d303f 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 071/100] 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 c69264c8f..5abc7e0ba 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 23646f821..17ca06fd4 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 da4fa9a06..b2d9be35f 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 e219e6106..2af54dfc4 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 99edf2ac1..1c84aab98 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 072/100] 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 2af54dfc4..26694f18b 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 073/100] 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 c742e0461..997ad3f11 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 8b39f84f7..4a618bcaa 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 7b05775db..54cb040eb 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 074/100] 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 5feaf6d7d..01e619bf2 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 73072c51b..076a02968 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 000000000..8dd6d8ccb --- /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 075/100] 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 6ef013bb7..14ae5f9e0 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 076/100] 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 8705cb237..b11b96603 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 d926e8d8d..becf1a3c1 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 077/100] 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 076a02968..c410537c6 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 078/100] 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 4b152289a..8b12aa698 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 079/100] 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 8b12aa698..37122ec65 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 becf1a3c1..6d28fa8e3 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 080/100] 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 29dad5938..1ae46cdbc 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 081/100] 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 1ae46cdbc..e45c4a16f 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 37122ec65..0e05d0bf6 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 bcb776795..8a0800375 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 6d28fa8e3..e1f304634 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 082/100] 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 30d0e6318..803632738 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 949791c7d..dea375c63 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 da4fa9a06..b35a9417c 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 083/100] 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 58dccd72b..11fc855e9 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 8eff6f79c..9de950fa5 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 000000000..cfc8ec494 --- /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 084/100] 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 83e269124..f4e6e31e9 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 000000000..6d05dc8c2 --- /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 085/100] 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 e45c4a16f..830e31e45 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 0e05d0bf6..3bfaca480 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 8a0800375..a3aafa599 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 6ca629758..a8fb16caa 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 e1f304634..014a2166f 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 086/100] 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 5abc7e0ba..7d015ea8b 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 17ca06fd4..a86bb12b8 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 1c84aab98..772f7c45d 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 087/100] 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 1acab3349..a6de97f27 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 c5daf71f8..6071e09ee 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 088/100] 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 89424a1ff..2e8e665ee 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 089/100] 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 f4d5ec9f4..98877a833 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 2e8e665ee..878352ab2 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 00b77c060..caeb4e2a6 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 26694f18b..0b53f84a7 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 6b0e10321..ed215a5a2 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 090/100] 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 3696ec5d5..cec517a64 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 091/100] 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 b883fc1cf..8016041e2 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 6f0fb39d5..ef872cf35 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 878352ab2..9f825a7b2 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 2cf3b545c..3fb16918e 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 949791c7d..bee14d7d7 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 f03a58d50..a7be20f89 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 b2d9be35f..a376dfe65 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 f08691e2d..a343c49be 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 c57abb934..0fbd54511 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 092/100] 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 7d015ea8b..3b2aba42d 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 772f7c45d..d8fef0167 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 093/100] 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 01e619bf2..73515ee7f 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 8a596d4ce..8495e6643 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 9a7fa8c1c..23f4090e1 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 c410537c6..4bf02c551 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 0fbd54511..9e2ebb41e 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 094/100] 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 d8fef0167..2d8613556 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 095/100] release time --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b2aba42d..4d756f9d8 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 096/100] 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 1488f46bf..5777b5d9a 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 097/100] 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 d464d0325..92a1a97fd 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 bdc6317c3..bf304a271 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 639a58f72..d32ada664 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 098/100] 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 7b3342ae9..c9f2b09d2 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 0a978d5d3..420112b30 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 6b9178fb7..35f0009e7 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 ef872cf35..346446da0 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 6a50ba0fb..4d3cf3c3d 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 8495e6643..8224a1077 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 338137cef..d68341f40 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 01538e097..3ba9e543f 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 abff3bc15..bf534a351 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 5953113df..316967cb0 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 8f71f2340..0c06672dd 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 099/100] 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 0b53f84a7..88226d4ce 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 100/100] 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 81a417951..745b0b8f7 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();