From 3513a902ae11a9aa1821b833e1b5b2dc6a0a73a9 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 27 Mar 2016 22:13:06 +0200 Subject: [PATCH 001/569] Work towards using greenDAO #206 --- GBDaoGenerator/.gitignore | 2 + GBDaoGenerator/build.gradle | 28 +++++ .../gadgetbridge/daogen/GBDaoGenerator.java | 112 ++++++++++++++++++ app/build.gradle | 6 + settings.gradle | 2 +- 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 GBDaoGenerator/.gitignore create mode 100644 GBDaoGenerator/build.gradle create mode 100644 GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java diff --git a/GBDaoGenerator/.gitignore b/GBDaoGenerator/.gitignore new file mode 100644 index 00000000..81631c69 --- /dev/null +++ b/GBDaoGenerator/.gitignore @@ -0,0 +1,2 @@ +/bin +/build diff --git a/GBDaoGenerator/build.gradle b/GBDaoGenerator/build.gradle new file mode 100644 index 00000000..897609bc --- /dev/null +++ b/GBDaoGenerator/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'java' +//apply plugin: 'maven' +apply plugin:'application' + +archivesBaseName = 'gadgetbridge-daogenerator' +//version = '0.9.2-SNAPSHOT' + +dependencies { + compile 'de.greenrobot:greendao-generator:2.1.0' +} + +sourceSets { + main { + java { + srcDir 'src' + srcDir 'src-gen' + } + } +} + +task (genSources, type: JavaExec) { + main = "nodomain.freeyourgadget.gadgetbridge.daogen.GBDaoGenerator" + classpath = sourceSets.main.runtimeClasspath +} + +artifacts { + archives jar +} diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java new file mode 100644 index 00000000..651b1aa3 --- /dev/null +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 Markus Junginger, greenrobot (http://greenrobot.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nodomain.freeyourgadget.gadgetbridge.daogen; + +import de.greenrobot.daogenerator.DaoGenerator; +import de.greenrobot.daogenerator.Entity; +import de.greenrobot.daogenerator.Property; +import de.greenrobot.daogenerator.Schema; + +/** + * Generates entities and DAOs for the example project DaoExample. + * Automatically run during build. + */ +public class GBDaoGenerator { + + public static final String VALID_FROM_UTC = "validFromUTC"; + public static final String VALID_TO_UTC = "validToUTC"; + + public static void main(String[] args) throws Exception { + Schema schema = new Schema(7, "nodomain.freeyourgadget.gadgetbridge.entities"); + + Entity userAttributes = addUserAttributes(schema); + Entity user = addUserInfo(schema, userAttributes); + + Entity deviceAttributes = addDeviceAttributes(schema); + Entity device = addDevice(schema, deviceAttributes); + + addActivitySample(schema, user, device); + + new DaoGenerator().generateAll(schema, "../app/src/main/gen"); + } + + private static Entity addUserInfo(Schema schema, Entity userAttributes) { + Entity user = schema.addEntity("User"); + user.addIdProperty(); + user.addStringProperty("name").notNull(); + user.addDateProperty("birthday").notNull(); + user.addIntProperty("sex").notNull(); + Property userId = userAttributes.addLongProperty("userId").notNull().getProperty(); + user.addToMany(userAttributes, userId); + + return user; + } + + private static Entity addUserAttributes(Schema schema) { + // additional properties of a user, which may change during the lifetime of a user + // this allows changing attributes while preserving user identity + Entity userAttributes = schema.addEntity("UserAttributes"); + userAttributes.addIdProperty(); + userAttributes.addIntProperty("heightCM").notNull(); + userAttributes.addIntProperty("weightKG").notNull(); + userAttributes.addIntProperty("sleepGoalHPD"); + userAttributes.addIntProperty("stepsGoalSPD"); + userAttributes.addDateProperty(VALID_FROM_UTC); + userAttributes.addDateProperty(VALID_TO_UTC); + + return userAttributes; + } + + private static Entity addDevice(Schema schema, Entity deviceAttributes) { + Entity device = schema.addEntity("Device"); + device.addIdProperty(); + device.addStringProperty("name").notNull(); + device.addStringProperty("manufacturer").notNull(); + device.addStringProperty("identifier").notNull().javaDocGetterAndSetter("The fixed identifier, i.e. MAC address of the device."); + Property deviceId = deviceAttributes.addLongProperty("deviceId").notNull().getProperty(); + device.addToMany(deviceAttributes, deviceId); + + return device; + } + + private static Entity addDeviceAttributes(Schema schema) { + Entity deviceAttributes = schema.addEntity("DeviceAttributes"); + deviceAttributes.addIdProperty(); + deviceAttributes.addStringProperty("firmwareVersion1").notNull(); + deviceAttributes.addStringProperty("firmwareVersion2"); + deviceAttributes.addDateProperty(VALID_FROM_UTC); + deviceAttributes.addDateProperty(VALID_TO_UTC); + + return deviceAttributes; + } + + private static Entity addActivitySample(Schema schema, Entity user, Entity device) { +// public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { + Entity activitySample = schema.addEntity("ActivitySample"); + activitySample.addIdProperty(); + activitySample.addIntProperty("timestamp").notNull(); + activitySample.addIntProperty("intensity").notNull(); + activitySample.addIntProperty("steps").notNull(); + activitySample.addIntProperty("type").notNull(); + activitySample.addIntProperty("customValue").notNull(); + Property userId = activitySample.addLongProperty("userId").getProperty(); + activitySample.addToOne(user, userId); + Property deviceId = activitySample.addLongProperty("deviceId").getProperty(); + activitySample.addToOne(device, deviceId); + + return activitySample; + } +} diff --git a/app/build.gradle b/app/build.gradle index accb84ae..2da01cac 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,6 +53,12 @@ dependencies { compile 'com.github.PhilJay:MPAndroidChart:v2.2.3' compile 'com.github.pfichtner:durationformatter:0.1.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.2' + compile 'de.greenrobot:greendao:2.1.0' +} + +preBuild.dependsOn(":GBDaoGenerator:genSources") +gradle.beforeProject { + preBuild.dependsOn(":GBDaoGenerator:genSources") } check.dependsOn 'findbugs', 'pmd', 'lint' diff --git a/settings.gradle b/settings.gradle index e7b4def4..6579a81e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':app', ':GBDaoGenerator' From 206b0670a4e65c4d839092b682536b0ffe813161 Mon Sep 17 00:00:00 2001 From: Lem Dulfo Date: Sun, 10 Apr 2016 00:18:16 +0800 Subject: [PATCH 002/569] Fixed mainClassName error, fix entity generation --- GBDaoGenerator/build.gradle | 8 +++++--- .../gadgetbridge/daogen/GBDaoGenerator.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/GBDaoGenerator/build.gradle b/GBDaoGenerator/build.gradle index 897609bc..a708acb1 100644 --- a/GBDaoGenerator/build.gradle +++ b/GBDaoGenerator/build.gradle @@ -13,14 +13,16 @@ sourceSets { main { java { srcDir 'src' - srcDir 'src-gen' } } } -task (genSources, type: JavaExec) { - main = "nodomain.freeyourgadget.gadgetbridge.daogen.GBDaoGenerator" +mainClassName = "nodomain.freeyourgadget.gadgetbridge.daogen.GBDaoGenerator" + +task genSources(type: JavaExec) { + main = mainClassName classpath = sourceSets.main.runtimeClasspath + workingDir = '../' } artifacts { diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 651b1aa3..5d1cb457 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -40,7 +40,7 @@ public class GBDaoGenerator { addActivitySample(schema, user, device); - new DaoGenerator().generateAll(schema, "../app/src/main/gen"); + new DaoGenerator().generateAll(schema, "app/src/main/java"); } private static Entity addUserInfo(Schema schema, Entity userAttributes) { From 82b4394b40188cee1ce64644e53acbb15b34d0f0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 10 Apr 2016 21:51:47 +0200 Subject: [PATCH 003/569] Ignore generated entities --- .../nodomain/freeyourgadget/gadgetbridge/entities/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/.gitignore diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/.gitignore b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/.gitignore new file mode 100644 index 00000000..4d5306db --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/.gitignore @@ -0,0 +1 @@ +*.java From a45eacf9b83f1b8709018ae0eb301eb737901e8e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 17 Apr 2016 19:52:51 +0200 Subject: [PATCH 004/569] WIP: schema update, ... #206 --- .../gadgetbridge/daogen/GBDaoGenerator.java | 24 ++++++++++++++----- .../gadgetbridge/GBApplication.java | 17 +++++++++++++ .../gadgetbridge/activities/GBActivity.java | 5 ++++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 5d1cb457..3d14794c 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -38,7 +38,8 @@ public class GBDaoGenerator { Entity deviceAttributes = addDeviceAttributes(schema); Entity device = addDevice(schema, deviceAttributes); - addActivitySample(schema, user, device); + addMiBandActivitySample(schema, user, device); + addPebbleActivitySample(schema, user, device); new DaoGenerator().generateAll(schema, "app/src/main/java"); } @@ -93,20 +94,31 @@ public class GBDaoGenerator { return deviceAttributes; } - private static Entity addActivitySample(Schema schema, Entity user, Entity device) { + private static Entity addMiBandActivitySample(Schema schema, Entity user, Entity device) { // public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { - Entity activitySample = schema.addEntity("ActivitySample"); + Entity activitySample = schema.addEntity("MiBandActivitySample"); + addCommonAcivitySampleProperties(schema, activitySample, user, device); + activitySample.addIntProperty("heartrate"); + return activitySample; + } + + private static Entity addPebbleActivitySample(Schema schema, Entity user, Entity device) { +// public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { + Entity activitySample = schema.addEntity("PebbleActivitySample"); + addCommonAcivitySampleProperties(schema, activitySample, user, device); +// activitySample.addIntProperty("heartrate").notNull(); + return activitySample; + } + + private static void addCommonAcivitySampleProperties(Schema schema, Entity activitySample, Entity user, Entity device) { activitySample.addIdProperty(); activitySample.addIntProperty("timestamp").notNull(); activitySample.addIntProperty("intensity").notNull(); activitySample.addIntProperty("steps").notNull(); activitySample.addIntProperty("type").notNull(); - activitySample.addIntProperty("customValue").notNull(); Property userId = activitySample.addLongProperty("userId").getProperty(); activitySample.addToOne(user, userId); Property deviceId = activitySample.addLongProperty("deviceId").getProperty(); activitySample.addToOne(device, deviceId); - - return activitySample; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index f292133a..2667274b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; import android.os.Build; import android.os.Build.VERSION; import android.preference.PreferenceManager; @@ -25,6 +26,8 @@ import java.util.concurrent.locks.ReentrantLock; import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBConstants; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; @@ -50,6 +53,7 @@ public class GBApplication extends Application { //if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version private static final int CURRENT_PREFS_VERSION = 2; private static LimitedQueue mIDSenderLookup = new LimitedQueue(16); + private static DaoSession daoSession; public static final String ACTION_QUIT = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit"; @@ -99,6 +103,8 @@ public class GBApplication extends Application { // StatusPrinter.print(lc); // Logger logger = LoggerFactory.getLogger(GBApplication.class); + setupDatabase(); + deviceService = createDeviceService(); GB.environment = GBEnvironment.createDeviceEnvironment(); mActivityDatabaseHandler = new ActivityDatabaseHandler(context); @@ -151,6 +157,17 @@ public class GBApplication extends Application { return LoggerFactory.getLogger(GBApplication.class); } + private void setupDatabase() { + DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "test-db", null); + SQLiteDatabase db = helper.getWritableDatabase(); + DaoMaster daoMaster = new DaoMaster(db); + daoSession = daoMaster.newSession(); + } + + public static DaoSession getDaoSession() { + return daoSession; + } + public static Context getContext() { return context; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java index 39c972ea..5eb566b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java @@ -6,6 +6,7 @@ import android.support.v7.app.AppCompatActivity; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; public class GBActivity extends AppCompatActivity { @@ -19,4 +20,8 @@ public class GBActivity extends AppCompatActivity { super.onCreate(savedInstanceState); } + + protected DaoSession getDAOSession() { + return GBApplication.getDaoSession(); + } } From 403a14ce8d4c97a8abac7ec212714da9c0262823 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 29 Apr 2016 22:50:37 +0200 Subject: [PATCH 005/569] Use a different application id until the db refactoring is done This allows installing both versions at the same time and avoids data loss when the database is scrubbed. --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 65c680fd..0e79900b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,7 +11,7 @@ android { buildToolsVersion "23.0.3" defaultConfig { - applicationId "nodomain.freeyourgadget.gadgetbridge" + applicationId "nodomain.freeyourgadget.gadgetbridge.greendao" minSdkVersion 19 targetSdkVersion 23 From b363d08efb03d0c4fc7a097b2ef1423013b09055 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 29 Apr 2016 23:12:30 +0200 Subject: [PATCH 006/569] WIP: a little work towards greendao need to think of how to integrate MiBandActivitySample and PebbleActivitySample into the app. There's GBActivitySample, MiBandSampleProvider, PebbleSampleProvider, etc. --- .../gadgetbridge/GBApplication.java | 6 +- .../database/ActivityDatabaseHandler.java | 42 +-------- .../gadgetbridge/database/DBHandler.java | 5 + .../gadgetbridge/database/DaoHandler.java | 82 ++++++++++++++++ .../database/schema/SchemaMigration.java | 59 ++++++++++++ .../gadgetbridge/impl/GBActivitySample2.java | 94 +++++++++++++++++++ 6 files changed, 247 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DaoHandler.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample2.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 5cdf5fc9..c26013d1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -30,6 +30,7 @@ import ch.qos.logback.core.Appender; import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBConstants; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DaoHandler; import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService; @@ -51,7 +52,7 @@ public class GBApplication extends Application { // Since this class must not log to slf4j, we use plain android.util.Log private static final String TAG = "GBApplication"; private static GBApplication context; - private static ActivityDatabaseHandler mActivityDatabaseHandler; + private static DBHandler mActivityDatabaseHandler; private static final Lock dbLock = new ReentrantLock(); private static DeviceService deviceService; private static SharedPreferences sharedPrefs; @@ -118,7 +119,7 @@ public class GBApplication extends Application { deviceService = createDeviceService(); GB.environment = GBEnvironment.createDeviceEnvironment(); - mActivityDatabaseHandler = new ActivityDatabaseHandler(context); +// mActivityDatabaseHandler = new ActivityDatabaseHandler(context); loadBlackList(); IntentFilter filterLocal = new IntentFilter(); @@ -209,6 +210,7 @@ public class GBApplication extends Application { SQLiteDatabase db = helper.getWritableDatabase(); DaoMaster daoMaster = new DaoMaster(db); daoSession = daoMaster.newSession(); + mActivityDatabaseHandler = new DaoHandler(daoMaster, helper); } public static DaoSession getDaoSession() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index ce9e1ae4..539667af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.schema.ActivityDBCreationScript; +import nodomain.freeyourgadget.gadgetbridge.database.schema.SchemaMigration; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -52,49 +53,12 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - LOG.info("ActivityDatabase: schema upgrade requested from " + oldVersion + " to " + newVersion); - try { - for (int i = oldVersion + 1; i <= newVersion; i++) { - DBUpdateScript updater = getUpdateScript(db, i); - if (updater != null) { - LOG.info("upgrading activity database to version " + i); - updater.upgradeSchema(db); - } - } - LOG.info("activity database is now at version " + newVersion); - } catch (RuntimeException ex) { - GB.toast("Error upgrading database.", Toast.LENGTH_SHORT, GB.ERROR, ex); - throw ex; // reject upgrade - } + new SchemaMigration().onUpgrade(db, oldVersion, newVersion); } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - LOG.info("ActivityDatabase: schema downgrade requested from " + oldVersion + " to " + newVersion); - try { - for (int i = oldVersion; i >= newVersion; i--) { - DBUpdateScript updater = getUpdateScript(db, i); - if (updater != null) { - LOG.info("downgrading activity database to version " + (i - 1)); - updater.downgradeSchema(db); - } - } - LOG.info("activity database is now at version " + newVersion); - } catch (RuntimeException ex) { - GB.toast("Error downgrading database.", Toast.LENGTH_SHORT, GB.ERROR, ex); - throw ex; // reject downgrade - } - } - - private DBUpdateScript getUpdateScript(SQLiteDatabase db, int version) { - try { - Class updateClass = getClass().getClassLoader().loadClass(getClass().getPackage().getName() + ".schema.ActivityDBUpdate_" + version); - return (DBUpdateScript) updateClass.newInstance(); - } catch (ClassNotFoundException e) { - return null; - } catch (InstantiationException | IllegalAccessException e) { - throw new RuntimeException("Error instantiating DBUpdate class for version " + version, e); - } + new SchemaMigration().onDowngrade(db, oldVersion, newVersion); } public void addGBActivitySample(ActivitySample sample) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java index 49dbbd40..b0ad2620 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java @@ -10,6 +10,11 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; public interface DBHandler { + /** + * Closes the database. + */ + void close(); + SQLiteOpenHelper getHelper(); /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DaoHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DaoHandler.java new file mode 100644 index 00000000..6887ac12 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DaoHandler.java @@ -0,0 +1,82 @@ +package nodomain.freeyourgadget.gadgetbridge.database; + +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; + +public class DaoHandler implements DBHandler { + + private final DaoMaster mDaoMaster; + private final SQLiteOpenHelper openHelper; + + public DaoHandler(DaoMaster master, DaoMaster.DevOpenHelper helper) { + mDaoMaster = master; + openHelper = helper; + } + + @Override + public void close() { + getHelper().close(); + } + + @Override + public SQLiteOpenHelper getHelper() { + return openHelper; + } + + @Override + public void release() { + GBApplication.releaseDB(); + } + + @Override + public List getAllActivitySamples(int tsFrom, int tsTo, SampleProvider provider) { + return null; + } + + @Override + public List getActivitySamples(int tsFrom, int tsTo, SampleProvider provider) { + return null; + } + + @Override + public List getSleepSamples(int tsFrom, int tsTo, SampleProvider provider) { + return null; + } + + @Override + public void addGBActivitySample(int timestamp, int provider, int intensity, int steps, int kind, int heartrate) { + + } + + @Override + public void addGBActivitySamples(ActivitySample[] activitySamples) { + + } + + @Override + public SQLiteDatabase getWritableDatabase() { + return mDaoMaster.getDatabase(); + } + + @Override + public void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind, SampleProvider provider) { + + } + + @Override + public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind, SampleProvider provider) { + + } + + @Override + public int fetchLatestTimestamp(SampleProvider provider) { + return 0; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java new file mode 100644 index 00000000..6e1c959b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java @@ -0,0 +1,59 @@ +package nodomain.freeyourgadget.gadgetbridge.database.schema; + +import android.database.sqlite.SQLiteDatabase; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class SchemaMigration { + private static final Logger LOG = LoggerFactory.getLogger(SchemaMigration.class); + + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + LOG.info("ActivityDatabase: schema upgrade requested from " + oldVersion + " to " + newVersion); + try { + for (int i = oldVersion + 1; i <= newVersion; i++) { + DBUpdateScript updater = getUpdateScript(db, i); + if (updater != null) { + LOG.info("upgrading activity database to version " + i); + updater.upgradeSchema(db); + } + } + LOG.info("activity database is now at version " + newVersion); + } catch (RuntimeException ex) { + GB.toast("Error upgrading database.", Toast.LENGTH_SHORT, GB.ERROR, ex); + throw ex; // reject upgrade + } + } + + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + LOG.info("ActivityDatabase: schema downgrade requested from " + oldVersion + " to " + newVersion); + try { + for (int i = oldVersion; i >= newVersion; i--) { + DBUpdateScript updater = getUpdateScript(db, i); + if (updater != null) { + LOG.info("downgrading activity database to version " + (i - 1)); + updater.downgradeSchema(db); + } + } + LOG.info("activity database is now at version " + newVersion); + } catch (RuntimeException ex) { + GB.toast("Error downgrading database.", Toast.LENGTH_SHORT, GB.ERROR, ex); + throw ex; // reject downgrade + } + } + + private DBUpdateScript getUpdateScript(SQLiteDatabase db, int version) { + try { + Class updateClass = getClass().getClassLoader().loadClass(getClass().getPackage().getName() + ".schema.ActivityDBUpdate_" + version); + return (DBUpdateScript) updateClass.newInstance(); + } catch (ClassNotFoundException e) { + return null; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException("Error instantiating DBUpdate class for version " + version, e); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample2.java new file mode 100644 index 00000000..d01b322e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample2.java @@ -0,0 +1,94 @@ +package nodomain.freeyourgadget.gadgetbridge.impl; + +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; + +public class GBActivitySample2 implements ActivitySample { + private final int timestamp; + private final SampleProvider provider; + private final int intensity; + private final int steps; + private final int type; + private final int customValue; + + public GBActivitySample2(SampleProvider provider, int timestamp, int intensity, int steps, int type) { + this(provider, timestamp, intensity, steps, type, 0); + } + + public GBActivitySample2(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { + this.timestamp = timestamp; + this.provider = provider; + this.intensity = intensity; + this.steps = steps; + this.customValue = customValue; + this.type = type; + validate(); + } + + private void validate() { + if (steps < 0) { + throw new IllegalArgumentException("steps must be >= 0"); + } + if (intensity < 0) { + throw new IllegalArgumentException("intensity must be >= 0"); + } + if (timestamp < 0) { + throw new IllegalArgumentException("timestamp must be >= 0"); + } + if (customValue < 0) { + throw new IllegalArgumentException("customValue must be >= 0"); + } + } + + @Override + public int getTimestamp() { + return timestamp; + } + + @Override + public SampleProvider getProvider() { + return provider; + } + + @Override + public int getRawIntensity() { + return intensity; + } + + @Override + public float getIntensity() { + return getProvider().normalizeIntensity(getRawIntensity()); + } + + @Override + public int getSteps() { + return steps; + } + + @Override + public int getRawKind() { + return type; + } + + @Override + public int getKind() { + return getProvider().normalizeType(getRawKind()); + } + + @Override + public int getCustomValue() { + return customValue; + } + + @Override + public String toString() { + return "GBActivitySample{" + + "timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimeStamp(timestamp)) + + ", intensity=" + getIntensity() + + ", steps=" + getSteps() + + ", customValue=" + getCustomValue() + + ", type=" + getKind() + + '}'; + } +} From 7d15d4ff4279fc8c076de53f182fdf4ae8fad402 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 29 Apr 2016 23:19:19 +0200 Subject: [PATCH 007/569] Also update the package name in the manifest --- app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5d0ac7d1..46564e15 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="nodomain.freeyourgadget.gadgetbridge.greendao"> @@ -48,6 +59,14 @@ +
+

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

+
+ +

Incoming configuration data:

diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index df85886d..a40bf2d7 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -63,6 +63,7 @@ function gbPebble() { } } this.actuallyOpenURL = function() { + document.getElementById('step1compat').style.display="block"; window.open(this.configurationURL.toString(), "config"); } @@ -114,6 +115,22 @@ function gbPebble() { this.ready = function() { } + this.parseReturnedPebbleJS = function() { + + var str = document.getElementById('pastereturn').value; + var needle = "pebblejs://close#"; + + if (str.split(needle)[1] !== undefined) { + var t = new Object(); + t.response = unescape(str.split(needle)[1]); + this.parseconfig(t); + document.getElementById('step1').style.display="none"; + document.getElementById('step1compat').style.display="none"; + document.getElementById('step2').style.display="block"; + } else { + console.error("No valid configuration found in the entered string."); + } + } } var Pebble = new gbPebble(); @@ -123,13 +140,15 @@ if (jsConfigFile != null) { loadScript(jsConfigFile, function() { if (getURLVariable('config') == 'true') { document.getElementById('step1').style.display="none"; + document.getElementById('step1compat').style.display="none"; + document.getElementById('step2').style.display="block"; + var json_string = unescape(getURLVariable('json')); var t = new Object(); t.response = json_string; if (json_string != '') Pebble.parseconfig(t); } else { - document.getElementById('step2').style.display="none"; Pebble.ready(); Pebble.showConfiguration(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index a5181dcc..f2d30264 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -54,8 +54,12 @@ public class ExternalPebbleJSActivity extends GBActivity { Uri uri = getIntent().getData(); if (uri != null) { //getting back with configuration data - appUuid = UUID.fromString(uri.getHost()); - queryString = uri.getEncodedQuery(); + try { + appUuid = UUID.fromString(uri.getHost()); + queryString = uri.getEncodedQuery(); + } catch (IllegalArgumentException e) { + Log.d("returned uri: ", uri.toString()); + } } else { appUuid = (UUID) getIntent().getSerializableExtra("app_uuid"); } From 79b439da287a111d416392b9ddfbe2db19956bac Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 17 Jun 2016 22:43:06 +0200 Subject: [PATCH 052/569] Implement App Sorting - grab icon to move apps - cache can be sorted but nothing will be send to watch - if sorting apps or watchfaces, order will be sent to watch - we try to keep track of what is installed and what not Firmware 2.x is currently not working properly --- .../AbstractAppManagerFragment.java | 122 ++++++++++++++---- .../appmanager/AppManagerActivity.java | 58 +++++++++ .../appmanager/AppManagerFragmentCache.java | 8 +- .../AppManagerFragmentInstalledApps.java | 28 +++- ...AppManagerFragmentInstalledWatchfaces.java | 27 +++- .../adapter/GBDeviceAppAdapter.java | 4 - .../devices/pebble/PBWInstallHandler.java | 4 + .../devices/pebble/PebbleIoThread.java | 25 +++- 8 files changed, 233 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index e0e927da..bc92624f 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 @@ -48,13 +48,21 @@ public abstract class AbstractAppManagerFragment extends Fragment { private static final Logger LOG = LoggerFactory.getLogger(AbstractAppManagerFragment.class); - public void refreshList() { + protected void refreshList() { } - public String getSortFilename() { + protected String getSortFilename() { return null; } + protected void onChangedAppOrder() { + List uuidList = new ArrayList<>(); + for (GBDeviceApp gbDeviceApp : mGBDeviceAppAdapter.getItemList()) { + uuidList.add(gbDeviceApp.getUUID()); + } + AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuidList); + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -62,30 +70,33 @@ public abstract class AbstractAppManagerFragment extends Fragment { if (action.equals(GBApplication.ACTION_QUIT)) { // finish(); } else if (action.equals(ACTION_REFRESH_APPLIST)) { - int appCount = intent.getIntExtra("app_count", 0); - for (Integer i = 0; i < appCount; i++) { - String appName = intent.getStringExtra("app_name" + i.toString()); - String appCreator = intent.getStringExtra("app_creator" + i.toString()); - UUID uuid = UUID.fromString(intent.getStringExtra("app_uuid" + i.toString())); - GBDeviceApp.Type appType = GBDeviceApp.Type.values()[intent.getIntExtra("app_type" + i.toString(), 0)]; + if (intent.hasExtra("app_count")) { + int appCount = intent.getIntExtra("app_count", 0); + for (Integer i = 0; i < appCount; i++) { + String appName = intent.getStringExtra("app_name" + i.toString()); + String appCreator = intent.getStringExtra("app_creator" + i.toString()); + UUID uuid = UUID.fromString(intent.getStringExtra("app_uuid" + i.toString())); + GBDeviceApp.Type appType = GBDeviceApp.Type.values()[intent.getIntExtra("app_type" + i.toString(), 0)]; - boolean found = false; - for (final ListIterator iter = appList.listIterator(); iter.hasNext(); ) { - final GBDeviceApp app = iter.next(); - if (app.getUUID().equals(uuid)) { + boolean found = false; + for (final ListIterator iter = appList.listIterator(); iter.hasNext(); ) { + final GBDeviceApp app = iter.next(); + if (app.getUUID().equals(uuid)) { + app.setOnDevice(true); + iter.set(app); + found = true; + break; + } + } + if (!found) { + GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType); app.setOnDevice(true); - iter.set(app); - found = true; - break; + appList.add(app); } } - if (!found) { - GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType); - app.setOnDevice(true); - appList.add(app); - } + } else { + refreshList(); } - mGBDeviceAppAdapter.notifyDataSetChanged(); } } @@ -116,7 +127,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { return systemWatchfaces; } - protected List getCachedApps() { + protected List getCachedApps(List uuids) { List cachedAppList = new ArrayList<>(); File cachePath; try { @@ -126,7 +137,16 @@ public abstract class AbstractAppManagerFragment extends Fragment { return cachedAppList; } - File files[] = cachePath.listFiles(); + File[] files; + if (uuids == null) { + files = cachePath.listFiles(); + } else { + files = new File[uuids.size()]; + int index = 0; + for (UUID uuid : uuids) { + files[index++] = new File(uuid.toString() + ".pbw"); + } + } if (files != null) { for (File file : files) { if (file.getName().endsWith(".pbw")) { @@ -140,8 +160,27 @@ public abstract class AbstractAppManagerFragment extends Fragment { JSONObject json = new JSONObject(jsonstring); cachedAppList.add(new GBDeviceApp(json, configFile.exists())); } catch (Exception e) { - LOG.warn("could not read json file for " + baseName, e.getMessage(), e); - cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), baseName, "N/A", "", GBDeviceApp.Type.UNKNOWN)); + LOG.info("could not read json file for " + baseName); + //FIXME: this is really ugly, if we do not find system uuids in pbw cache add them manually + if (prefs.getBoolean("pebble_force_untested", false)) { + if (baseName.equals("4dab81a6-d2fc-458a-992c-7a1f3b96a970")) { + cachedAppList.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } else if (baseName.equals("cf1e816a-9db0-4511-bbb8-f60c48ca8fac")) { + cachedAppList.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } + } + if (baseName.equals("8f3c8686-31a1-4f5f-91f5-01600c9bdc59")) { + cachedAppList.add(new GBDeviceApp(UUID.fromString("8f3c8686-31a1-4f5f-91f5-01600c9bdc59"), "Tic Toc (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); + } + if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion()))) { + if (baseName.equals(PebbleProtocol.UUID_PEBBLE_HEALTH.toString())) { + cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + continue; + } + } + if (uuids == null) { + cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), baseName, "N/A", "", GBDeviceApp.Type.UNKNOWN)); + } } } } @@ -177,9 +216,35 @@ public abstract class AbstractAppManagerFragment extends Fragment { mGBDeviceAppAdapter = new GBDeviceAppAdapter(appList, R.layout.item_with_details, R.id.item_image, this.getContext(), this); appListView.setAdapter(mGBDeviceAppAdapter, false); appListView.setCanDragHorizontally(false); + appListView.setDragListListener(new DragListView.DragListListener() { + @Override + public void onItemDragStarted(int position) { + } + + @Override + public void onItemDragging(int itemPosition, float x, float y) { + } + + @Override + public void onItemDragEnded(int fromPosition, int toPosition) { + onChangedAppOrder(); + } + }); return rootView; } + protected void sendOrderToDevice(String concatFilename) { + ArrayList uuids = new ArrayList(); + for (GBDeviceApp gbDeviceApp : mGBDeviceAppAdapter.getItemList()) { + uuids.add(gbDeviceApp.getUUID()); + } + if (concatFilename != null) { + ArrayList concatUuids = AppManagerActivity.getUuidsFromFile(concatFilename); + uuids.addAll(concatUuids); + } + GBApplication.deviceService().onAppReorder(uuids.toArray(new UUID[uuids.size()])); + } + private void removeAppFromList(UUID uuid) { for (final ListIterator iter = appList.listIterator(); iter.hasNext(); ) { final GBDeviceApp app = iter.next(); @@ -247,9 +312,13 @@ public abstract class AbstractAppManagerFragment extends Fragment { LOG.info("deleted file: " + fileToDelete.toString()); } } - removeAppFromList(selectedApp.getUUID()); + AppManagerActivity.deleteFromAppOrderFile("pbwcacheorder.txt", selectedApp.getUUID()); // FIXME: only if successful // fall through case R.id.appmanager_app_delete: + AppManagerActivity.deleteFromAppOrderFile(mGBDevice.getAddress() + ".watchapps", selectedApp.getUUID()); // FIXME: only if successful + AppManagerActivity.deleteFromAppOrderFile(mGBDevice.getAddress() + ".watchfaces", selectedApp.getUUID()); // FIXME: only if successful + Intent refreshIntent = new Intent(AbstractAppManagerFragment.ACTION_REFRESH_APPLIST); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(refreshIntent); GBApplication.deviceService().onAppDelete(selectedApp.getUUID()); return true; case R.id.appmanager_app_reinstall: @@ -274,7 +343,6 @@ public abstract class AbstractAppManagerFragment extends Fragment { startActivity(startIntent); return true; case R.id.appmanager_app_move_to_top: - GBApplication.deviceService().onAppReorder(new UUID[]{selectedApp.getUUID()}); return true; default: return super.onContextItemSelected(item); 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 d71ead93..e1a9d44b 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 @@ -7,13 +7,29 @@ import android.support.v4.app.NavUtils; import android.support.v4.view.ViewPager; import android.view.MenuItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdapter; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; public class AppManagerActivity extends AbstractGBFragmentActivity { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractAppManagerFragment.class); + private GBDevice mGBDevice = null; public GBDevice getGBDevice() { @@ -45,6 +61,12 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { return new SectionsPagerAdapter(fragmentManager); } + public static synchronized void deleteFromAppOrderFile(String filename, UUID uuid) { + ArrayList uuids = getUuidsFromFile(filename); + uuids.remove(uuid); + rewriteAppOrderFile(filename, uuids); + } + public class SectionsPagerAdapter extends AbstractFragmentPagerAdapter { public SectionsPagerAdapter(FragmentManager fm) { @@ -95,4 +117,40 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { return super.onOptionsItemSelected(item); } + + static synchronized void rewriteAppOrderFile(String filename, List uuids) { + try { + FileWriter fileWriter = new FileWriter(FileUtils.getExternalFilesDir() + "/" + filename); + BufferedWriter out = new BufferedWriter(fileWriter); + for (UUID uuid : uuids) { + out.write(uuid.toString()); + out.newLine(); + } + out.close(); + } catch (IOException e) { + LOG.warn("can't write app order to file!"); + } + } + + synchronized public static void addToAppOrderFile(String filename, UUID uuid) { + ArrayList uuids = getUuidsFromFile(filename); + uuids.remove(uuid); // if alread there + uuids.add(uuid); + rewriteAppOrderFile(filename, uuids); + } + + static synchronized ArrayList getUuidsFromFile(String filename) { + ArrayList uuids = new ArrayList<>(); + try { + FileReader fileReader = new FileReader(FileUtils.getExternalFilesDir() + "/" + filename); + BufferedReader in = new BufferedReader(fileReader); + String line; + while ((line = in.readLine()) != null) { + uuids.add(UUID.fromString(line)); + } + } catch (IOException e) { + LOG.warn("could not read sort file"); + } + return uuids; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java index e902912a..6435bc12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java @@ -3,6 +3,12 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; public class AppManagerFragmentCache extends AbstractAppManagerFragment { @Override public void refreshList() { - appList.addAll(getCachedApps()); + appList.clear(); + appList.addAll(getCachedApps(null)); + } + + @Override + public String getSortFilename() { + return "pbwcacheorder.txt"; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java index bfc28e46..474e4c24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java @@ -1,13 +1,33 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; +import java.util.ArrayList; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; + public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment { @Override - public void refreshList() { - appList.addAll(getSystemApps()); + protected void refreshList() { + appList.clear(); + ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename()); + if (uuids.isEmpty()) { + appList.addAll(getSystemApps()); + for (GBDeviceApp gbDeviceApp : appList) { + uuids.add(gbDeviceApp.getUUID()); + } + AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuids); + } else { + appList.addAll(getCachedApps(uuids)); + } } - @Override - public String getSortFilename() { + @Override + protected String getSortFilename() { return mGBDevice.getAddress() + ".watchapps"; } + + @Override + protected void onChangedAppOrder() { + super.onChangedAppOrder(); + sendOrderToDevice(mGBDevice.getAddress() + ".watchfaces"); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java index 14c916f0..79958459 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java @@ -1,12 +1,33 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; +import java.util.ArrayList; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; + public class AppManagerFragmentInstalledWatchfaces extends AbstractAppManagerFragment { @Override - public void refreshList() { - appList.addAll(getSystemWatchfaces()); + protected void refreshList() { + appList.clear(); + ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename()); + if (uuids.isEmpty()) { + appList.addAll(getSystemWatchfaces()); + for (GBDeviceApp gbDeviceApp : appList) { + uuids.add(gbDeviceApp.getUUID()); + } + AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuids); + } else { + appList.addAll(getCachedApps(uuids)); + } } - public String getSortFilename() { + @Override + protected String getSortFilename() { return mGBDevice.getAddress() + ".watchfaces"; } + + @Override + protected void onChangedAppOrder() { + super.onChangedAppOrder(); + sendOrderToDevice(mGBDevice.getAddress() + ".watchapps"); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java index 56a263a7..35549ae8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java @@ -59,10 +59,6 @@ public class GBDeviceAppAdapter extends DragItemAdapter= 3 && !mPBWReader.isLanguage()) { if (appId == 0) { // only install metadata - not the binaries - write(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion(), mPBWReader.getFlags(), mPBWReader.getIconId())); - write(mPebbleProtocol.encodeAppStart(app.getUUID(), true)); + write(mPebbleProtocol.encodeInstallMetadata(mCurrentlyInstallingApp.getUUID(), mCurrentlyInstallingApp.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion(), mPBWReader.getFlags(), mPBWReader.getIconId())); + write(mPebbleProtocol.encodeAppStart(mCurrentlyInstallingApp.getUUID(), true)); } else { // this came from an app fetch request, so do the real stuff mIsInstalling = true; @@ -637,7 +641,7 @@ public class PebbleIoThread extends GBDeviceIoThread { writeInstallApp(mPebbleProtocol.encodeGetTime()); } else { mInstallState = PebbleAppInstallState.WAIT_SLOT; - writeInstallApp(mPebbleProtocol.encodeAppDelete(app.getUUID())); + writeInstallApp(mPebbleProtocol.encodeAppDelete(mCurrentlyInstallingApp.getUUID())); } } } @@ -651,6 +655,17 @@ public class PebbleIoThread extends GBDeviceIoThread { GB.updateInstallNotification(getContext().getString(R.string.installation_failed_), false, 0, getContext()); } else { GB.updateInstallNotification(getContext().getString(R.string.installation_successful), false, 0, getContext()); + String filenameSuffix; + if (mCurrentlyInstallingApp != null) { + if (mCurrentlyInstallingApp.getType() == GBDeviceApp.Type.WATCHFACE) { + filenameSuffix = ".watchfaces"; + } else { + filenameSuffix = ".watchapps"; + } + AppManagerActivity.addToAppOrderFile(gbDevice.getAddress() + filenameSuffix, mCurrentlyInstallingApp.getUUID()); + Intent refreshIntent = new Intent(AbstractAppManagerFragment.ACTION_REFRESH_APPLIST); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(refreshIntent); + } } mInstallState = PebbleAppInstallState.UNKNOWN; @@ -660,6 +675,8 @@ public class PebbleIoThread extends GBDeviceIoThread { mPBWReader = null; mIsInstalling = false; + mCurrentlyInstallingApp = null; + if (mFis != null) { try { mFis.close(); From 41e6833b2df11b58d1b0d6030d08ea7044025139 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 18 Jun 2016 01:26:36 +0200 Subject: [PATCH 053/569] Getting closer... db migration almost works. --- .../gadgetbridge/LockHandler.java | 5 -- .../activities/DebugActivity.java | 62 ++++++++++++------- .../charts/AbstractChartFragment.java | 2 +- .../database/ActivityDatabaseHandler.java | 9 ++- .../gadgetbridge/database/DBHelper.java | 14 ++--- .../devices/DeviceCoordinator.java | 3 +- .../devices/UnknownDeviceCoordinator.java | 3 +- .../devices/miband/MiBandCoordinator.java | 5 +- .../devices/pebble/PebbleCoordinator.java | 3 +- app/src/main/res/layout/activity_debug.xml | 36 +++++++---- 10 files changed, 87 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java index bf59275e..979b3493 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java @@ -37,11 +37,6 @@ public class LockHandler implements DBHandler { if (session == null) { throw new RuntimeException("Unable to create database session"); } - if (helper.importOldDbIfNecessary(daoMaster, this)) { - session.clear(); - session = daoMaster.newSession(); - } - } @Override 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 a5def126..5348e4db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -73,6 +73,7 @@ public class DebugActivity extends GBActivity { private Button HeartRateButton; private Button exportDBButton; private Button importDBButton; + private Button importOldActivityDataButton; private Button deleteDBButton; private EditText editContent; @@ -194,6 +195,14 @@ public class DebugActivity extends GBActivity { } }); + importOldActivityDataButton = (Button) findViewById(R.id.mergeOldActivityData); + importOldActivityDataButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mergeOldActivityDbContents(); + } + }); + deleteDBButton = (Button) findViewById(R.id.emptyDBButton); deleteDBButton.setOnClickListener(new View.OnClickListener() { @Override @@ -300,53 +309,57 @@ public class DebugActivity extends GBActivity { .show(); } - private void insertActivityDbContents() { - DBHelper helper = new DBHelper(getBaseContext()); - ActivityDatabaseHandler oldHandler = helper.getOldActivityDatabaseHandler(); + private void mergeOldActivityDbContents() { + final DBHelper helper = new DBHelper(getBaseContext()); + final ActivityDatabaseHandler oldHandler = helper.getOldActivityDatabaseHandler(); if (oldHandler == null) { + GB.toast(this, "No old activity database found, nothing to import.", Toast.LENGTH_LONG, GB.ERROR); return; } - GBDevice device = getDeviceForMergingActivityDatabaseInto(); - if (device == null) { - return; - } - try (DBHandler targetHandler = GBApplication.acquireDB()) { - helper.importOldDb(oldHandler, device, targetHandler.getDaoMaster(), targetHandler); - } catch (GBException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } + selectDeviceForMergingActivityDatabaseInto(new DeviceSelectionCallback() { + @Override + public void invoke(GBDevice device) { + if (device == null) { + GB.toast(DebugActivity.this, "No device to associate old activity data with.", Toast.LENGTH_LONG, GB.ERROR); + return; + } + try (DBHandler targetHandler = GBApplication.acquireDB()) { + helper.importOldDb(oldHandler, device, targetHandler); + } catch (Exception ex) { + GB.toast(DebugActivity.this, "Error importing old activity data into new database.", Toast.LENGTH_LONG, GB.ERROR, ex); + } + } + }); } - private GBDevice getDeviceForMergingActivityDatabaseInto() { + private void selectDeviceForMergingActivityDatabaseInto(final DeviceSelectionCallback callback) { final List availableDevices = new ArrayList<>(DeviceHelper.getInstance().getAvailableDevices(getBaseContext())); if (availableDevices.isEmpty()) { - return null; + callback.invoke(null); + return; } else if (availableDevices.size() == 1) { - return availableDevices.get(0); + callback.invoke(null); + return; } GBDeviceAdapter adapter = new GBDeviceAdapter(getBaseContext(), availableDevices); - final GBDevice[] result = new GBDevice[1]; new AlertDialog.Builder(this) .setCancelable(true) - .setTitle("Delete Activity Data?") - .setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() { + .setTitle("Associate old Data with Device") + .setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { GBDevice device = availableDevices.get(which); - result[0] = device; + callback.invoke(device); } }) - .setMessage("Select the device to merge the previous activity database data into.") .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + callback.invoke(null); } }) .show(); - return result[0]; } private void deleteActivityDatabase() { @@ -424,4 +437,7 @@ public class DebugActivity extends GBActivity { unregisterReceiver(mReceiver); } + public static interface DeviceSelectionCallback { + void invoke(GBDevice device); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index edd3c0ba..f44f141b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -296,7 +296,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { protected SampleProvider getProvider(DBHandler db, GBDevice device) { DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device); - return coordinator.getSampleProvider(db); + return coordinator.getSampleProvider(db.getDaoSession()); } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index 68abfcc6..1e941ee1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.schema.ActivityDBCreationSc import nodomain.freeyourgadget.gadgetbridge.database.schema.SchemaMigration; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -295,12 +296,13 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl public boolean hasContent() { try { try (SQLiteDatabase db = this.getReadableDatabase()) { - try (Cursor cursor = db.query(TABLE_GBACTIVITYSAMPLES, new String[]{KEY_TIMESTAMP}, null, null, null, KEY_TIMESTAMP + " DESC", "1")) { + try (Cursor cursor = db.query(TABLE_GBACTIVITYSAMPLES, new String[]{KEY_TIMESTAMP}, null, null, null, null, null, "1")) { return cursor.moveToFirst(); } } } catch (Exception ex) { // can't expect anything + GB.log("Error looking for old activity data: " + ex.getMessage(), GB.ERROR, ex); return false; } } @@ -309,4 +311,9 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl public DaoSession getDaoSession() { throw new UnsupportedOperationException(); } + + @Override + public DaoMaster getDaoMaster() { + throw new UnsupportedOperationException(); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index c6666e5b..d9777b21 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -232,10 +232,10 @@ public class DBHelper { if (prefsUser.getWeightKg() != attr.getWeightKG()) { return false; } - if (prefsUser.getSleepDuration() != attr.getSleepGoalHPD()) { + if (!Integer.valueOf(prefsUser.getSleepDuration()).equals(attr.getSleepGoalHPD())) { return false; } - if (prefsUser.getStepsGoal() != attr.getStepsGoalSPD()) { + if (!Integer.valueOf(prefsUser.getStepsGoal()).equals(attr.getStepsGoalSPD())) { return false; } return true; @@ -330,10 +330,10 @@ public class DBHelper { return null; } - public void importOldDb(ActivityDatabaseHandler oldDb, GBDevice targetDevice, DaoMaster daoMaster, DBHandler targetDBHandler) { - DaoSession tempSession = daoMaster.newSession(); + public void importOldDb(ActivityDatabaseHandler oldDb, GBDevice targetDevice, DBHandler targetDBHandler) { + DaoSession tempSession = targetDBHandler.getDaoMaster().newSession(); try { - importActivityDatabase(oldDb, targetDevice, tempSession, targetDBHandler); + importActivityDatabase(oldDb, targetDevice, tempSession); } finally { tempSession.clear(); } @@ -345,11 +345,11 @@ public class DBHelper { return totalSamplesCount == 0; } - private void importActivityDatabase(ActivityDatabaseHandler oldDbHandler, GBDevice targetDevice, DaoSession session, DBHandler targetDBHandler) { + private void importActivityDatabase(ActivityDatabaseHandler oldDbHandler, GBDevice targetDevice, DaoSession session) { try (SQLiteDatabase oldDB = oldDbHandler.getReadableDatabase()) { User user = DBHelper.getUser(session); for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) { - AbstractSampleProvider sampleProvider = (AbstractSampleProvider) coordinator.getSampleProvider(targetDBHandler); + AbstractSampleProvider sampleProvider = (AbstractSampleProvider) coordinator.getSampleProvider(session); importActivitySamples(oldDB, targetDevice, session, sampleProvider, user); } } 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 75fdbe86..32d07e87 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -6,6 +6,7 @@ import android.net.Uri; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -85,7 +86,7 @@ public interface DeviceCoordinator { * * @return */ - SampleProvider getSampleProvider(DBHandler db); + SampleProvider getSampleProvider(DaoSession session); /** * Finds an install handler for the given uri that can install the given 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 d04bc5c9..fce07d99 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -9,6 +9,7 @@ import java.util.List; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -110,7 +111,7 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { } @Override - public SampleProvider getSampleProvider(DBHandler db) { + public SampleProvider getSampleProvider(DaoSession session) { return new UnknownSampleProvider(); } 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 32d5a5b3..c08d6d1b 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 @@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -55,8 +56,8 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { } @Override - public SampleProvider getSampleProvider(DBHandler db) { - return new MiBandSampleProvider(db.getDaoSession()); + public SampleProvider getSampleProvider(DaoSession session) { + return new MiBandSampleProvider(session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index 643ffe05..6ada2cd7 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 @@ -49,9 +49,8 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { } @Override - public SampleProvider getSampleProvider(DBHandler db) { + public SampleProvider getSampleProvider(DaoSession session) { Prefs prefs = GBApplication.getPrefs(); - DaoSession session = db.getDaoSession(); int activityTracker = prefs.getInt("pebble_activitytracker", SampleProvider.PROVIDER_PEBBLE_HEALTH); switch (activityTracker) { case SampleProvider.PROVIDER_PEBBLE_HEALTH: diff --git a/app/src/main/res/layout/activity_debug.xml b/app/src/main/res/layout/activity_debug.xml index e031e754..5c16244b 100644 --- a/app/src/main/res/layout/activity_debug.xml +++ b/app/src/main/res/layout/activity_debug.xml @@ -76,16 +76,6 @@ android:layout_row="10" android:text="create test notification" /> - +
-
+

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


@@ -67,9 +69,11 @@ configuration
-
+

Incoming configuration data:

- +
diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index a40bf2d7..7738e6f5 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -1,3 +1,6 @@ +//clay stores the values in the localStorage +localStorage.clear(); + function loadScript(url, callback) { // Adding the script tag to the head as suggested before var head = document.getElementsByTagName('head')[0]; @@ -29,46 +32,63 @@ function getURLVariable(variable, defaultValue) { return defaultValue || false; } +function showStep(desiredStep) { + var steps = document.getElementsByClassName("step"); + var testStep = null; + for (var i = 0; i < steps.length; i ++) { + if (steps[i].id == desiredStep) + testStep = steps[i].id; + } + if (testStep !== null) { + for (var i = 0; i < steps.length; i ++) { + steps[i].style.display = 'none'; + } + document.getElementById(desiredStep).style.display="block"; + } +} + function gbPebble() { this.configurationURL = null; this.configurationValues = null; + var self = this; this.addEventListener = function(e, f) { if(e == 'ready') { - this.ready = f; + self.ready = f; } if(e == 'showConfiguration') { - this.showConfiguration = f; + self.showConfiguration = f; } if(e == 'webviewclosed') { - this.parseconfig = f; + self.parseconfig = f; } if(e == 'appmessage') { - this.appmessage = f; + self.appmessage = f; } } this.removeEventListener = function(e, f) { if(e == 'ready') { - this.ready = null; + self.ready = null; } if(e == 'showConfiguration') { - this.showConfiguration = null; + self.showConfiguration = null; } if(e == 'webviewclosed') { - this.parseconfig = null; + self.parseconfig = null; } if(e == 'appmessage') { - this.appmessage = null; + self.appmessage = null; } } this.actuallyOpenURL = function() { - document.getElementById('step1compat').style.display="block"; - window.open(this.configurationURL.toString(), "config"); + showStep("step1compat"); + window.open(self.configurationURL.toString(), "config"); } this.actuallySendData = function() { - GBjs.sendAppMessage(this.configurationValues); + GBjs.sendAppMessage(self.configurationValues); + GBjs.closeActivity(); } //needs to be called like this because of original Pebble function name @@ -76,7 +96,7 @@ function gbPebble() { if (url.lastIndexOf("http", 0) === 0) { document.getElementById("config_url").innerHTML=url; var UUID = GBjs.getAppUUID(); - this.configurationURL = new Uri(url).addQueryParam("return_to", "gadgetbridge://"+UUID+"?config=true&json="); + self.configurationURL = new Uri(url).addQueryParam("return_to", "gadgetbridge://"+UUID+"?config=true&json="); } else { //TODO: add custom return_to location.href = url; @@ -90,7 +110,7 @@ function gbPebble() { this.sendAppMessage = function (dict, callbackAck, callbackNack){ try { - this.configurationValues = JSON.stringify(dict); + self.configurationValues = JSON.stringify(dict); document.getElementById("jsondata").innerHTML=this.configurationValues; return callbackAck; } @@ -108,6 +128,10 @@ function gbPebble() { return GBjs.getWatchToken(); } + this.getTimelineToken = function() { + return ''; + } + this.showSimpleNotificationOnPebble = function(title, body) { GBjs.gbLog("app wanted to show: " + title + " body: "+ body); } @@ -116,17 +140,14 @@ function gbPebble() { } this.parseReturnedPebbleJS = function() { - var str = document.getElementById('pastereturn').value; var needle = "pebblejs://close#"; if (str.split(needle)[1] !== undefined) { var t = new Object(); t.response = unescape(str.split(needle)[1]); - this.parseconfig(t); - document.getElementById('step1').style.display="none"; - document.getElementById('step1compat').style.display="none"; - document.getElementById('step2').style.display="block"; + self.parseconfig(t); + showStep("step2"); } else { console.error("No valid configuration found in the entered string."); } @@ -136,13 +157,11 @@ function gbPebble() { var Pebble = new gbPebble(); var jsConfigFile = GBjs.getAppConfigurationFile(); +document.addEventListener('DOMContentLoaded', function(){ if (jsConfigFile != null) { loadScript(jsConfigFile, function() { if (getURLVariable('config') == 'true') { - document.getElementById('step1').style.display="none"; - document.getElementById('step1compat').style.display="none"; - document.getElementById('step2').style.display="block"; - + showStep("step2"); var json_string = unescape(getURLVariable('json')); var t = new Object(); t.response = json_string; @@ -154,3 +173,4 @@ if (jsConfigFile != null) { } }); } +}, false); \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index f2d30264..f756ecb5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -28,6 +28,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; @@ -37,7 +38,9 @@ public class ExternalPebbleJSActivity extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); private UUID appUuid; + private Uri confUri; private GBDevice mGBDevice = null; + private WebView myWebView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -46,27 +49,15 @@ public class ExternalPebbleJSActivity extends GBActivity { Bundle extras = getIntent().getExtras(); if (extras != null) { mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE); + appUuid = (UUID) extras.getSerializable(DeviceService.EXTRA_APP_UUID); } else { throw new IllegalArgumentException("Must provide a device when invoking this activity"); } - String queryString = ""; - Uri uri = getIntent().getData(); - if (uri != null) { - //getting back with configuration data - try { - appUuid = UUID.fromString(uri.getHost()); - queryString = uri.getEncodedQuery(); - } catch (IllegalArgumentException e) { - Log.d("returned uri: ", uri.toString()); - } - } else { - appUuid = (UUID) getIntent().getSerializableExtra("app_uuid"); - } setContentView(R.layout.activity_external_pebble_js); - WebView myWebView = (WebView) findViewById(R.id.configureWebview); + myWebView = (WebView) findViewById(R.id.configureWebview); myWebView.clearCache(true); myWebView.setWebViewClient(new GBWebClient()); myWebView.setWebChromeClient(new GBChromeClient()); @@ -78,7 +69,32 @@ public class ExternalPebbleJSActivity extends GBActivity { JSInterface gbJSInterface = new JSInterface(); myWebView.addJavascriptInterface(gbJSInterface, "GBjs"); - myWebView.loadUrl("file:///android_asset/app_config/configure.html?" + queryString); + myWebView.loadUrl("file:///android_asset/app_config/configure.html"); + + } + + @Override + protected void onNewIntent(Intent incoming) { + super.onNewIntent(incoming); + confUri = incoming.getData(); + } + + @Override + protected void onResume() { + super.onResume(); + String queryString = ""; + + if (confUri != null) { + //getting back with configuration data + try { + appUuid = UUID.fromString(confUri.getHost()); + queryString = confUri.getEncodedQuery(); + } catch (IllegalArgumentException e) { + GB.toast("returned uri: " + confUri.toString(), Toast.LENGTH_LONG, GB.ERROR); + } + myWebView.loadUrl("file:///android_asset/app_config/configure.html?" + queryString); + } + } private JSONObject getAppConfigurationKeys() { @@ -112,8 +128,7 @@ public class ExternalPebbleJSActivity extends GBActivity { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("http://") || url.startsWith("https://")) { - Intent i = new Intent(Intent.ACTION_VIEW, - Uri.parse(url)); + Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(i); } else { url = url.replaceFirst("^pebblejs://close#", "file:///android_asset/app_config/configure.html?config=true&json="); @@ -222,6 +237,11 @@ public class ExternalPebbleJSActivity extends GBActivity { //specification says: A string that is is guaranteed to be identical for each Pebble device for the same app across different mobile devices. The token is unique to your app and cannot be used to track Pebble devices across applications. see https://developer.pebble.com/docs/js/Pebble/ return "gb" + appUuid.toString(); } + + @JavascriptInterface + public void closeActivity() { + finish(); + } } @Override From 6749c493b1a39b2321e38bafd2ecccb793c24565 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 18 Jun 2016 19:13:03 +0200 Subject: [PATCH 056/569] Changelog for app configuration --- CHANGELOG.md | 6 ++++++ app/src/main/res/xml/changelog_master.xml | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ab2fbb..ebd0ff29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ ###Changelog + + +####Version 0.10.2 +* Pebble: allow to manually paste configuration data for legacy configuration pages +* Pebble: various improvements to the configuration page + ####Version 0.10.1 * Pebble: set extended music info by dissecting notifications on Android 5.0+ * Pebble: various other improvemnts to music playback diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 30d19573..089322be 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,12 @@ + + Pebble: allow to manually paste configuration data for legacy configuration pages + + Pebble: various improvements to the configuration page + Pebble: set extended music info by dissecting notifications on Android 5.0+ Pebble: various other improvemnts to music playback From 245b8655e778696f81e4e32e20a126332917a273 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 18 Jun 2016 21:01:32 +0200 Subject: [PATCH 057/569] Fixed typo #251 --- app/src/main/assets/app_config/configure.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/app_config/configure.html b/app/src/main/assets/app_config/configure.html index 68edb92d..e606d466 100644 --- a/app/src/main/assets/app_config/configure.html +++ b/app/src/main/assets/app_config/configure.html @@ -62,7 +62,7 @@
-

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

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


+

App presets:

+

In case of "network error" after saving settings in the watchhapp, copy the "network error" @@ -75,5 +80,11 @@ +

App Presets:

+ +

Existing presets will be deleted.

diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index b37d5089..81cf1548 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -91,6 +91,23 @@ function gbPebble() { GBjs.closeActivity(); } + this.savePreset = function() { + GBjs.saveAppStoredPreset(self.configurationValues); + } + + this.loadPreset = function() { + showStep("step2"); + var presetElements = document.getElementsByClassName("store_presets"); + for (var i = 0; i < presetElements.length; i ++) { + presetElements[i].style.display = 'none'; + } + var json_string = GBjs.getAppStoredPreset(); + var t = new Object(); + t.response = json_string; + if (json_string != '') + Pebble.parseconfig(t); + } + //needs to be called like this because of original Pebble function name this.openURL = function(url) { if (url.lastIndexOf("http", 0) === 0) { @@ -111,7 +128,7 @@ function gbPebble() { this.sendAppMessage = function (dict, callbackAck, callbackNack){ try { self.configurationValues = JSON.stringify(dict); - document.getElementById("jsondata").innerHTML=this.configurationValues; + document.getElementById("jsondata").innerHTML=self.configurationValues; return callbackAck; } catch (e) { @@ -162,6 +179,8 @@ function gbPebble() { var Pebble = new gbPebble(); var jsConfigFile = GBjs.getAppConfigurationFile(); +var storedPreset = GBjs.getAppStoredPreset(); + document.addEventListener('DOMContentLoaded', function(){ if (jsConfigFile != null) { loadScript(jsConfigFile, function() { @@ -173,6 +192,12 @@ if (jsConfigFile != null) { if (json_string != '') Pebble.parseconfig(t); } else { + if (storedPreset === undefined) { + var presetElements = document.getElementsByClassName("load_presets"); + for (var i = 0; i < presetElements.length; i ++) { + presetElements[i].style.display = 'none'; + } + } Pebble.ready(); Pebble.showConfiguration(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 200c0328..1a68138e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -20,8 +20,11 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; import java.io.IOException; +import java.io.Writer; import java.util.Iterator; import java.util.Scanner; import java.util.UUID; @@ -233,6 +236,38 @@ public class ExternalPebbleJSActivity extends GBActivity { return null; } + @JavascriptInterface + public String getAppStoredPreset() { + try { + File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); + File configurationFile = new File(destDir, appUuid.toString() + "_preset.json"); + if (configurationFile.exists()) { + return FileUtils.getStringFromFile(configurationFile); + } + } catch (IOException e) { + GB.toast("Error reading presets", Toast.LENGTH_LONG, GB.ERROR); + e.printStackTrace(); + } + return null; + } + + @JavascriptInterface + public void saveAppStoredPreset(String msg) { + Writer writer; + + try { + File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); + File presetsFile = new File(destDir, appUuid.toString() + "_preset.json"); + writer = new BufferedWriter(new FileWriter(presetsFile)); + writer.write(msg); + writer.close(); + GB.toast("Presets stored", Toast.LENGTH_SHORT, GB.INFO); + } catch (IOException e) { + GB.toast("Error storing presets", Toast.LENGTH_LONG, GB.ERROR); + e.printStackTrace(); + } + } + @JavascriptInterface public String getAppUUID() { return appUuid.toString(); 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 eff9df8f..8247dcd3 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 @@ -312,7 +312,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { return true; } - String[] suffixToDelete = new String[]{".pbw", ".json", "_config.js"}; + String[] suffixToDelete = new String[]{".pbw", ".json", "_config.js", "_preset.json"}; for (String suffix : suffixToDelete) { File fileToDelete = new File(baseName + suffix); diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index e58cf225..9e499d93 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -3,6 +3,7 @@ Pebble: new App Manager (keeps track of installed apps and allows app sorting on FW 3.x) Pebble: call dismissal with canned SMS (FW 3.x) + Pebble: watchapp configuration presets Pebble: fix regression with FW 2.x (almost everything was broken in 0.10.2) From 69be5dbbc7ad7770ddca97a13edc4dd21d9d4ab1 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 26 Jun 2016 14:13:47 +0200 Subject: [PATCH 089/569] Set the json directly instead of using parseconfig Some watchfaces do some processing on the incoming json, and we are storing the json after the fact, and double parsing isn't good. --- .../main/assets/app_config/js/gadgetbridge_boilerplate.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index 81cf1548..d3021c31 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -101,11 +101,8 @@ function gbPebble() { for (var i = 0; i < presetElements.length; i ++) { presetElements[i].style.display = 'none'; } - var json_string = GBjs.getAppStoredPreset(); - var t = new Object(); - t.response = json_string; - if (json_string != '') - Pebble.parseconfig(t); + self.configurationValues = GBjs.getAppStoredPreset(); + document.getElementById("jsondata").innerHTML=self.configurationValues; } //needs to be called like this because of original Pebble function name From 07283d4a75fa7b114d60d415e0d586515ac8f6d3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 26 Jun 2016 18:00:18 +0200 Subject: [PATCH 090/569] update Japanese from transifex (thanks!) --- app/src/main/res/values-ja/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 85074faf..91c193b1 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -248,4 +248,7 @@ ファームウェアを送信しませんでした 心拍数 心拍数 + キャッシュ中のアプリ + インストール済アプリ + インストール済ウォッチフェイス From 358cd6df5ebb0903e3f38a2452f0109d630b9d02 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 26 Jun 2016 19:01:39 +0200 Subject: [PATCH 091/569] update German translation --- app/src/main/res/values-de/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b77af118..7ffbd9ac 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -248,4 +248,7 @@ Firmware wurde nicht gesendet Herzfrequenz Herzfrequenz + Apps im Zwischenspeicher + Installierte Apps + Installierte Zifferblätter From e70a2290c3f4032fe085c7d5e5687e386fc031d0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 27 Jun 2016 20:41:20 +0200 Subject: [PATCH 092/569] sampleproviders now return device specific data #206 --- .../charts/AbstractChartFragment.java | 2 +- .../gadgetbridge/database/DBHelper.java | 17 +++++++++------ .../devices/AbstractSampleProvider.java | 21 +++++++++++++++++-- .../devices/DeviceCoordinator.java | 2 +- .../devices/UnknownDeviceCoordinator.java | 2 +- .../devices/miband/MiBandCoordinator.java | 4 ++-- .../devices/miband/MiBandSampleProvider.java | 10 +++++++-- .../pebble/AbstractPebbleSampleProvider.java | 11 ++++++++-- .../devices/pebble/HealthSampleProvider.java | 5 +++-- .../devices/pebble/MisfitSampleProvider.java | 5 +++-- .../pebble/MorpheuzSampleProvider.java | 5 +++-- .../devices/pebble/PebbleCoordinator.java | 12 +++++------ .../PebbleGadgetBridgeSampleProvider.java | 5 +++-- .../operations/FetchActivityOperation.java | 2 +- .../pebble/AppMessageHandlerGBPebble.java | 2 +- .../pebble/AppMessageHandlerMisfit.java | 4 +++- .../pebble/AppMessageHandlerMorpheuz.java | 2 +- .../DatalogSessionHealthOverlayData.java | 7 ++++--- .../pebble/DatalogSessionHealthSleep.java | 7 ++++--- .../pebble/DatalogSessionHealthSteps.java | 12 +++-------- .../pebble/DatalogSessionPebbleHealth.java | 10 ++++++++- .../devices/pebble/PebbleProtocol.java | 4 ++-- 22 files changed, 98 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index ea651108..914f5c76 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -296,7 +296,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { protected SampleProvider getProvider(DBHandler db, GBDevice device) { DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device); - return coordinator.getSampleProvider(db.getDaoSession()); + return coordinator.getSampleProvider(device, db.getDaoSession()); } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index f33ca1ce..fb59411f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -260,15 +260,20 @@ public class DBHelper { return false; } - public static Device getDevice(GBDevice gbDevice, DaoSession session) { + public static Device findDevice(GBDevice gbDevice, DaoSession session) { DeviceDao deviceDao = session.getDeviceDao(); Query query = deviceDao.queryBuilder().where(DeviceDao.Properties.Identifier.eq(gbDevice.getAddress())).build(); List devices = query.list(); - Device device; - if (devices.isEmpty()) { + if (devices.size() > 0) { + return devices.get(0); + } + return null; + } + + public static Device getDevice(GBDevice gbDevice, DaoSession session) { + Device device = findDevice(gbDevice, session); + if (device == null) { device = createDevice(session, gbDevice); - } else { - device = devices.get(0); } ensureDeviceAttributes(device, gbDevice, session); @@ -348,7 +353,7 @@ public class DBHelper { try (SQLiteDatabase oldDB = oldDbHandler.getReadableDatabase()) { User user = DBHelper.getUser(session); for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) { - AbstractSampleProvider sampleProvider = (AbstractSampleProvider) coordinator.getSampleProvider(session); + AbstractSampleProvider sampleProvider = (AbstractSampleProvider) coordinator.getSampleProvider(targetDevice, session); importActivitySamples(oldDB, targetDevice, session, sampleProvider, user); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index d61076a8..227543b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -1,13 +1,17 @@ package nodomain.freeyourgadget.gadgetbridge.devices; +import java.util.Collections; import java.util.List; import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; import de.greenrobot.dao.query.QueryBuilder; import de.greenrobot.dao.query.WhereCondition; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; /** @@ -18,11 +22,17 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public abstract class AbstractSampleProvider implements SampleProvider { private static final WhereCondition[] NO_CONDITIONS = new WhereCondition[0]; private final DaoSession mSession; + private final GBDevice mDevice; - protected AbstractSampleProvider(DaoSession session) { + protected AbstractSampleProvider(GBDevice device, DaoSession session) { + mDevice = device; mSession = session; } + public GBDevice getmDevice() { + return mDevice; + } + public DaoSession getSession() { return mSession; } @@ -83,7 +93,13 @@ public abstract class AbstractSampleProvider i protected List getGBActivitySamples(int timestamp_from, int timestamp_to, int activityType) { QueryBuilder qb = getSampleDao().queryBuilder(); Property timestampProperty = getTimestampSampleProperty(); - qb.where(timestampProperty.ge(timestamp_from)) + Device dbDevice = DBHelper.findDevice(getmDevice(), getSession()); + if (dbDevice == null) { + // no device, no samples + return Collections.emptyList(); + } + Property deviceProperty = getDeviceIdentifierSampleProperty(); + qb.where(deviceProperty.eq(dbDevice.getId()), timestampProperty.ge(timestamp_from)) .where(timestampProperty.le(timestamp_to), getClauseForActivityType(qb, activityType)); List samples = qb.build().list(); for (T sample : samples) { @@ -131,4 +147,5 @@ public abstract class AbstractSampleProvider i protected abstract Property getRawKindSampleProperty(); protected abstract Property getTimestampSampleProperty(); + protected abstract Property getDeviceIdentifierSampleProperty(); } 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 a9cda31d..e8161d06 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -86,7 +86,7 @@ public interface DeviceCoordinator { * * @return */ - SampleProvider getSampleProvider(DaoSession session); + SampleProvider getSampleProvider(GBDevice device, DaoSession session); /** * Finds an install handler for the given uri that can install the given 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 0c5c1b9f..49df5658 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -111,7 +111,7 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { } @Override - public SampleProvider getSampleProvider(DaoSession session) { + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { return new UnknownSampleProvider(); } 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 d50609c9..3fba2bc0 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 @@ -56,8 +56,8 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { } @Override - public SampleProvider getSampleProvider(DaoSession session) { - return new MiBandSampleProvider(session); + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return new MiBandSampleProvider(device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java index eddf67e2..7cc06141 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java @@ -7,6 +7,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class MiBandSampleProvider extends AbstractSampleProvider { @@ -28,8 +29,8 @@ public class MiBandSampleProvider extends AbstractSampleProvider { - protected AbstractPebbleSampleProvider(DaoSession session) { - super(session); + protected AbstractPebbleSampleProvider(GBDevice device, DaoSession session) { + super(device, session); } @Override @@ -27,6 +29,11 @@ public abstract class AbstractPebbleSampleProvider extends AbstractSampleProvide return PebbleActivitySampleDao.Properties.RawKind; } + @Override + protected Property getDeviceIdentifierSampleProperty() { + return PebbleActivitySampleDao.Properties.DeviceId; + } + @Override public PebbleActivitySample createActivitySample() { return new PebbleActivitySample(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java index 5c24bf34..b5509903 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java @@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class HealthSampleProvider extends AbstractPebbleSampleProvider { @@ -12,8 +13,8 @@ public class HealthSampleProvider extends AbstractPebbleSampleProvider { protected final float movementDivisor = 8000f; - public HealthSampleProvider(DaoSession session) { - super(session); + public HealthSampleProvider(GBDevice device, DaoSession session) { + super(device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java index 082f42a2..1fa44f8d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java @@ -2,13 +2,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; public class MisfitSampleProvider extends AbstractPebbleSampleProvider { protected final float movementDivisor = 300f; - public MisfitSampleProvider(DaoSession session) { - super(session); + public MisfitSampleProvider(GBDevice device, DaoSession session) { + super(device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MorpheuzSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MorpheuzSampleProvider.java index 8350751d..0e82e608 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MorpheuzSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MorpheuzSampleProvider.java @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class MorpheuzSampleProvider extends AbstractPebbleSampleProvider { @@ -13,8 +14,8 @@ public class MorpheuzSampleProvider extends AbstractPebbleSampleProvider { protected float movementDivisor = 5000f; - public MorpheuzSampleProvider(DaoSession session) { - super(session); + public MorpheuzSampleProvider(GBDevice device, DaoSession session) { + super(device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index f1310a79..61355398 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 @@ -46,20 +46,20 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { } @Override - public SampleProvider getSampleProvider(DaoSession session) { + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { Prefs prefs = GBApplication.getPrefs(); int activityTracker = prefs.getInt("pebble_activitytracker", SampleProvider.PROVIDER_PEBBLE_HEALTH); switch (activityTracker) { case SampleProvider.PROVIDER_PEBBLE_HEALTH: - return new HealthSampleProvider(session); + return new HealthSampleProvider(device, session); case SampleProvider.PROVIDER_PEBBLE_MISFIT: - return new MisfitSampleProvider(session); + return new MisfitSampleProvider(device, session); case SampleProvider.PROVIDER_PEBBLE_MORPHEUZ: - return new MorpheuzSampleProvider(session); + return new MorpheuzSampleProvider(device, session); case SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE: - return new PebbleGadgetBridgeSampleProvider(session); + return new PebbleGadgetBridgeSampleProvider(device, session); default: - return new HealthSampleProvider(session); + return new HealthSampleProvider(device, session); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java index da70697f..f04c70b4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java @@ -2,10 +2,11 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; public class PebbleGadgetBridgeSampleProvider extends MorpheuzSampleProvider { - public PebbleGadgetBridgeSampleProvider(DaoSession session) { - super(session); + public PebbleGadgetBridgeSampleProvider(GBDevice device, DaoSession session) { + super(device, session); movementDivisor = 63.0f; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index 1a550d61..ae560d71 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -305,7 +305,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { byte category, intensity, steps, heartrate = 0; try (DBHandler dbHandler = GBApplication.acquireDB()){ - MiBandSampleProvider provider = new MiBandSampleProvider(dbHandler.getDaoSession()); + MiBandSampleProvider provider = new MiBandSampleProvider(getDevice(), dbHandler.getDaoSession()); Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); int minutes = 0; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java index 9ddb48e9..4652a540 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java @@ -58,7 +58,7 @@ public class AppMessageHandlerGBPebble extends AppMessageHandler { try (DBHandler db = GBApplication.acquireDB()) { User user = DBHelper.getUser(db.getDaoSession()); Device device = DBHelper.getDevice(getDevice(), db.getDaoSession()); - PebbleGadgetBridgeSampleProvider sampleProvider = new PebbleGadgetBridgeSampleProvider(db.getDaoSession()); + PebbleGadgetBridgeSampleProvider sampleProvider = new PebbleGadgetBridgeSampleProvider(getDevice(), db.getDaoSession()); PebbleActivitySample[] activitySamples = new PebbleActivitySample[samples_remaining]; int i = 0; while (samples_remaining-- > 0) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 67b9481b..a72ca21c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -22,6 +22,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MisfitSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -51,6 +52,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + GBDevice device = getDevice(); for (Pair pair : pairs) { switch (pair.first) { case KEY_INCOMING_DATA_BEGIN: @@ -116,7 +118,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { } LOG.info("total steps for above period: " + totalSteps); - MisfitSampleProvider sampleProvider = new MisfitSampleProvider(db.getDaoSession()); + MisfitSampleProvider sampleProvider = new MisfitSampleProvider(device, db.getDaoSession()); sampleProvider.addGBActivitySamples(activitySamples); } catch (Exception e) { LOG.error("Error acquiring database", e); 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 36e751d8..a2b4c5e2 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 @@ -102,7 +102,7 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { try (DBHandler db = GBApplication.acquireDB()) { User user = DBHelper.getUser(db.getDaoSession()); Device device = DBHelper.getDevice(getDevice(), db.getDaoSession()); - MorpheuzSampleProvider sampleProvider = new MorpheuzSampleProvider(db.getDaoSession()); + MorpheuzSampleProvider sampleProvider = new MorpheuzSampleProvider(getDevice(), db.getDaoSession()); sampleProvider.addGBActivitySample(createSample(recording_base_timestamp + index * 600, intensity, 0, type, user, device)); } catch (Exception e) { LOG.error("Error acquiring database", e); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index b60ddae3..5555835f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -10,6 +10,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -17,8 +18,8 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthOverlayData.class); - public DatalogSessionHealthOverlayData(byte id, UUID uuid, int tag, byte item_type, short item_size) { - super(id, uuid, tag, item_type, item_size); + public DatalogSessionHealthOverlayData(byte id, UUID uuid, int tag, byte item_type, short item_size, GBDevice device) { + super(id, uuid, tag, item_type, item_size, device); taginfo = "(health - overlay data " + tag + " )"; } @@ -59,7 +60,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { private boolean store(OverlayRecord[] overlayRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - SampleProvider sampleProvider = new HealthSampleProvider(dbHandler.getDaoSession()); + SampleProvider sampleProvider = new HealthSampleProvider(getDevice(), dbHandler.getDaoSession()); int latestTimestamp = sampleProvider.fetchLatestTimestamp(); for (OverlayRecord overlayRecord : overlayRecords) { if (latestTimestamp < (overlayRecord.timestampStart + overlayRecord.durationSeconds)) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 21e09b96..3b6561c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -10,6 +10,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -17,8 +18,8 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSleep.class); - public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size) { - super(id, uuid, tag, item_type, item_size); + public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size, GBDevice device) { + super(id, uuid, tag, item_type, item_size, device); taginfo = "(health - sleep " + tag + " )"; } @@ -58,7 +59,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { private boolean store(SleepRecord[] sleepRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - SampleProvider sampleProvider = new HealthSampleProvider(dbHandler.getDaoSession()); + SampleProvider sampleProvider = new HealthSampleProvider(getDevice(), dbHandler.getDaoSession()); int latestTimestamp = sampleProvider.fetchLatestTimestamp(); for (SleepRecord sleepRecord : sleepRecords) { if (latestTimestamp < sleepRecord.bedTimeEnd) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index 408555a2..3a2551f2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -10,25 +10,19 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; -import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSteps.class); - private final GBDevice device; public DatalogSessionHealthSteps(byte id, UUID uuid, int tag, byte item_type, short item_size, GBDevice device) { - super(id, uuid, tag, item_type, item_size); + super(id, uuid, tag, item_type, item_size, device); taginfo = "(health - steps)"; - this.device = device; } @Override @@ -81,11 +75,11 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { private void store(StepsRecord[] stepsRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - HealthSampleProvider sampleProvider = new HealthSampleProvider(dbHandler.getDaoSession()); + HealthSampleProvider sampleProvider = new HealthSampleProvider(getDevice(), dbHandler.getDaoSession()); PebbleActivitySample[] samples = new PebbleActivitySample[stepsRecords.length]; // TODO: user and device Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); - Long deviceId = DBHelper.getDevice(device, dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); for (int j = 0; j < stepsRecords.length; j++) { StepsRecord stepsRecord = stepsRecords[j]; samples[j] = new PebbleActivitySample( diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java index 6df7a751..0e984553 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java @@ -3,12 +3,20 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; abstract class DatalogSessionPebbleHealth extends DatalogSession { - DatalogSessionPebbleHealth(byte id, UUID uuid, int tag, byte itemType, short itemSize) { + private final GBDevice mDevice; + + DatalogSessionPebbleHealth(byte id, UUID uuid, int tag, byte itemType, short itemSize, GBDevice device) { super(id, uuid, tag, itemType, itemSize); + mDevice = device; + } + + public GBDevice getDevice() { + return mDevice; } protected boolean isPebbleHealthEnabled() { 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 27e7a192..c57a25c8 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 @@ -1999,9 +1999,9 @@ public class PebbleProtocol extends GBDeviceProtocol { if (uuid.equals(UUID_ZERO) && log_tag == 81) { mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size, getDevice())); } else if (uuid.equals(UUID_ZERO) && log_tag == 83) { - mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size)); + mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size, getDevice())); } else if (uuid.equals(UUID_ZERO) && log_tag == 84) { - mDatalogSessions.put(id, new DatalogSessionHealthOverlayData(id, uuid, log_tag, item_type, item_size)); + mDatalogSessions.put(id, new DatalogSessionHealthOverlayData(id, uuid, log_tag, item_type, item_size, getDevice())); } else { mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); } From 56615de1f0a57fc3e25a0512bf0e3db5986caaa8 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 27 Jun 2016 21:29:39 +0200 Subject: [PATCH 093/569] log FileNotFoundException when checking if directory is writable or not: #343 --- .../nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java index 470ef392..f3e2e520 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java @@ -116,6 +116,7 @@ public class FileUtils { file.delete(); return true; } catch (FileNotFoundException e) { + GB.log("Cannot write to directory: " + dir.getAbsolutePath(), GB.INFO, e); return false; } } From 76a44ad3a41f2c12142099089e6e6c640f865696 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 27 Jun 2016 22:01:52 +0200 Subject: [PATCH 094/569] more logging to detect problems with external dirs: #343 --- .../freeyourgadget/gadgetbridge/util/FileUtils.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java index f3e2e520..5fbb34f1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java @@ -145,13 +145,18 @@ public class FileUtils { } for (int i = 0; i < dirs.length; i++) { File dir = dirs[i]; - if (dir == null || (!dir.exists() && !dir.mkdirs())) { + if (dir == null) { continue; } + if (!dir.exists() && !dir.mkdirs()) { + GB.log("Unable to create directories: " + dir.getAbsolutePath(), GB.INFO, null); + continue; + } + // the first directory is also the primary external storage, i.e. the same as Environment.getExternalFilesDir() // TODO: check the mount state of *all* dirs when switching to later API level if (!dir.canWrite() || (i == 0 && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))) { - Log.i(TAG, "ignoring non-writable external storage dir: " + dir); + GB.log("ignoring non-writable external storage dir: " + dir, GB.INFO, null); continue; } result.add(dir); // add last From 7613b62dabdcc07ae7c9eb22885210779304766c Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 28 Jun 2016 00:35:50 +0200 Subject: [PATCH 095/569] Improved discovery mechanism #323 Does not rely solely on mac addresses anymore. Should help when mac address randomization is used. --- .../devices/AbstractDeviceCoordinator.java | 30 +++++++++++++++++ .../devices/miband/MiBandConst.java | 1 + .../devices/miband/MiBandCoordinator.java | 18 +++++++++-- .../gadgetbridge/impl/GBDeviceCandidate.java | 32 +++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index 28e5fcf8..f27a8290 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -1,9 +1,39 @@ package nodomain.freeyourgadget.gadgetbridge.devices; +import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothDevice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviceCoordinator.class); + public boolean allowFetchActivityData(GBDevice device) { return device.isInitialized() && !device.isBusy() && supportsActivityDataFetching(); } + + public boolean isHealthWearable(BluetoothDevice device) { + BluetoothClass bluetoothClass = device.getBluetoothClass(); + if (bluetoothClass == null) { + LOG.warn("unable to determine bluetooth device class of " + device); + return false; + } + if (bluetoothClass.getMajorDeviceClass() == BluetoothClass.Device.Major.WEARABLE + || bluetoothClass.getMajorDeviceClass() == BluetoothClass.Device.Major.UNCATEGORIZED) { + int deviceClasses = + BluetoothClass.Device.HEALTH_BLOOD_PRESSURE + | BluetoothClass.Device.HEALTH_DATA_DISPLAY + | BluetoothClass.Device.HEALTH_PULSE_RATE + | BluetoothClass.Device.HEALTH_WEIGHING + | BluetoothClass.Device.HEALTH_UNCATEGORIZED + | BluetoothClass.Device.HEALTH_PULSE_OXIMETER + | BluetoothClass.Device.HEALTH_GLUCOSE; + + return (bluetoothClass.getDeviceClass() & deviceClasses) != 0; + } + return false; + } } 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 ca8b9e64..8dca9b5f 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 @@ -24,6 +24,7 @@ public final class MiBandConst { public static final String ORIGIN_K9MAIL = "k9mail"; public static final String ORIGIN_PEBBLEMSG = "pebblemsg"; public static final String ORIGIN_GENERIC = "generic"; + public static final String MI_GENERAL_NAME_PREFIX = "MI"; public static final String MI_1 = "1"; public static final String MI_1A = "1A"; public static final String MI_1S = "1S"; 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 3fba2bc0..509f4e68 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 @@ -32,8 +32,22 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { @Override public boolean supports(GBDeviceCandidate candidate) { String macAddress = candidate.getMacAddress().toUpperCase(); - return macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1_1A) - || macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1S); + if (macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1_1A) + || macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1S)) { + return true; + } + if (candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND_SERVICE)) { + return true; + } + // and a heuristic + try { + if (isHealthWearable(candidate.getDevice())) { + return candidate.getName().toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase()); + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return false; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java index d0755636..286e5897 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -2,8 +2,14 @@ package nodomain.freeyourgadget.gadgetbridge.impl; import android.bluetooth.BluetoothDevice; import android.os.Parcel; +import android.os.ParcelUuid; import android.os.Parcelable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.UUID; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; @@ -14,6 +20,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; * support this candidate, will the candidate be promoted to a GBDevice. */ public class GBDeviceCandidate implements Parcelable { + private static final Logger LOG = LoggerFactory.getLogger(GBDeviceCandidate.class); + private final BluetoothDevice device; private final short rssi; private DeviceType deviceType = DeviceType.UNKNOWN; @@ -40,6 +48,10 @@ public class GBDeviceCandidate implements Parcelable { dest.writeString(deviceType.name()); } + public BluetoothDevice getDevice() { + return device; + } + public DeviceType getDeviceType() { return deviceType; } @@ -48,6 +60,21 @@ public class GBDeviceCandidate implements Parcelable { return device != null ? device.getAddress() : GBApplication.getContext().getString(R.string._unknown_); } + public boolean supportsService(UUID aService) { + ParcelUuid[] uuids = device.getUuids(); + if (uuids == null) { + LOG.warn("no cached services available for " + this); + return false; + } + + for (ParcelUuid uuid : uuids) { + if (uuid != null && aService.equals(uuid.getUuid())) { + return true; + } + } + return false; + } + public String getName() { String name = null; if (device != null) { @@ -85,4 +112,9 @@ public class GBDeviceCandidate implements Parcelable { public int hashCode() { return device.getAddress().hashCode() ^ 37; } + + @Override + public String toString() { + return getName() + ": " + getMacAddress(); + } } From 76dcb8f8286648db9c0ffc1346bd1953ca2e302a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 28 Jun 2016 22:16:44 +0200 Subject: [PATCH 096/569] Remove authors and link to contributors.md instead --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 581ec928..e765c19a 100644 --- a/README.md +++ b/README.md @@ -94,11 +94,10 @@ Known Issues: 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. -## Authors (in order of first code contribution) +## Authors -* Andreas Shimokawa -* Carsten Pfeiffer -* Daniele Gobbetti +See [the list of contributors](https://raw.githubusercontent.com/Freeyourgadget/Gadgetbridge/master/CONTRIBUTORS.md) +in order of first code contribution. ## Contribute From f0da25c49b6d9b1e78c58c64d6f8d1a40e924e70 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 28 Jun 2016 23:07:24 +0200 Subject: [PATCH 097/569] Fix activity fetching getting stuck when double tapping #333 --- .../gadgetbridge/activities/charts/ChartsActivity.java | 2 +- .../gadgetbridge/service/btle/AbstractBTLEOperation.java | 6 ++++-- .../devices/miband/operations/AbstractMiBandOperation.java | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java index f09fca79..292202cc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java @@ -102,8 +102,8 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts swipeLayout.setRefreshing(true); } else { boolean wasBusy = swipeLayout.isRefreshing(); + swipeLayout.setRefreshing(false); if (wasBusy) { - swipeLayout.setRefreshing(false); LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(REFRESH)); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java index 258199ad..7747567d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java @@ -105,8 +105,10 @@ public abstract class AbstractBTLEOperation } protected void unsetBusy() { - getDevice().unsetBusyTask(); - getDevice().sendDeviceUpdateIntent(getContext()); + if (getDevice().isBusy()) { + getDevice().unsetBusyTask(); + getDevice().sendDeviceUpdateIntent(getContext()); + } } public boolean isOperationRunning() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java index 2f6577d7..971a63d4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java @@ -18,6 +18,7 @@ public abstract class AbstractMiBandOperation extends AbstractBTLEOperation Date: Tue, 28 Jun 2016 23:23:29 +0200 Subject: [PATCH 098/569] log raw activity data from mi band, closes #341 Hopefully aids in deciphering activity kinds. --- .../devices/miband/operations/FetchActivityOperation.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index ae560d71..a5de2974 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -312,7 +312,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { try { int timestampInSeconds = (int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000); if ((activityStruct.activityDataHolderProgress % bpm) != 0) { - throw new IllegalStateException("Unexpected data, progress should be mutiple of " + bpm + ": " + activityStruct.activityDataHolderProgress); + throw new IllegalStateException("Unexpected data, progress should be multiple of " + bpm + ": " + activityStruct.activityDataHolderProgress); } int numSamples = activityStruct.activityDataHolderProgress / bpm; MiBandActivitySample[] samples = new MiBandActivitySample[numSamples]; @@ -337,6 +337,10 @@ public class FetchActivityOperation extends AbstractMiBandOperation { heartrate & 0xff); // samples[minutes].setProvider(dbHandler); + if (LOG.isDebugEnabled()) { + LOG.debug("sample: " + samples[minutes]); + } + // next minute minutes++; timestampInSeconds += 60; From 09c1717e68069acc75842c4a912cf3e732abd6a4 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 29 Jun 2016 23:26:59 +0200 Subject: [PATCH 099/569] Revert "Remove authors and link to contributors.md instead" This reverts commit 76dcb8f8286648db9c0ffc1346bd1953ca2e302a. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e765c19a..581ec928 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,11 @@ Known Issues: 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. -## Authors +## Authors (in order of first code contribution) -See [the list of contributors](https://raw.githubusercontent.com/Freeyourgadget/Gadgetbridge/master/CONTRIBUTORS.md) -in order of first code contribution. +* Andreas Shimokawa +* Carsten Pfeiffer +* Daniele Gobbetti ## Contribute From 9eb768ace00fe4f913c9b2f1118f36b15043d130 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 29 Jun 2016 23:58:47 +0200 Subject: [PATCH 100/569] update information about app management on FW 3.x --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 581ec928..7ad593ef 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ need to create an account and transmit any of your data to the vendor's servers. ## Notes about Firmware 3.x (Pebble Time, updated OG) -* Listing installed watchfaces will simply display previously installed watchapps, no matter if they are still installed or not. +* Gadgetbridge will keep track of installed watchfaces, but if the Pebble is used with another phone or another app, the information displayed in the app manager can get out of sync since it is impossible to query Firmware >= 3.x for installed apps/watchfaces. ## Getting Started (Pebble) From 8b24e098eabedc3cbdf05e653efda67c9cc53696 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 1 Jul 2016 00:22:54 +0200 Subject: [PATCH 101/569] Set sampleProvider to avoid NPEs --- .../devices/miband/operations/FetchActivityOperation.java | 2 +- .../service/devices/pebble/AppMessageHandlerGBPebble.java | 1 + .../service/devices/pebble/AppMessageHandlerMisfit.java | 3 ++- .../service/devices/pebble/AppMessageHandlerMorpheuz.java | 5 ++++- .../service/devices/pebble/DatalogSessionHealthSteps.java | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index a5de2974..a116ac59 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -335,7 +335,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { userId, deviceId, heartrate & 0xff); -// samples[minutes].setProvider(dbHandler); + samples[minutes].setProvider(provider); if (LOG.isDebugEnabled()) { LOG.debug("sample: " + samples[minutes]); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java index 4652a540..02713c43 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java @@ -67,6 +67,7 @@ public class AppMessageHandlerGBPebble extends AppMessageHandler { int intensity = ((sample & 0x1f80) >>> 7); int steps = (sample & 0x007f); activitySamples[i++] = createSample(timestamp + offset_seconds, intensity, steps, type, user, device); + activitySamples[i].setProvider(sampleProvider); offset_seconds += 60; } sampleProvider.addGBActivitySamples(activitySamples); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index a72ca21c..538a21b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -82,6 +82,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { int totalSteps = 0; PebbleActivitySample[] activitySamples = new PebbleActivitySample[samples]; try (DBHandler db = GBApplication.acquireDB()) { + MisfitSampleProvider sampleProvider = new MisfitSampleProvider(device, db.getDaoSession()); Long userId = DBHelper.getUser(db.getDaoSession()).getId(); Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); for (int i = 0; i < samples; i++) { @@ -115,10 +116,10 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { LOG.info("got steps for sample " + i + " : " + steps + "(" + Integer.toHexString(sample & 0xffff) + ")"); activitySamples[i] = new PebbleActivitySample(null, timestamp + i * 60, intensity, steps, activityKind, userId, deviceId); + activitySamples[i].setProvider(sampleProvider); } LOG.info("total steps for above period: " + totalSteps); - MisfitSampleProvider sampleProvider = new MisfitSampleProvider(device, db.getDaoSession()); sampleProvider.addGBActivitySamples(activitySamples); } catch (Exception e) { LOG.error("Error acquiring database", e); 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 a2b4c5e2..116153e9 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 @@ -19,6 +19,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSleepMonit import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MorpheuzSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -103,7 +104,9 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { User user = DBHelper.getUser(db.getDaoSession()); Device device = DBHelper.getDevice(getDevice(), db.getDaoSession()); MorpheuzSampleProvider sampleProvider = new MorpheuzSampleProvider(getDevice(), db.getDaoSession()); - sampleProvider.addGBActivitySample(createSample(recording_base_timestamp + index * 600, intensity, 0, type, user, device)); + PebbleActivitySample sample = createSample(recording_base_timestamp + index * 600, intensity, 0, type, user, device); + sample.setProvider(sampleProvider); + sampleProvider.addGBActivitySample(sample); } catch (Exception e) { LOG.error("Error acquiring database", e); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index 3a2551f2..c03c3ab7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -89,6 +89,7 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { stepsRecord.steps, sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), userId, deviceId); + samples[j].setProvider(sampleProvider); } sampleProvider.addGBActivitySamples(samples); From a2c2e48719a4068c10f98980575e2f15e102ac32 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 3 Jul 2016 21:29:02 +0200 Subject: [PATCH 102/569] fix NPE in settings when last known position is null This is not a proper fix, we would have to request a location first. I just had a last known position when testing before. :/ Issue #346 --- .../activities/SettingsActivity.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 8073d86f..b9d1eea2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -142,15 +142,19 @@ public class SettingsActivity extends AbstractSettingsActivity { String provider = locationManager.getBestProvider(criteria, false); if (provider != null) { Location location = locationManager.getLastKnownLocation(provider); - String latitude = String.format(Locale.US, "%.6g", location.getLatitude()); - String longitude = String.format(Locale.US, "%.6g", location.getLongitude()); - LOG.info("got location. Lat: " + latitude + " Lng: " + longitude); - EditTextPreference pref_latitude = (EditTextPreference) findPreference("location_latitude"); - EditTextPreference pref_longitude = (EditTextPreference) findPreference("location_longitude"); - pref_latitude.setText(latitude); - pref_longitude.setText(longitude); - pref_latitude.setSummary(latitude); - pref_longitude.setSummary(longitude); + if (location != null) { + String latitude = String.format(Locale.US, "%.6g", location.getLatitude()); + String longitude = String.format(Locale.US, "%.6g", location.getLongitude()); + LOG.info("got location. Lat: " + latitude + " Lng: " + longitude); + EditTextPreference pref_latitude = (EditTextPreference) findPreference("location_latitude"); + EditTextPreference pref_longitude = (EditTextPreference) findPreference("location_longitude"); + pref_latitude.setText(latitude); + pref_longitude.setText(longitude); + pref_latitude.setSummary(latitude); + pref_longitude.setSummary(longitude); + } else { + GB.toast(SettingsActivity.this, "no last known position", 3000, 0); + } } else { LOG.warn("No location provider found, did you deny location permission?"); } From 966b9abb87144aee99892464cd3169683c8a58e6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 3 Jul 2016 22:07:14 +0200 Subject: [PATCH 103/569] preferences: set longitude/latitude inputType to "numberDecimal|numberSigned" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit allows you to live west of 0°E ;) Also pops up a better keyboard --- app/src/main/res/xml/preferences.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index bf34e969..980d1c6c 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -190,13 +190,13 @@ android:title="@string/pref_title_location_aquire"/> From 73b2fc357ef58fb75b9ee64935712fc695a42008 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 4 Jul 2016 22:09:56 +0200 Subject: [PATCH 104/569] Pebble: Add some Pebble Time 2 support - NOT WORKING Not working because these are expected to use BLE. The Emulator should work though. --- .../gadgetbridge/devices/pebble/PBWReader.java | 3 +++ .../service/devices/pebble/PebbleProtocol.java | 10 +++++----- .../freeyourgadget/gadgetbridge/util/PebbleUtils.java | 4 ++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index 9e613a23..d6babc62 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 @@ -112,6 +112,9 @@ public class PBWReader { case "diorite": platformDirs = new String[]{"diorite/", "aplite/"}; break; + case "emery": + platformDirs = new String[]{"emery/", "basalt/"}; + break; default: platformDirs = new String[]{"aplite/"}; } 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 c57a25c8..fcaabe2f 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 @@ -252,16 +252,16 @@ public class PebbleProtocol extends GBDeviceProtocol { // base is -8 private static final String[] hwRevisions = { // Emulator - "silk_bb2", "unknown", "silk_bb", + "silk_bb2", "robert_bb", "silk_bb", "spalding_bb2", "snowy_bb2", "snowy_bb", "bb2", "bb", "unknown", - // Pebble + // Pebble Classic Series "ev1", "ev2", "ev2_3", "ev2_4", "v1_5", "v2_0", - // Pebble Time + // Pebble Time Series "snowy_evt2", "snowy_dvt", "spalding_dvt", "snowy_s3", "spalding", - // Pebble 2 - "silk_evt", "unknown", "silk" + // Pebble 2 Series + "silk_evt", "robert_evt", "silk" }; private static final Random mRandom = new Random(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java index 8a00a816..bd18cde2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -9,6 +9,8 @@ public class PebbleUtils { platformName = "chalk"; } else if (hwRev.startsWith("silk")) { platformName = "diorite"; + } else if (hwRev.startsWith("robert")) { + platformName = "emery"; } else { platformName = "aplite"; } @@ -24,6 +26,8 @@ public class PebbleUtils { model = "pebble_time_round_black_20mm"; } else if (hwRev.startsWith("silk")) { model = "pebble2_black"; + } else if (hwRev.startsWith("robert")) { + model = "pebble_time2_black"; } else { model = "pebble_black"; } From 91d1cea51f6459e2cf48f2bbbf3ceaca70d2e215 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 4 Jul 2016 22:40:01 +0200 Subject: [PATCH 105/569] Avoid potential NPEs --- .../service/devices/miband/MiBandSupport.java | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) 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 96996834..1307960f 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 @@ -817,9 +817,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } else if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { handleBatteryInfo(characteristic.getValue(), status); } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { - logHeartrate(characteristic.getValue()); + logHeartrate(characteristic.getValue(), status); } else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) { - logDate(characteristic.getValue()); + logDate(characteristic.getValue(), status); } else { LOG.info("Unhandled characteristic read: " + characteristicUUID); logMessageContent(characteristic.getValue()); @@ -845,27 +845,35 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { * @param value */ public void logMessageContent(byte[] value) { - LOG.info("RECEIVED DATA WITH LENGTH: " + value.length); - for (byte b : value) { - LOG.warn("DATA: " + String.format("0x%2x", b)); + LOG.info("RECEIVED DATA WITH LENGTH: " + ((value != null) ? value.length : "(null)")); + if (value != null) { + for (byte b : value) { + LOG.warn("DATA: " + String.format("0x%2x", b)); + } } } - public void logDate(byte[] value) { - GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value); - LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime())); - } - - public void logHeartrate(byte[] value) { - LOG.info("Got heartrate:"); - if (value.length == 2 && value[0] == 6) { - int hrValue = (value[1] & 0xff); - GB.toast(getContext(), "Heart Rate measured: " + hrValue, Toast.LENGTH_LONG, GB.INFO); + public void logDate(byte[] value, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value); + LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime())); } else { logMessageContent(value); } } + public void logHeartrate(byte[] value, int status) { + if (status == BluetoothGatt.GATT_SUCCESS && value != null) { + LOG.info("Got heartrate:"); + if (value.length == 2 && value[0] == 6) { + int hrValue = (value[1] & 0xff); + GB.toast(getContext(), "Heart Rate measured: " + hrValue, Toast.LENGTH_LONG, GB.INFO); + } + return; + } + logMessageContent(value); + } + private void handleHeartrate(byte[] value) { if (value.length == 2 && value[0] == 6) { int hrValue = (value[1] & 0xff); From 8549031c6f8aaed9b666319f2f77509c9640f632 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 4 Jul 2016 23:38:25 +0200 Subject: [PATCH 106/569] Only attempt to reconnect when we were previously initialized --- .../freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index fc7358a8..15e6df2b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -198,6 +198,7 @@ public final class BtLEQueue { if (mWaitForActionResultLatch != null) { mWaitForActionResultLatch.countDown(); } + boolean wasInitialized = mGbDevice.isInitialized(); setDeviceConnectionState(State.NOT_CONNECTED); // either we've been disconnected because the device is out of range @@ -207,7 +208,7 @@ public final class BtLEQueue { // reconnecting automatically, so we try to fix this by re-creating mBluetoothGatt. // Not sure if this actually works without re-initializing the device... if (status != 0) { - if (!maybeReconnect()) { + if (!wasInitialized || !maybeReconnect()) { disconnect(); // ensure that we start over cleanly next time } } From abeb642972f7122fa565b26c7e54b4cf3679ae9f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 5 Jul 2016 20:27:23 +0200 Subject: [PATCH 107/569] Don't remember paired Mi device in preferences (this was just a very old workaround for an even older Mi firmware that couldn't pair) --- .../gadgetbridge/devices/miband/MiBandPairingActivity.java | 5 ----- 1 file changed, 5 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 12d88683..0d22bef9 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 @@ -168,11 +168,6 @@ public class MiBandPairingActivity extends Activity { LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver); unregisterReceiver(mBondingReceiver); - if (pairedSuccessfully) { - Prefs prefs = GBApplication.getPrefs(); - prefs.getPreferences().edit().putString(MiBandConst.PREF_MIBAND_ADDRESS, macAddress).apply(); - } - Intent intent = new Intent(this, ControlCenter.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); finish(); From 9881b6c28189fd772224a04dab5807e8de5eb632 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 5 Jul 2016 20:46:47 +0200 Subject: [PATCH 108/569] Do not display paired devices in the discovery activity --- .../gadgetbridge/activities/DiscoveryActivity.java | 4 ++++ 1 file changed, 4 insertions(+) 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 3f00ced4..d77ea7a8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -179,6 +179,10 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC } private void handleDeviceFound(BluetoothDevice device, short rssi) { + if (device.getBondState() == BluetoothDevice.BOND_BONDED) { + return; // ignore already bonded devices + } + GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi); if (DeviceHelper.getInstance().isSupported(candidate)) { int index = deviceCandidates.indexOf(candidate); From 9ae69eac55d53973901c544499eecdd84851afaf Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 5 Jul 2016 21:03:30 +0200 Subject: [PATCH 109/569] Avoid NPEs when BluetoothDevice.getName() returns null --- .../nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index e4ab7360..3441e7a0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -59,7 +59,7 @@ public class GBDevice implements Parcelable { public GBDevice(String address, String name, DeviceType deviceType) { mAddress = address; - mName = name; + mName = (name != null) ? name : mAddress; mDeviceType = deviceType; validate(); } From 43f95aee9cc9a5bb59aaba0fe717a4adc493faf3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 5 Jul 2016 21:47:51 +0200 Subject: [PATCH 110/569] fix NPE --- .../gadgetbridge/activities/ControlCenter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 e63e00eb..47c94ff8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -72,8 +72,10 @@ public class ControlCenter extends GBActivity { case DeviceManager.ACTION_DEVICES_CHANGED: refreshPairedDevices(); GBDevice selectedDevice = deviceManager.getSelectedDevice(); - refreshBusyState(selectedDevice); - enableSwipeRefresh(selectedDevice); + if (selectedDevice != null) { + refreshBusyState(selectedDevice); + enableSwipeRefresh(selectedDevice); + } break; } } From 3bb1a228ec778999bdad1664dc64e320e280e960 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 5 Jul 2016 21:48:10 +0200 Subject: [PATCH 111/569] Fix crash during device discovery --- .../gadgetbridge/activities/ControlCenter.java | 2 +- .../gadgetbridge/devices/DeviceManager.java | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) 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 47c94ff8..73fd863c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -82,7 +82,7 @@ public class ControlCenter extends GBActivity { }; private void refreshBusyState(GBDevice dev) { - if (dev.isBusy()) { + if (dev != null && dev.isBusy()) { swipeLayout.setRefreshing(true); } else { boolean wasBusy = swipeLayout.isRefreshing(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java index a5f37715..99488931 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java @@ -5,6 +5,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; import org.slf4j.Logger; @@ -86,12 +87,13 @@ public class DeviceManager { if (selectedDevice == null) { selectedDevice = dev; } else { - if (!selectedDevice.equals(dev)) { + if (selectedDevice.equals(dev)) { + selectedDevice = dev; // equality vs identity! + } else { if (selectedDevice.isConnected() && dev.isConnected()) { LOG.warn("multiple connected devices -- this is currently not really supported"); selectedDevice = dev; // use the last one that changed - } - if (!selectedDevice.isConnected()) { + } else if (!selectedDevice.isConnected()) { selectedDevice = dev; // use the last one that changed } } @@ -118,6 +120,7 @@ public class DeviceManager { return Collections.unmodifiableList(deviceList); } + @Nullable public GBDevice getSelectedDevice() { return selectedDevice; } From 94cc1a883a97b85f32d520aeff44c754e9025781 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 5 Jul 2016 22:13:11 +0200 Subject: [PATCH 112/569] Use GBActivity in Pairing Activity for theming --- .../gadgetbridge/devices/miband/MiBandPairingActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 0d22bef9..f6d99403 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 @@ -21,12 +21,13 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity; +import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class MiBandPairingActivity extends Activity { +public class MiBandPairingActivity extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class); private static final int REQ_CODE_USER_SETTINGS = 52; From 903890067d9e64db0a82b3104c8560eebabc6c01 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 5 Jul 2016 23:14:48 +0200 Subject: [PATCH 113/569] Settings: properly check the input type flag Fixes a bug where latitude/longitude could be made empty --- .../gadgetbridge/activities/AbstractSettingsActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index 965a0cfd..8f008567 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -44,7 +44,7 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { @Override public boolean onPreferenceChange(Preference preference, Object value) { if (preference instanceof EditTextPreference) { - if (((EditTextPreference) preference).getEditText().getKeyListener().getInputType() == InputType.TYPE_CLASS_NUMBER) { + if ((((EditTextPreference) preference).getEditText().getKeyListener().getInputType() & InputType.TYPE_CLASS_NUMBER) != 0) { if ("".equals(String.valueOf(value))) { // reject empty numeric input return false; From 154b7d28bb47ff794f5bba2226da8058f571691b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 5 Jul 2016 22:39:05 +0200 Subject: [PATCH 114/569] Let discovery activity also display device aliases --- .../activities/DiscoveryActivity.java | 1 + .../gadgetbridge/impl/GBDeviceCandidate.java | 22 ++++++++++++++----- .../gadgetbridge/util/DeviceHelper.java | 17 ++------------ 3 files changed, 19 insertions(+), 21 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 d77ea7a8..12a7f9e3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -179,6 +179,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC } private void handleDeviceFound(BluetoothDevice device, short rssi) { + LOG.debug("found device: " + device.getName() + ", " + device.getAddress()); if (device.getBondState() == BluetoothDevice.BOND_BONDED) { return; // ignore already bonded devices } 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 286e5897..7820bc2a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -8,6 +8,8 @@ import android.os.Parcelable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -76,14 +78,22 @@ public class GBDeviceCandidate implements Parcelable { } public String getName() { - String name = null; - if (device != null) { - name = device.getName(); + String deviceName = null; + try { + Method method = device.getClass().getMethod("getAliasName"); + if (method != null) { + deviceName = (String) method.invoke(device); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignore) { + LOG.info("Could not get device alias for " + deviceName); } - if (name == null || name.length() == 0) { - name = GBApplication.getContext().getString(R.string._unknown_); + if (deviceName == null || deviceName.length() == 0) { + deviceName = device.getName(); } - return name; + if (deviceName == null || deviceName.length() == 0) { + deviceName = "(unknown)"; + } + return deviceName; } public short getRssi() { 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 a562e893..21addeea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -8,8 +8,6 @@ import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -18,7 +16,6 @@ import java.util.Set; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; @@ -115,22 +112,12 @@ public class DeviceHelper { public GBDevice toSupportedDevice(BluetoothDevice device) { GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN); - String deviceName = device.getName(); - try { - Method method = device.getClass().getMethod("getAliasName"); - if (method != null) { - deviceName = (String) method.invoke(device); - } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignore) { - LOG.info("Could not get device alias for " + deviceName); - } - if (coordinator != null && coordinator.supports(candidate)) { - return new GBDevice(device.getAddress(), deviceName, coordinator.getDeviceType()); + return new GBDevice(device.getAddress(), candidate.getName(), coordinator.getDeviceType()); } for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(candidate)) { - return new GBDevice(device.getAddress(), deviceName, coordinator.getDeviceType()); + return new GBDevice(device.getAddress(), candidate.getName(), coordinator.getDeviceType()); } } return null; From 20d8732d104729d4a1239a6168a23d86f61b2ae1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 5 Jul 2016 23:35:20 +0200 Subject: [PATCH 115/569] In GBDevice.toString(), return the correct state instead of the simplified one --- .../gadgetbridge/impl/GBDevice.java | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 3441e7a0..5297f8e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -214,25 +214,41 @@ public class GBDevice implements Parcelable { } public String getStateString() { - /* - * for simplicity the user wont see all internal states, just connecting -> connected - * instead of connecting->connected->initializing->initialized - */ + return getStateString(true); + } + + /** + * for simplicity the user wont see all internal states, just connecting -> connected + * instead of connecting->connected->initializing->initialized + * Set simple to true to get this behavior. + */ + private String getStateString(boolean simple) { switch (mState) { case NOT_CONNECTED: return GBApplication.getContext().getString(R.string.not_connected); case WAITING_FOR_RECONNECT: return GBApplication.getContext().getString(R.string.waiting_for_reconnect); case CONNECTING: - case CONNECTED: - case INITIALIZING: return GBApplication.getContext().getString(R.string.connecting); + case CONNECTED: + if (simple) { + return GBApplication.getContext().getString(R.string.connecting); + } + return GBApplication.getContext().getString(R.string.connected); + case INITIALIZING: + if (simple) { + return GBApplication.getContext().getString(R.string.connecting); + } + return GBApplication.getContext().getString(R.string.initializing); case AUTHENTICATION_REQUIRED: return GBApplication.getContext().getString(R.string.authentication_required); case AUTHENTICATING: return GBApplication.getContext().getString(R.string.authenticating); case INITIALIZED: - return GBApplication.getContext().getString(R.string.connected); + if (simple) { + return GBApplication.getContext().getString(R.string.connected); + } + return GBApplication.getContext().getString(R.string.initialized); } return GBApplication.getContext().getString(R.string.unknown_state); } @@ -323,7 +339,7 @@ public class GBDevice implements Parcelable { @Override public String toString() { - return "Device " + getName() + ", " + getAddress() + ", " + getStateString(); + return "Device " + getName() + ", " + getAddress() + ", " + getStateString(false); } /** From 4de45787c3c81ed33906060b94142f7093fdbe1c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 5 Jul 2016 23:52:48 +0200 Subject: [PATCH 116/569] Properly acquire network location if last location is not known A toast will be shown if the network location provider is disabled. Location will be automatically acquired after enabling it. Fixes #346 --- .../activities/SettingsActivity.java | 46 +++++++++++++++---- app/src/main/res/values/strings.xml | 3 ++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index b9d1eea2..990c9085 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -7,6 +7,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.location.Criteria; import android.location.Location; +import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.preference.EditTextPreference; @@ -143,17 +144,30 @@ public class SettingsActivity extends AbstractSettingsActivity { if (provider != null) { Location location = locationManager.getLastKnownLocation(provider); if (location != null) { - String latitude = String.format(Locale.US, "%.6g", location.getLatitude()); - String longitude = String.format(Locale.US, "%.6g", location.getLongitude()); - LOG.info("got location. Lat: " + latitude + " Lng: " + longitude); - EditTextPreference pref_latitude = (EditTextPreference) findPreference("location_latitude"); - EditTextPreference pref_longitude = (EditTextPreference) findPreference("location_longitude"); - pref_latitude.setText(latitude); - pref_longitude.setText(longitude); - pref_latitude.setSummary(latitude); - pref_longitude.setSummary(longitude); + setLocationPreferences(location); } else { - GB.toast(SettingsActivity.this, "no last known position", 3000, 0); + locationManager.requestSingleUpdate(provider, new LocationListener() { + @Override + public void onLocationChanged(Location location) { + setLocationPreferences(location); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + LOG.info("provider status changed to " + status + " (" + provider + ")"); + } + + @Override + public void onProviderEnabled(String provider) { + LOG.info("provider enabled (" + provider + ")"); + } + + @Override + public void onProviderDisabled(String provider) { + LOG.info("provider disabled (" + provider + ")"); + GB.toast(SettingsActivity.this, getString(R.string.toast_enable_networklocationprovider), 3000, 0); + } + }, null); } } else { LOG.warn("No location provider found, did you deny location permission?"); @@ -255,4 +269,16 @@ public class SettingsActivity extends AbstractSettingsActivity { }; } + private void setLocationPreferences(Location location) { + String latitude = String.format(Locale.US, "%.6g", location.getLatitude()); + String longitude = String.format(Locale.US, "%.6g", location.getLongitude()); + LOG.info("got location. Lat: " + latitude + " Lng: " + longitude); + GB.toast(SettingsActivity.this, getString(R.string.toast_aqurired_networklocation), 2000, 0); + EditTextPreference pref_latitude = (EditTextPreference) findPreference("location_latitude"); + EditTextPreference pref_longitude = (EditTextPreference) findPreference("location_longitude"); + pref_latitude.setText(latitude); + pref_longitude.setText(longitude); + pref_latitude.setSummary(latitude); + pref_longitude.setSummary(longitude); + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb3f700d..842f3299 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -95,6 +95,9 @@ Latitude Longitude + Please enable network location + location acquired + Force Notification Protocol This option forces using the latest notification protocol depending on the firmware version. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING! Enable untested features From 26bab26917c307f229e8c713a7e1cfe157dad996 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 6 Jul 2016 22:35:41 +0200 Subject: [PATCH 117/569] bump version update changelog --- CHANGELOG.md | 4 ++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 143ababd..b7fee1b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ###Changelog +####Version 0.11.1 +* Various fixes (including crashes) for location settings +* Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) + ####Version 0.11.0 * Pebble: new App Manager (keeps track of installed apps and allows app sorting on FW 3.x) * Pebble: call dismissal with canned SMS (FW 3.x) diff --git a/app/build.gradle b/app/build.gradle index 46f8286b..92f82f8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.11.0" - versionCode 56 + versionName "0.11.1" + versionCode 57 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 9e499d93..6679f3fb 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,9 @@ + + Various fixes (including crashes) for location settings + Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) + Pebble: new App Manager (keeps track of installed apps and allows app sorting on FW 3.x) Pebble: call dismissal with canned SMS (FW 3.x) From 8ea0fa46fbfdaee2487f28086a4d80d8b7b563e2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 6 Jul 2016 22:36:29 +0200 Subject: [PATCH 118/569] update spanish translation from transifex (THANKS!) --- app/src/main/res/values-es/strings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index eb2e87bd..975150f4 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -53,7 +53,11 @@ cuando la pantalla está apagada nunca Excluir aplicaciones + Mensajes preservados + Respuestas Sufijo habitual + Rechazar llamada + Actualizar en Pebble Opciones de desarrollador Dirección de MiBand Ajustes de Pebble @@ -244,4 +248,7 @@ Firmware no enviado Pulsaciones Pulsaciones + Apps en caché + Apps instaladas + Esferas instaladas From 31c9d7ed3b0e7011962f9d3dd7751a1b9783aae6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 6 Jul 2016 23:57:32 +0200 Subject: [PATCH 119/569] Updated changelog for 0.11.1 (cherry picked from commit 563af6d0175b5ff415085fe562f4cf03ab364098) --- CHANGELOG.md | 9 +++++++++ app/src/main/res/xml/changelog_master.xml | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7fee1b4..dc7a1987 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ ####Version 0.11.1 * Various fixes (including crashes) for location settings * Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) +* Fix a rare crash when, due to bluetooth problems, a device has no name +* Fix activity fetching getting stuck when double tapping (#333) +* Mi Band: in the Device Discovery activity, do not display devices that are already paired +* Mi Band: only allow automatic reconnection on disconnect when the device was previously fully connected +* Mi Band: fix a rare crash when reading data fails due to bluetooth problems +* Mi Band: log full activity sample to help deciphering activity kinds (#341) +* Mi Band 2: improved discovery mechanism to not rely on mac addresses (#323) +* Charts: only display heart rate samples on devices that support that +* Add more logging to detect problems with external directories (#343) ####Version 0.11.0 * Pebble: new App Manager (keeps track of installed apps and allows app sorting on FW 3.x) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 6679f3fb..3b98e4b0 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -3,7 +3,16 @@ Various fixes (including crashes) for location settings Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) - + Fix a rare crash when, due to bluetooth problems, a device has no name + Fix activity fetching getting stuck when double tapping (#333) + Mi Band: in the Device Discovery activity, do not display devices that are already paired + Mi Band: only allow automatic reconnection on disconnect when the device was previously fully connected + Mi Band: fix a rare crash when reading data fails due to bluetooth problems + Mi Band: log full activity sample to help deciphering activity kinds (#341) + Mi Band 2: improved discovery mechanism to not rely on mac addresses (#323) + Charts: only display heart rate samples on devices that support that + Add more logging to detect problems with external directories (#343) + Pebble: new App Manager (keeps track of installed apps and allows app sorting on FW 3.x) Pebble: call dismissal with canned SMS (FW 3.x) From ce47f62c5bf9616ecf7892539d923abcf85167c3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 8 Jul 2016 22:01:01 +0200 Subject: [PATCH 120/569] Missed a word in changelog --- CHANGELOG.md | 2 +- app/src/main/res/xml/changelog_master.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc7a1987..e0cb07a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ####Version 0.11.1 * Various fixes (including crashes) for location settings * Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) -* Fix a rare crash when, due to bluetooth problems, a device has no name +* Fix a rare crash when, due to bluetooth problems, when a device has no name * Fix activity fetching getting stuck when double tapping (#333) * Mi Band: in the Device Discovery activity, do not display devices that are already paired * Mi Band: only allow automatic reconnection on disconnect when the device was previously fully connected diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 3b98e4b0..84dacbd6 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -3,7 +3,7 @@ Various fixes (including crashes) for location settings Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) - Fix a rare crash when, due to bluetooth problems, a device has no name + Fix a rare crash when, due to bluetooth problems, when a device has no name Fix activity fetching getting stuck when double tapping (#333) Mi Band: in the Device Discovery activity, do not display devices that are already paired Mi Band: only allow automatic reconnection on disconnect when the device was previously fully connected From 8154a887cb95fb8673665be14b8e17aeea7cee9e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 8 Jul 2016 22:15:36 +0200 Subject: [PATCH 121/569] When there are cached services, skip service discovery --- .../gadgetbridge/service/btle/BtLEQueue.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index 15e6df2b..21c9a3ae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -330,8 +330,14 @@ public final class BtLEQueue { LOG.info("Connected to GATT server."); setDeviceConnectionState(State.CONNECTED); // Attempts to discover services after successful connection. - LOG.info("Attempting to start service discovery:" + - gatt.discoverServices()); + List cachedServices = gatt.getServices(); + if (cachedServices != null && cachedServices.size() > 0) { + LOG.info("Using cached services, skipping discovery"); + onServicesDiscovered(gatt, BluetoothGatt.GATT_SUCCESS); + } else { + LOG.info("Attempting to start service discovery:" + + gatt.discoverServices()); + } break; case BluetoothProfile.STATE_DISCONNECTED: LOG.info("Disconnected from GATT server."); From 921523334447222ba530336d3fbc0e748842b2d2 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 8 Jul 2016 22:17:19 +0200 Subject: [PATCH 122/569] Whitelist 4.16.3.7 Mi1S firmware --- .../gadgetbridge/devices/miband/MiBandFWHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index 20ff39d9..41ba4451 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -47,6 +47,7 @@ public class MiBandFWHelper { 16779782, //1.0.10.6 reported on the wiki 16779787, //1.0.10.11 tested by developer //FW_16779790, //1.0.10.14 reported on the wiki (vibration does not work currently) + 68158215, // 4.16.3.7 tested by developer 84870926, // 5.15.7.14 tested by developer }; From f54163faeb84c265eaf37aa6eeb09512d8d5caa2 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 8 Jul 2016 22:35:52 +0200 Subject: [PATCH 123/569] centralize quit() functionality in GBApplication --- .../gadgetbridge/GBApplication.java | 23 +++++-------------- .../activities/ControlCenter.java | 5 +--- .../BluetoothStateChangeReceiver.java | 6 +---- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index df26fc15..8f756b4b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -77,20 +77,13 @@ public class GBApplication extends Application { } }; private static DeviceManager deviceManager; - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - switch (action) { - case ACTION_QUIT: - quit(); - break; - } - } - }; - private void quit() { - GB.removeAllNotifications(this); + public static void quit() { + GB.log("Quitting Gadgetbridge...", GB.INFO, null); + Intent quitIntent = new Intent(GBApplication.ACTION_QUIT); + LocalBroadcastManager.getInstance(context).sendBroadcast(quitIntent); + GBApplication.deviceService().quit(); + GB.removeAllNotifications(context); } public GBApplication() { @@ -130,10 +123,6 @@ public class GBApplication extends Application { // mActivityDatabaseHandler = new ActivityDatabaseHandler(context); loadBlackList(); - IntentFilter filterLocal = new IntentFilter(); - filterLocal.addAction(ACTION_QUIT); - LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal); - if (isRunningMarshmallowOrLater()) { notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); } 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 73fd863c..ea13096c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -311,10 +311,7 @@ public class ControlCenter extends GBActivity { startActivity(debugIntent); return true; case R.id.action_quit: - GBApplication.deviceService().quit(); - - Intent quitIntent = new Intent(GBApplication.ACTION_QUIT); - LocalBroadcastManager.getInstance(this).sendBroadcast(quitIntent); + GBApplication.quit(); return true; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java index 08a11ad8..0e639733 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java @@ -28,11 +28,7 @@ public class BluetoothStateChangeReceiver extends BroadcastReceiver { GBApplication.deviceService().connect(); } else if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) { - GBApplication.deviceService().quit(); - - Intent quitIntent = new Intent(GBApplication.ACTION_QUIT); - - LocalBroadcastManager.getInstance(context).sendBroadcast(quitIntent); + GBApplication.quit(); } } } From 340a0f4a668a004e4daa960146c5ab278e27a4bb Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 9 Jul 2016 23:10:21 +0200 Subject: [PATCH 124/569] Fix coordinators not recognitzing devices by name Do not ask a device candidate for its name , ask the underlying BluetoothDevice The candidate uses the device alias - not good for matching --- .../gadgetbridge/devices/miband/MiBandCoordinator.java | 4 +--- .../gadgetbridge/devices/pebble/PebbleCoordinator.java | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) 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 509f4e68..5d001c7d 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 @@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; @@ -18,7 +17,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -42,7 +40,7 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { // and a heuristic try { if (isHealthWearable(candidate.getDevice())) { - return candidate.getName().toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase()); + return candidate.getDevice().getName().toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase()); } } catch (Exception ex) { LOG.error("unable to check device support", ex); 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 61355398..3d0ae971 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 @@ -23,7 +23,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { @Override public boolean supports(GBDeviceCandidate candidate) { - return candidate.getName().startsWith("Pebble"); + return candidate.getDevice().getName().startsWith("Pebble"); } @Override From eb7b4be98675aa7d410a4a6bb74fa6f66fe9308e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 8 Jul 2016 22:44:01 +0200 Subject: [PATCH 125/569] No restart necessary after enabling logging --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ad593ef..bb3b138d 100644 --- a/README.md +++ b/README.md @@ -119,10 +119,9 @@ Feel free to open an issue on our issue tracker, but please: ## Having problems? 1. Open Gadgetbridge's settings and check the option to write log files -2. Quit Gadgetbridge and restart it -3. Reproduce the problem you encountered -4. Check the logfile at /sdcard/Android/data/nodomain.freeyourgadget.gadgetbridge/files/gadgetbridge.log -5. File an issue at https://github.com/Freeyourgadget/Gadgetbridge/issues/new and possibly provide the logfile +2. Reproduce the problem you encountered +3. Check the logfile at /sdcard/Android/data/nodomain.freeyourgadget.gadgetbridge/files/gadgetbridge.log +4. File an issue at https://github.com/Freeyourgadget/Gadgetbridge/issues/new and possibly provide the logfile Alternatively you may use the standard logcat functionality to access the log. From 80930ce42af3f05e98d95ea71bb4ada8273ae969 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 10 Jul 2016 00:10:50 +0200 Subject: [PATCH 126/569] More logging for pairing, destroy pairing activity when bonding failed #349 --- .../devices/miband/MiBandPairingActivity.java | 16 +++++++++++++--- 1 file changed, 13 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 f6d99403..db381426 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 @@ -56,6 +56,7 @@ public class MiBandPairingActivity extends GBActivity { public void onReceive(Context context, Intent intent) { if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + LOG.info("Bond state changed: " + device + ", state: " + device.getBondState() + ", expected address: " + bondingMacAddress); if (bondingMacAddress != null && bondingMacAddress.equals(device.getAddress())) { int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); if (bondState == BluetoothDevice.BOND_BONDED) { @@ -68,6 +69,13 @@ public class MiBandPairingActivity extends GBActivity { performPair(); } }, DELAY_AFTER_BONDING); + } else if (bondState == BluetoothDevice.BOND_BONDING) { + LOG.info("Bonding in progress with " + device.getAddress()); + } else if (bondState == BluetoothDevice.BOND_NONE) { + LOG.info("Not bonded with " + device.getAddress() + ", aborting bonding."); + pairingFinished(false); + } else { + LOG.warn("Unknown bond state for device " + device.getAddress() + ": " + bondState); } } } @@ -169,8 +177,10 @@ public class MiBandPairingActivity extends GBActivity { LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver); unregisterReceiver(mBondingReceiver); - Intent intent = new Intent(this, ControlCenter.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); + if (pairedSuccessfully) { + Intent intent = new Intent(this, ControlCenter.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } finish(); } @@ -182,7 +192,7 @@ public class MiBandPairingActivity extends GBActivity { protected void performBluetoothPair(BluetoothDevice device) { int bondState = device.getBondState(); if (bondState == BluetoothDevice.BOND_BONDED) { - LOG.info("Already bonded: " + device.getAddress()); + GB.toast("Already bonded with " + device.getName() + " (" + device.getAddress() + "), connecting...", Toast.LENGTH_SHORT, GB.INFO); performPair(); return; } From 76895aa2b13262d3111a2427f583aafc47da994c Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 11 Jul 2016 00:28:15 +0200 Subject: [PATCH 127/569] Register to device name and alias changes and update accordingly --- .../gadgetbridge/devices/DeviceManager.java | 28 +++++++++++++++++-- .../gadgetbridge/impl/GBDevice.java | 10 ++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java index 99488931..f9856d4f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java @@ -27,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; public class DeviceManager { private static final Logger LOG = LoggerFactory.getLogger(DeviceManager.class); + public static final String BLUETOOTH_DEVICE_ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED"; /** * Intent action to notify that the list of devices has changed. */ @@ -50,10 +51,16 @@ public class DeviceManager { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { - case ACTION_REFRESH_DEVICELIST: + case ACTION_REFRESH_DEVICELIST: // fall through case BluetoothDevice.ACTION_BOND_STATE_CHANGED: refreshPairedDevices(); break; + case BluetoothDevice.ACTION_NAME_CHANGED: + case BLUETOOTH_DEVICE_ACTION_ALIAS_CHANGED: + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + String newName = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); + updateDeviceName(device, newName); + break; case GBDevice.ACTION_DEVICE_CHANGED: GBDevice dev = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE); if (dev.getAddress() != null) { @@ -78,11 +85,28 @@ public class DeviceManager { filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED); filterLocal.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); LocalBroadcastManager.getInstance(context).registerReceiver(mReceiver, filterLocal); - context.registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)); + + IntentFilter filterGlobal = new IntentFilter(); + filterGlobal.addAction(BluetoothDevice.ACTION_NAME_CHANGED); + filterGlobal.addAction(BLUETOOTH_DEVICE_ACTION_ALIAS_CHANGED); + filterGlobal.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + context.registerReceiver(mReceiver, filterGlobal); refreshPairedDevices(); } + private void updateDeviceName(BluetoothDevice device, String newName) { + for (GBDevice dev : deviceList) { + if (device.getAddress().equals(dev.getAddress())) { + if (!dev.getName().equals(newName)) { + dev.setName(newName); + notifyDevicesChanged(); + return; + } + } + } + } + private void updateSelectedDevice(GBDevice dev) { if (selectedDevice == null) { selectedDevice = dev; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 5297f8e4..53b16cbd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -43,7 +43,7 @@ public class GBDevice implements Parcelable { private static final String DEVINFO_HW_VER = "HW: "; private static final String DEVINFO_FW_VER = "FW: "; private static final String DEVINFO_ADDR = "ADDR: "; - private final String mName; + private String mName; private final String mAddress; private final DeviceType mDeviceType; private String mFirmwareVersion; @@ -109,6 +109,14 @@ public class GBDevice implements Parcelable { return mName; } + public void setName(String name) { + if (name == null) { + LOG.warn("Ignoring setting of GBDevice name to null for " + this); + return; + } + mName = name; + } + public String getAddress() { return mAddress; } From aa00d2f93a477e829193e1e63f3ce0a36af148e6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 12 Jul 2016 00:24:23 +0200 Subject: [PATCH 128/569] Avoid NPEs when device-name is null --- .../gadgetbridge/devices/miband/MiBandCoordinator.java | 7 +++++-- .../gadgetbridge/devices/pebble/PebbleCoordinator.java | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) 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 5d001c7d..910c80f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; import android.app.Activity; +import android.bluetooth.BluetoothDevice; import android.content.Context; import android.net.Uri; @@ -39,8 +40,10 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { } // and a heuristic try { - if (isHealthWearable(candidate.getDevice())) { - return candidate.getDevice().getName().toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase()); + BluetoothDevice device = candidate.getDevice(); + if (isHealthWearable(device)) { + String name = device.getName(); + return name != null && name.toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase()); } } catch (Exception ex) { LOG.error("unable to check device support", ex); 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 3d0ae971..a1425951 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 @@ -23,7 +23,8 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { @Override public boolean supports(GBDeviceCandidate candidate) { - return candidate.getDevice().getName().startsWith("Pebble"); + String name = candidate.getDevice().getName(); + return name != null && name.startsWith("Pebble"); } @Override From 367091587fb5dbfefb158f5b57567b6735e00636 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 14 Jul 2016 20:15:54 +0200 Subject: [PATCH 129/569] No more 0x8 in the logs It's the confirmation that setting the latency succeeded. --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 3 +++ 1 file changed, 3 insertions(+) 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 1307960f..5a1ad7dc 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 @@ -939,6 +939,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { getDevice().sendDeviceUpdateIntent(getContext()); break; + case MiBandService.NOTIFY_SET_LATENCY_SUCCESS: + LOG.info("Setting latency succeeded."); + break; default: for (byte b : value) { LOG.warn("DATA: " + String.format("0x%2x", b)); From ebda3e1535cf010d2db464778bb1603953c45cf8 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 14 Jul 2016 20:37:20 +0200 Subject: [PATCH 130/569] uncomment some constants --- .../gadgetbridge/devices/miband/MiBandService.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java index 39d5bd69..366d920d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java @@ -209,17 +209,15 @@ public class MiBandService { */ - /* TEST: unkown (maybe sent to UUID_CHARACTERISTIC_TEST characteristic? + // TEST_*: sent to UUID_CHARACTERISTIC_TEST characteristic - public static final TEST_DISCONNECTED_REMINDER = 0x5t + public static final byte TEST_DISCONNECTED_REMINDER = 0x5; - public static final TEST_NOTIFICATION = 0x3t + public static final byte TEST_NOTIFICATION = 0x3; - public static final TEST_REMOTE_DISCONNECT = 0x1t + public static final byte TEST_REMOTE_DISCONNECT = 0x1; - public static final TEST_SELFTEST = 0x2t - - */ + public static final byte TEST_SELFTEST = 0x2; private static final Map MIBAND_DEBUG; From e8a4c2851059b898e0a17c33f6f70f619d3198ed Mon Sep 17 00:00:00 2001 From: Roman Plevka Date: Thu, 14 Jul 2016 23:15:36 +0200 Subject: [PATCH 131/569] Fixed 'Activiy' typo 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 842f3299..c64cd3e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -117,7 +117,7 @@ Bluetooth is not supported. Bluetooth is disabled. Tap connected device for App Manager - Tap connected device for Activiy + Tap connected device for Activity Tap a device to connect Cannot connect. BT address invalid? Gadgetbridge running From 7b26986ab04ccb90db62084d60c583d2917d236a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 14 Jul 2016 23:53:25 +0200 Subject: [PATCH 132/569] Fix for #349 We must not use UUID_CHAR_PAIR anymore. This prevents connecting without being bonded. Connecting when bonded still works. As without bonding, ControlCenter would not display the device anymore, we have to re-install the "remember last connected device" in the preferences thing. --- .../devices/miband/MiBandPairingActivity.java | 44 +++++++++++++------ .../service/devices/miband/MiBandSupport.java | 4 +- 2 files changed, 34 insertions(+), 14 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 db381426..48ab4078 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 @@ -1,6 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; -import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; @@ -44,8 +43,12 @@ 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()) && device.isInitialized()) { - pairingFinished(true); + if (macAddress.equals(device.getAddress())) { + if (device.isInitialized()) { + pairingFinished(true, macAddress); + } else if (device.isConnecting() || device.isInitializing()) { + LOG.info("still connecting/initializing device..."); + } } } } @@ -62,26 +65,32 @@ public class MiBandPairingActivity extends GBActivity { if (bondState == BluetoothDevice.BOND_BONDED) { LOG.info("Bonded with " + device.getAddress()); bondingMacAddress = null; - Looper mainLooper = Looper.getMainLooper(); - new Handler(mainLooper).postDelayed(new Runnable() { - @Override - public void run() { - performPair(); - } - }, DELAY_AFTER_BONDING); + attemptToConnect(); } else if (bondState == BluetoothDevice.BOND_BONDING) { LOG.info("Bonding in progress with " + device.getAddress()); } else if (bondState == BluetoothDevice.BOND_NONE) { - LOG.info("Not bonded with " + device.getAddress() + ", aborting bonding."); - pairingFinished(false); + LOG.info("Not bonded with " + device.getAddress() + ", attempting to connect anyway."); + bondingMacAddress = null; + attemptToConnect(); } else { LOG.warn("Unknown bond state for device " + device.getAddress() + ": " + bondState); + pairingFinished(false, bondingMacAddress); } } } } }; + private void attemptToConnect() { + Looper mainLooper = Looper.getMainLooper(); + new Handler(mainLooper).postDelayed(new Runnable() { + @Override + public void run() { + performPair(); + } + }, DELAY_AFTER_BONDING); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -166,7 +175,7 @@ public class MiBandPairingActivity extends GBActivity { } } - private void pairingFinished(boolean pairedSuccessfully) { + private void pairingFinished(boolean pairedSuccessfully, String macAddress) { LOG.debug("pairingFinished: " + pairedSuccessfully); if (!isPairing) { // already gone? @@ -178,6 +187,14 @@ public class MiBandPairingActivity extends GBActivity { unregisterReceiver(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. + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); + if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) { + Prefs prefs = GBApplication.getPrefs(); + prefs.getPreferences().edit().putString(MiBandConst.PREF_MIBAND_ADDRESS, macAddress).apply(); + } Intent intent = new Intent(this, ControlCenter.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); } @@ -210,6 +227,7 @@ public class MiBandPairingActivity extends GBActivity { } private void performPair() { + GBApplication.deviceService().disconnect(); // just to make sure... GBApplication.deviceService().connect(macAddress, true); } } 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 5a1ad7dc..44716f80 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 @@ -109,7 +109,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { enableNotifications(builder, true) .setLowLatency(builder) .readDate(builder) // without reading the data, we get sporadic connection problems, especially directly after turning on BT - .pair(builder) +// this is apparently not needed anymore, and actually causes problems when bonding is not used/does not work +// so we simply not use the UUID_PAIR characteristic. +// .pair(builder) .requestDeviceInfo(builder) .sendUserInfo(builder) .checkAuthenticationNeeded(builder, getDevice()) From 802314fc13f0c9986c6bb7a143354106edfc43f1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 15 Jul 2016 00:48:50 +0200 Subject: [PATCH 133/569] Updates for 0.11.2 release --- CHANGELOG.md | 3 +++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0cb07a9..cd9ac95c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ###Changelog +####Version 0.11.2 +Mi Band: support for devices that cannot pair with the band (#349) + ####Version 0.11.1 * Various fixes (including crashes) for location settings * Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) diff --git a/app/build.gradle b/app/build.gradle index 92f82f8a..b9e2ea4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.11.1" - versionCode 57 + versionName "0.11.2" + versionCode 58 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 84dacbd6..b20ff6b6 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + Mi Band: support for devices that cannot pair with the band (#349) + Various fixes (including crashes) for location settings Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge) From 1997a9b7fa6f423b74e678389848fd461fc81ee3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 18 Jul 2016 23:55:44 +0200 Subject: [PATCH 134/569] some more service discovery logging --- .../gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java index 26118112..03918682 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java @@ -148,12 +148,14 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im private void gattServicesDiscovered(List discoveredGattServices) { if (discoveredGattServices == null) { + LOG.warn("No gatt services discovered: null!"); return; } Set supportedServices = getSupportedServices(); mAvailableCharacteristics = new HashMap<>(); for (BluetoothGattService service : discoveredGattServices) { if (supportedServices.contains(service.getUuid())) { + LOG.debug("discovered supported service: " + service.getUuid()); List characteristics = service.getCharacteristics(); if (characteristics == null || characteristics.isEmpty()) { LOG.warn("Supported LE service " + service.getUuid() + "did not return any characteristics"); @@ -164,6 +166,8 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im intmAvailableCharacteristics.put(characteristic.getUuid(), characteristic); } mAvailableCharacteristics.putAll(intmAvailableCharacteristics); + } else { + LOG.debug("discovered unsupported service: " + service.getUuid()); } } } From df59ce7b969627e154ae97d72081443f77f241fa Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 19 Jul 2016 20:43:28 +0200 Subject: [PATCH 135/569] Switch light sleep and deep sleep #250 Apparently REM is considered deep sleep and NREM is considered light sleep even though NREM (non-rapid-eye-movement) phase 3 is actually defined as deep sleep. --- .../gadgetbridge/devices/miband/MiBandSampleProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java index 7cc06141..13f77224 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java @@ -11,8 +11,8 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class MiBandSampleProvider extends AbstractSampleProvider { - public static final int TYPE_DEEP_SLEEP = 5; - public static final int TYPE_LIGHT_SLEEP = 4; + public static final int TYPE_DEEP_SLEEP = 4; + public static final int TYPE_LIGHT_SLEEP = 5; public static final int TYPE_ACTIVITY = -1; public static final int TYPE_UNKNOWN = -1; public static final int TYPE_NONWEAR = 3; From fd1e0e5648423ddb92539bd7ab43dbe7b4b8b658 Mon Sep 17 00:00:00 2001 From: Ivan Date: Fri, 22 Jul 2016 20:13:08 +0300 Subject: [PATCH 136/569] Update strings.xml --- app/src/main/res/values-ru/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7733f1e1..c33c875e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -181,9 +181,9 @@ История шагов за минуту Начните вашу активность Активность - Неглубокий сон + Легкий сон Глубокий сон - Не носилось + Не носился Не подключен. Все будильники отключены Храните данные о деятельности на устройстве From f5ba09ebe0ea8e57e4bc7a58d512534e597cb6dd Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 25 Jul 2016 00:00:22 +0200 Subject: [PATCH 137/569] Some babysteps towards miband2 support #323 Start to implement standard BLE profiles/services. --- .../devices/miband/MiBandService.java | 5 +- .../btle/AbstractBTLEDeviceSupport.java | 34 +- .../service/btle/BleNamesResolver.java | 203 +++ .../service/btle/GattCharacteristic.java | 5 + .../btle/profiles/AbstractBleProfile.java | 73 + .../service/btle/profiles/ValueDecoder.java | 21 + .../btle/profiles/battery/BatteryInfo.java | 46 + .../profiles/battery/BatteryInfoProfile.java | 71 + .../btle/profiles/deviceinfo/DeviceInfo.java | 133 ++ .../deviceinfo/DeviceInfoProfile.java | 148 ++ .../devices/miband/MiBand2Support.java | 1185 +++++++++++++++++ .../miband/V1NotificationStrategy.java | 5 +- .../miband/V2NotificationStrategy.java | 5 +- 13 files changed, 1925 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java index 366d920d..41bf6a90 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java @@ -13,8 +13,9 @@ public class MiBandService { public static final String MAC_ADDRESS_FILTER_1S = "C8:0F:10"; public static final UUID UUID_SERVICE_MIBAND_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE0")); - + public static final UUID UUID_SERVICE_MIBAND2_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE1")); public static final UUID UUID_SERVICE_HEART_RATE = UUID.fromString(String.format(BASE_UUID, "180D")); + public static final String UUID_SERVICE_WEIGHT_SERVICE = "00001530-0000-3512-2118-0009af100700"; public static final UUID UUID_CHARACTERISTIC_DEVICE_INFO = UUID.fromString(String.format(BASE_UUID, "FF01")); @@ -53,8 +54,6 @@ public class MiBandService { /* FURTHER UUIDS that were mixed with the other params below. The base UUID for these is unknown */ - public static final String UUID_SERVICE_WEIGHT_SERVICE = "00001530-0000-3512-2118-0009af100700"; - public static final byte ALIAS_LEN = 0xa; /*NOTIFICATIONS: usually received on the UUID_CHARACTERISTIC_NOTIFICATION characteristic */ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java index 03918682..bfa9f59b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java @@ -9,6 +9,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.AbstractCollection; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -17,6 +19,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile; /** * Abstract base class for all devices connected through Bluetooth Low Energy (LE) aka @@ -35,6 +38,7 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im private BtLEQueue mQueue; private HashMap mAvailableCharacteristics; private final Set mSupportedServices = new HashSet<>(4); + private final List> mSupportedProfiles = new ArrayList<>(); public static final String BASE_UUID = "0000%s-0000-1000-8000-00805f9b34fb"; //this is common for all BTLE devices. see http://stackoverflow.com/questions/18699251/finding-out-android-bluetooth-le-gatt-profiles @@ -131,6 +135,10 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im mSupportedServices.add(aSupportedService); } + protected void addSupportedProfile(AbstractBleProfile profile) { + mSupportedProfiles.add(profile); + } + /** * Returns the characteristic matching the given UUID. Only characteristics * are returned whose service is marked as supported. @@ -155,7 +163,7 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im mAvailableCharacteristics = new HashMap<>(); for (BluetoothGattService service : discoveredGattServices) { if (supportedServices.contains(service.getUuid())) { - LOG.debug("discovered supported service: " + service.getUuid()); + LOG.debug("discovered supported service: " + BleNamesResolver.resolveServiceName(service.getUuid().toString()) + ": " + service.getUuid()); List characteristics = service.getCharacteristics(); if (characteristics == null || characteristics.isEmpty()) { LOG.warn("Supported LE service " + service.getUuid() + "did not return any characteristics"); @@ -164,10 +172,11 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im HashMap intmAvailableCharacteristics = new HashMap<>(characteristics.size()); for (BluetoothGattCharacteristic characteristic : characteristics) { intmAvailableCharacteristics.put(characteristic.getUuid(), characteristic); + LOG.info(" characteristic: " + BleNamesResolver.resolveCharacteristicName(characteristic.getUuid().toString()) + ": " + characteristic.getUuid()); } mAvailableCharacteristics.putAll(intmAvailableCharacteristics); } else { - LOG.debug("discovered unsupported service: " + service.getUuid()); + LOG.debug("discovered unsupported service: " + BleNamesResolver.resolveServiceName(service.getUuid().toString()) + ": " + service.getUuid()); } } } @@ -179,6 +188,9 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im // default implementations of event handler methods (gatt callbacks) @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + for (AbstractBleProfile profile : mSupportedProfiles) { + profile.onConnectionStateChange(gatt, status, newState); + } } @Override @@ -190,27 +202,45 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + for (AbstractBleProfile profile : mSupportedProfiles) { + profile.onCharacteristicRead(gatt, characteristic, status); + } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + for (AbstractBleProfile profile : mSupportedProfiles) { + profile.onCharacteristicWrite(gatt, characteristic, status); + } } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + for (AbstractBleProfile profile : mSupportedProfiles) { + profile.onDescriptorRead(gatt, descriptor, status); + } } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + for (AbstractBleProfile profile : mSupportedProfiles) { + profile.onDescriptorWrite(gatt, descriptor, status); + } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + for (AbstractBleProfile profile : mSupportedProfiles) { + profile.onCharacteristicChanged(gatt, characteristic); + } } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + for (AbstractBleProfile profile : mSupportedProfiles) { + profile.onReadRemoteRssi(gatt, rssi, status); + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java new file mode 100644 index 00000000..39713b38 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java @@ -0,0 +1,203 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle; + +import java.util.HashMap; + +import android.util.SparseArray; + +public class BleNamesResolver { + private static HashMap mServices = new HashMap(); + private static HashMap mCharacteristics = new HashMap(); + private static SparseArray mValueFormats = new SparseArray(); + private static SparseArray mAppearance = new SparseArray(); + private static SparseArray mHeartRateSensorLocation = new SparseArray(); + + static public String resolveServiceName(final String uuid) + { + String result = mServices.get(uuid); + if(result == null) result = "Unknown Service"; + return result; + } + + static public String resolveValueTypeDescription(final int format) + { + Integer tmp = Integer.valueOf(format); + return mValueFormats.get(tmp, "Unknown Format"); + } + + static public String resolveCharacteristicName(final String uuid) + { + String result = mCharacteristics.get(uuid); + if(result == null) result = "Unknown Characteristic"; + return result; + } + + static public String resolveUuid(final String uuid) { + String result = mServices.get(uuid); + if(result != null) return "Service: " + result; + + result = mCharacteristics.get(uuid); + if(result != null) return "Characteristic: " + result; + + result = "Unknown UUID"; + return result; + } + + static public String resolveAppearance(int key) { + Integer tmp = Integer.valueOf(key); + return mAppearance.get(tmp, "Unknown Appearance"); + } + + static public String resolveHeartRateSensorLocation(int key) { + Integer tmp = Integer.valueOf(key); + return mHeartRateSensorLocation.get(tmp, "Other"); + } + + static public boolean isService(final String uuid) { + return mServices.containsKey(uuid); + } + + static public boolean isCharacteristic(final String uuid) { + return mCharacteristics.containsKey(uuid); + } + + static { + mServices.put("00001811-0000-1000-8000-00805f9b34fb", "Alert Notification Service"); + mServices.put("0000180f-0000-1000-8000-00805f9b34fb", "Battery Service"); + mServices.put("00001810-0000-1000-8000-00805f9b34fb", "Blood Pressure"); + mServices.put("00001805-0000-1000-8000-00805f9b34fb", "Current Time Service"); + mServices.put("00001818-0000-1000-8000-00805f9b34fb", "Cycling Power"); + mServices.put("00001816-0000-1000-8000-00805f9b34fb", "Cycling Speed and Cadence"); + mServices.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information"); + mServices.put("00001800-0000-1000-8000-00805f9b34fb", "Generic Access"); + mServices.put("00001801-0000-1000-8000-00805f9b34fb", "Generic Attribute"); + mServices.put("00001808-0000-1000-8000-00805f9b34fb", "Glucose"); + mServices.put("00001809-0000-1000-8000-00805f9b34fb", "Health Thermometer"); + mServices.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate"); + mServices.put("00001812-0000-1000-8000-00805f9b34fb", "Human Interface Device"); + mServices.put("00001802-0000-1000-8000-00805f9b34fb", "Immediate Alert"); + mServices.put("00001803-0000-1000-8000-00805f9b34fb", "Link Loss"); + mServices.put("00001819-0000-1000-8000-00805f9b34fb", "Location and Navigation"); + mServices.put("00001807-0000-1000-8000-00805f9b34fb", "Next DST Change Service"); + mServices.put("0000180e-0000-1000-8000-00805f9b34fb", "Phone Alert Status Service"); + mServices.put("00001806-0000-1000-8000-00805f9b34fb", "Reference Time Update Service"); + mServices.put("00001814-0000-1000-8000-00805f9b34fb", "Running Speed and Cadence"); + mServices.put("00001813-0000-1000-8000-00805f9b34fb", "Scan Parameters"); + mServices.put("00001804-0000-1000-8000-00805f9b34fb", "Tx Power"); + mServices.put("0000fee0-0000-3512-2118-0009af100700", "(Propr: Xiaomi MiLi Service)"); + mServices.put("00001530-0000-3512-2118-0009af100700", "(Propr: Xiaomi Weight Service)"); + + + mCharacteristics.put("00002a43-0000-1000-8000-00805f9b34fb", "Alert Category ID"); + mCharacteristics.put("00002a42-0000-1000-8000-00805f9b34fb", "Alert Category ID Bit Mask"); + mCharacteristics.put("00002a06-0000-1000-8000-00805f9b34fb", "Alert Level"); + mCharacteristics.put("00002a44-0000-1000-8000-00805f9b34fb", "Alert Notification Control Point"); + mCharacteristics.put("00002a3f-0000-1000-8000-00805f9b34fb", "Alert Status"); + mCharacteristics.put("00002a01-0000-1000-8000-00805f9b34fb", "Appearance"); + mCharacteristics.put("00002a19-0000-1000-8000-00805f9b34fb", "Battery Level"); + mCharacteristics.put("00002a49-0000-1000-8000-00805f9b34fb", "Blood Pressure Feature"); + mCharacteristics.put("00002a35-0000-1000-8000-00805f9b34fb", "Blood Pressure Measurement"); + mCharacteristics.put("00002a38-0000-1000-8000-00805f9b34fb", "Body Sensor Location"); + mCharacteristics.put("00002a22-0000-1000-8000-00805f9b34fb", "Boot Keyboard Input Report"); + mCharacteristics.put("00002a32-0000-1000-8000-00805f9b34fb", "Boot Keyboard Output Report"); + mCharacteristics.put("00002a33-0000-1000-8000-00805f9b34fb", "Boot Mouse Input Report"); + mCharacteristics.put("00002a5c-0000-1000-8000-00805f9b34fb", "CSC Feature"); + mCharacteristics.put("00002a5b-0000-1000-8000-00805f9b34fb", "CSC Measurement"); + mCharacteristics.put("00002a2b-0000-1000-8000-00805f9b34fb", "Current Time"); + mCharacteristics.put("00002a66-0000-1000-8000-00805f9b34fb", "Cycling Power Control Point"); + mCharacteristics.put("00002a65-0000-1000-8000-00805f9b34fb", "Cycling Power Feature"); + mCharacteristics.put("00002a63-0000-1000-8000-00805f9b34fb", "Cycling Power Measurement"); + mCharacteristics.put("00002a64-0000-1000-8000-00805f9b34fb", "Cycling Power Vector"); + mCharacteristics.put("00002a08-0000-1000-8000-00805f9b34fb", "Date Time"); + mCharacteristics.put("00002a0a-0000-1000-8000-00805f9b34fb", "Day Date Time"); + mCharacteristics.put("00002a09-0000-1000-8000-00805f9b34fb", "Day of Week"); + mCharacteristics.put("00002a00-0000-1000-8000-00805f9b34fb", "Device Name"); + mCharacteristics.put("00002a0d-0000-1000-8000-00805f9b34fb", "DST Offset"); + mCharacteristics.put("00002a0c-0000-1000-8000-00805f9b34fb", "Exact Time 256"); + mCharacteristics.put("00002a26-0000-1000-8000-00805f9b34fb", "Firmware Revision String"); + mCharacteristics.put("00002a51-0000-1000-8000-00805f9b34fb", "Glucose Feature"); + mCharacteristics.put("00002a18-0000-1000-8000-00805f9b34fb", "Glucose Measurement"); + mCharacteristics.put("00002a34-0000-1000-8000-00805f9b34fb", "Glucose Measurement Context"); + mCharacteristics.put("00002a27-0000-1000-8000-00805f9b34fb", "Hardware Revision String"); + mCharacteristics.put("00002a39-0000-1000-8000-00805f9b34fb", "Heart Rate Control Point"); + mCharacteristics.put("00002a37-0000-1000-8000-00805f9b34fb", "Heart Rate Measurement"); + mCharacteristics.put("00002a4c-0000-1000-8000-00805f9b34fb", "HID Control Point"); + mCharacteristics.put("00002a4a-0000-1000-8000-00805f9b34fb", "HID Information"); + mCharacteristics.put("00002a2a-0000-1000-8000-00805f9b34fb", "IEEE 11073-20601 Regulatory Certification Data List"); + mCharacteristics.put("00002a36-0000-1000-8000-00805f9b34fb", "Intermediate Cuff Pressure"); + mCharacteristics.put("00002a1e-0000-1000-8000-00805f9b34fb", "Intermediate Temperature"); + mCharacteristics.put("00002a6b-0000-1000-8000-00805f9b34fb", "LN Control Point"); + mCharacteristics.put("00002a6a-0000-1000-8000-00805f9b34fb", "LN Feature"); + mCharacteristics.put("00002a0f-0000-1000-8000-00805f9b34fb", "Local Time Information"); + mCharacteristics.put("00002a67-0000-1000-8000-00805f9b34fb", "Location and Speed"); + mCharacteristics.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String"); + mCharacteristics.put("00002a21-0000-1000-8000-00805f9b34fb", "Measurement Interval"); + mCharacteristics.put("00002a24-0000-1000-8000-00805f9b34fb", "Model Number String"); + mCharacteristics.put("00002a68-0000-1000-8000-00805f9b34fb", "Navigation"); + mCharacteristics.put("00002a46-0000-1000-8000-00805f9b34fb", "New Alert"); + mCharacteristics.put("00002a04-0000-1000-8000-00805f9b34fb", "Peripheral Preferred Connection Parameters"); + mCharacteristics.put("00002a02-0000-1000-8000-00805f9b34fb", "Peripheral Privacy Flag"); + mCharacteristics.put("00002a50-0000-1000-8000-00805f9b34fb", "PnP ID"); + mCharacteristics.put("00002a69-0000-1000-8000-00805f9b34fb", "Position Quality"); + mCharacteristics.put("00002a4e-0000-1000-8000-00805f9b34fb", "Protocol Mode"); + mCharacteristics.put("00002a03-0000-1000-8000-00805f9b34fb", "Reconnection Address"); + mCharacteristics.put("00002a52-0000-1000-8000-00805f9b34fb", "Record Access Control Point"); + mCharacteristics.put("00002a14-0000-1000-8000-00805f9b34fb", "Reference Time Information"); + mCharacteristics.put("00002a4d-0000-1000-8000-00805f9b34fb", "Report"); + mCharacteristics.put("00002a4b-0000-1000-8000-00805f9b34fb", "Report Map"); + mCharacteristics.put("00002a40-0000-1000-8000-00805f9b34fb", "Ringer Control Point"); + mCharacteristics.put("00002a41-0000-1000-8000-00805f9b34fb", "Ringer Setting"); + mCharacteristics.put("00002a54-0000-1000-8000-00805f9b34fb", "RSC Feature"); + mCharacteristics.put("00002a53-0000-1000-8000-00805f9b34fb", "RSC Measurement"); + mCharacteristics.put("00002a55-0000-1000-8000-00805f9b34fb", "SC Control Point"); + mCharacteristics.put("00002a4f-0000-1000-8000-00805f9b34fb", "Scan Interval Window"); + mCharacteristics.put("00002a31-0000-1000-8000-00805f9b34fb", "Scan Refresh"); + mCharacteristics.put("00002a5d-0000-1000-8000-00805f9b34fb", "Sensor Location"); + mCharacteristics.put("00002a25-0000-1000-8000-00805f9b34fb", "Serial Number String"); + mCharacteristics.put("00002a05-0000-1000-8000-00805f9b34fb", "Service Changed"); + mCharacteristics.put("00002a28-0000-1000-8000-00805f9b34fb", "Software Revision String"); + mCharacteristics.put("00002a47-0000-1000-8000-00805f9b34fb", "Supported New Alert Category"); + mCharacteristics.put("00002a48-0000-1000-8000-00805f9b34fb", "Supported Unread Alert Category"); + mCharacteristics.put("00002a23-0000-1000-8000-00805f9b34fb", "System ID"); + mCharacteristics.put("00002a1c-0000-1000-8000-00805f9b34fb", "Temperature Measurement"); + mCharacteristics.put("00002a1d-0000-1000-8000-00805f9b34fb", "Temperature Type"); + mCharacteristics.put("00002a12-0000-1000-8000-00805f9b34fb", "Time Accuracy"); + mCharacteristics.put("00002a13-0000-1000-8000-00805f9b34fb", "Time Source"); + mCharacteristics.put("00002a16-0000-1000-8000-00805f9b34fb", "Time Update Control Point"); + mCharacteristics.put("00002a17-0000-1000-8000-00805f9b34fb", "Time Update State"); + mCharacteristics.put("00002a11-0000-1000-8000-00805f9b34fb", "Time with DST"); + mCharacteristics.put("00002a0e-0000-1000-8000-00805f9b34fb", "Time Zone"); + mCharacteristics.put("00002a07-0000-1000-8000-00805f9b34fb", "Tx Power Level"); + mCharacteristics.put("00002a45-0000-1000-8000-00805f9b34fb", "Unread Alert Status"); + + mValueFormats.put(Integer.valueOf(52), "32bit float"); + mValueFormats.put(Integer.valueOf(50), "16bit float"); + mValueFormats.put(Integer.valueOf(34), "16bit signed int"); + mValueFormats.put(Integer.valueOf(36), "32bit signed int"); + mValueFormats.put(Integer.valueOf(33), "8bit signed int"); + mValueFormats.put(Integer.valueOf(18), "16bit unsigned int"); + mValueFormats.put(Integer.valueOf(20), "32bit unsigned int"); + mValueFormats.put(Integer.valueOf(17), "8bit unsigned int"); + + // lets add also couple appearance string description + // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml + mAppearance.put(Integer.valueOf(833), "Heart Rate Sensor: Belt"); + mAppearance.put(Integer.valueOf(832), "Generic Heart Rate Sensor"); + mAppearance.put(Integer.valueOf(0), "Unknown"); + mAppearance.put(Integer.valueOf(64), "Generic Phone"); + mAppearance.put(Integer.valueOf(1157), "Cycling: Speed and Cadence Sensor"); + mAppearance.put(Integer.valueOf(1152), "General Cycling"); + mAppearance.put(Integer.valueOf(1153), "Cycling Computer"); + mAppearance.put(Integer.valueOf(1154), "Cycling: Speed Sensor"); + mAppearance.put(Integer.valueOf(1155), "Cycling: Cadence Sensor"); + mAppearance.put(Integer.valueOf(1156), "Cycling: Speed and Cadence Sensor"); + mAppearance.put(Integer.valueOf(1157), "Cycling: Power Sensor"); + + mHeartRateSensorLocation.put(Integer.valueOf(0), "Other"); + mHeartRateSensorLocation.put(Integer.valueOf(1), "Chest"); + mHeartRateSensorLocation.put(Integer.valueOf(2), "Wrist"); + mHeartRateSensorLocation.put(Integer.valueOf(3), "Finger"); + mHeartRateSensorLocation.put(Integer.valueOf(4), "Hand"); + mHeartRateSensorLocation.put(Integer.valueOf(5), "Ear Lobe"); + mHeartRateSensorLocation.put(Integer.valueOf(6), "Foot"); + } +} 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 afbd9ee6..6f9f0f51 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,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle; +import android.bluetooth.BluetoothGattCharacteristic; + import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -241,4 +243,7 @@ public class GattCharacteristic { return name; } + public static String toString(BluetoothGattCharacteristic characteristic) { + return characteristic.getUuid() + " (" + lookup(characteristic.getUuid(), "unknown") + ")"; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java new file mode 100644 index 00000000..50122b41 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java @@ -0,0 +1,73 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; + +import java.io.IOException; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractGattCallback; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; + +/** + * Base class for all BLE profiles, with things that all impplementations are + * expected to use. + * + * Instances are used in the context of a concrete AbstractBTLEDeviceSupport instance, + * i.e. a concrete device. + * + * @see nodomain.freeyourgadget.gadgetbridge.service.btle.GattService + * @see nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic + * @see https://www.bluetooth.com/specifications/assigned-numbers + */ +public abstract class AbstractBleProfile extends AbstractGattCallback { + private final T mSupport; + + public AbstractBleProfile(T support) { + this.mSupport = support; + } + + /** + * All notifications should be sent through this methods to make them testable. + * @param intent the intent to broadcast + */ + protected void notify(Intent intent) { + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + + /** + * Delegates to the DeviceSupport instance and additionally sets this instance as the Gatt + * callback for the transaction. + * + * @param taskName + * @return + * @throws IOException + */ + public TransactionBuilder performInitialized(String taskName) throws IOException { + TransactionBuilder builder = mSupport.performInitialized(taskName); + builder.setGattCallback(this); + return builder; + } + + public Context getContext() { + return mSupport.getContext(); + } + + protected GBDevice getDevice() { + return mSupport.getDevice(); + } + + protected BluetoothGattCharacteristic getCharacteristic(UUID uuid) { + return mSupport.getCharacteristic(uuid); + } + + protected BtLEQueue getQueue() { + return mSupport.getQueue(); + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java new file mode 100644 index 00000000..1e7d2bca --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java @@ -0,0 +1,21 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles; + +import android.bluetooth.BluetoothGattCharacteristic; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; + +public class ValueDecoder { + private static final Logger LOG = LoggerFactory.getLogger(ValueDecoder.class); + + public static int decodePercent(BluetoothGattCharacteristic characteristic) { + int percent = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); + if (percent > 100 || percent < 0) { + LOG.warn("Unexpected percent value: " + percent + ": " + GattCharacteristic.toString(characteristic)); + percent = Math.max(100, Math.min(0, percent)); + } + return percent; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java new file mode 100644 index 00000000..f13c47bf --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java @@ -0,0 +1,46 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery; + +import android.os.Parcel; +import android.os.Parcelable; + +public class BatteryInfo implements Parcelable{ + + private int percentCharged; + + public BatteryInfo() { + } + + protected BatteryInfo(Parcel in) { + percentCharged = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(percentCharged); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public BatteryInfo createFromParcel(Parcel in) { + return new BatteryInfo(in); + } + + @Override + public BatteryInfo[] newArray(int size) { + return new BatteryInfo[size]; + } + }; + + public int getPercentCharged() { + return percentCharged; + } + + public void setPercentCharged(int percentCharged) { + this.percentCharged = percentCharged; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java new file mode 100644 index 00000000..d471609b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java @@ -0,0 +1,71 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Intent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.UUID; + +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.profiles.AbstractBleProfile; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.ValueDecoder; + +public class BatteryInfoProfile extends AbstractBleProfile { + private static final Logger LOG = LoggerFactory.getLogger(BatteryInfoProfile.class); + + private static final String ACTION_PREFIX = BatteryInfoProfile.class.getName() + "_"; + + public static final String ACTION_BATTERY_INFO = ACTION_PREFIX + "BATTERY_INFO"; + public static final String EXTRA_BATTERY_INFO = "BATTERY_INFO"; + + public static final UUID SERVICE_UUID = GattService.UUID_SERVICE_BATTERY_SERVICE; + + public static final UUID UUID_CHARACTERISTIC_BATTERY_LEVEL = GattCharacteristic.UUID_CHARACTERISTIC_BATTERY_LEVEL; + private BatteryInfo batteryInfo; + + public BatteryInfoProfile(T support) { + super(support); + } + + public void requestBatteryInfo(TransactionBuilder builder) { + builder.read(getCharacteristic(UUID_CHARACTERISTIC_BATTERY_LEVEL)); + } + + public void enableNotifiy() { + // TODO: notification + } + + @Override + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + UUID charUuid = characteristic.getUuid(); + if (charUuid.equals(UUID_CHARACTERISTIC_BATTERY_LEVEL)) { + handleBatteryLevel(gatt, characteristic); + } else { + LOG.info("Unexpected onCharacteristicRead: " + GattCharacteristic.toString(characteristic)); + } + } else { + LOG.warn("error reading from characteristic:" + GattCharacteristic.toString(characteristic)); + } + } + + private void handleBatteryLevel(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + int percent = ValueDecoder.decodePercent(characteristic); + batteryInfo.setPercentCharged(percent); + + notify(createIntent(batteryInfo)); + } + + private Intent createIntent(BatteryInfo batteryInfo) { + Intent intent = new Intent(ACTION_BATTERY_INFO); + intent.putExtra(EXTRA_BATTERY_INFO, batteryInfo); + return intent; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java new file mode 100644 index 00000000..2dac1fb9 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java @@ -0,0 +1,133 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo; + +import android.os.Parcel; +import android.os.Parcelable; + +public class DeviceInfo implements Parcelable{ + private String manufacturerName; + private String modelNumber; + private String serialNumber; + private String hardwareRevision; + private String firmwareRevision; + private String softwareRevision; + private String systemId; + private String regulatoryCertificationDataList; + private String pnpId; + + public DeviceInfo() { + } + + protected DeviceInfo(Parcel in) { + manufacturerName = in.readString(); + modelNumber = in.readString(); + serialNumber = in.readString(); + hardwareRevision = in.readString(); + firmwareRevision = in.readString(); + softwareRevision = in.readString(); + systemId = in.readString(); + regulatoryCertificationDataList = in.readString(); + pnpId = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(manufacturerName); + dest.writeString(modelNumber); + dest.writeString(serialNumber); + dest.writeString(hardwareRevision); + dest.writeString(firmwareRevision); + dest.writeString(softwareRevision); + dest.writeString(systemId); + dest.writeString(regulatoryCertificationDataList); + dest.writeString(pnpId); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public DeviceInfo createFromParcel(Parcel in) { + return new DeviceInfo(in); + } + + @Override + public DeviceInfo[] newArray(int size) { + return new DeviceInfo[size]; + } + }; + + public String getManufacturerName() { + return manufacturerName; + } + + public void setManufacturerName(String manufacturerName) { + this.manufacturerName = manufacturerName; + } + + public String getModelNumber() { + return modelNumber; + } + + public void setModelNumber(String modelNumber) { + this.modelNumber = modelNumber; + } + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + public String getHardwareRevision() { + return hardwareRevision; + } + + public void setHardwareRevision(String hardwareRevision) { + this.hardwareRevision = hardwareRevision; + } + + public String getFirmwareRevision() { + return firmwareRevision; + } + + public void setFirmwareRevision(String firmwareRevision) { + this.firmwareRevision = firmwareRevision; + } + + public String getSoftwareRevision() { + return softwareRevision; + } + + public void setSoftwareRevision(String softwareRevision) { + this.softwareRevision = softwareRevision; + } + + public String getSystemId() { + return systemId; + } + + public void setSystemId(String systemId) { + this.systemId = systemId; + } + + public String getRegulatoryCertificationDataList() { + return regulatoryCertificationDataList; + } + + public void setRegulatoryCertificationDataList(String regulatoryCertificationDataList) { + this.regulatoryCertificationDataList = regulatoryCertificationDataList; + } + + public String getPnpId() { + return pnpId; + } + + public void setPnpId(String pnpId) { + this.pnpId = pnpId; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java new file mode 100644 index 00000000..7ae8b609 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java @@ -0,0 +1,148 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Intent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.UUID; + +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.profiles.AbstractBleProfile; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo; + +public class DeviceInfoProfile extends AbstractBleProfile { + private static final Logger LOG = LoggerFactory.getLogger(DeviceInfoProfile.class); + + private static final String ACTION_PREFIX = DeviceInfoProfile.class.getName() + "_"; + + public static final String ACTION_DEVICE_INFO = ACTION_PREFIX + "DEVICE_INFO"; + public static final String EXTRA_DEVICE_INFO = "DEVICE_INFO"; + + public static final UUID SERVICE_UUID = GattService.UUID_SERVICE_DEVICE_INFORMATION; + + public static final UUID UUID_CHARACTERISTIC_MANUFACTURER_NAME_STRING = GattCharacteristic.UUID_CHARACTERISTIC_MANUFACTURER_NAME_STRING; + public static final UUID UUID_CHARACTERISTIC_MODEL_NUMBER_STRING = GattCharacteristic.UUID_CHARACTERISTIC_MODEL_NUMBER_STRING; + public static final UUID UUID_CHARACTERISTIC_SERIAL_NUMBER_STRING = GattCharacteristic.UUID_CHARACTERISTIC_SERIAL_NUMBER_STRING; + public static final UUID UUID_CHARACTERISTIC_HARDWARE_REVISION_STRING = GattCharacteristic.UUID_CHARACTERISTIC_HARDWARE_REVISION_STRING; + public static final UUID UUID_CHARACTERISTIC_FIRMWARE_REVISION_STRING = GattCharacteristic.UUID_CHARACTERISTIC_FIRMWARE_REVISION_STRING; + public static final UUID UUID_CHARACTERISTIC_SOFTWARE_REVISION_STRING = GattCharacteristic.UUID_CHARACTERISTIC_SOFTWARE_REVISION_STRING; + public static final UUID UUID_CHARACTERISTIC_SYSTEM_ID = GattCharacteristic.UUID_CHARACTERISTIC_SYSTEM_ID; + public static final UUID UUID_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST = GattCharacteristic.UUID_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST; + public static final UUID UUID_CHARACTERISTIC_PNP_ID = GattCharacteristic.UUID_CHARACTERISTIC_PNP_ID; + private final DeviceInfo deviceInfo = new DeviceInfo(); + + public DeviceInfoProfile(T support) { + super(support); + } + + public void requestDeviceInfo(TransactionBuilder builder) { + builder.read(getCharacteristic(UUID_CHARACTERISTIC_MANUFACTURER_NAME_STRING)) + .read(getCharacteristic(UUID_CHARACTERISTIC_MODEL_NUMBER_STRING)) + .read(getCharacteristic(UUID_CHARACTERISTIC_SERIAL_NUMBER_STRING)) + .read(getCharacteristic(UUID_CHARACTERISTIC_HARDWARE_REVISION_STRING)) + .read(getCharacteristic(UUID_CHARACTERISTIC_FIRMWARE_REVISION_STRING)) + .read(getCharacteristic(UUID_CHARACTERISTIC_SOFTWARE_REVISION_STRING)) + .read(getCharacteristic(UUID_CHARACTERISTIC_SYSTEM_ID)) + .read(getCharacteristic(UUID_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST)) + .read(getCharacteristic(UUID_CHARACTERISTIC_PNP_ID)); + } + + @Override + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + UUID charUuid = characteristic.getUuid(); + if (charUuid.equals(UUID_CHARACTERISTIC_MANUFACTURER_NAME_STRING)) { + handleManufacturerName(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_MODEL_NUMBER_STRING)) { + handleModelNumber(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_SERIAL_NUMBER_STRING)) { + handleSerialNumber(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_HARDWARE_REVISION_STRING)) { + handleHardwareRevision(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_FIRMWARE_REVISION_STRING)) { + handleFirmwareRevision(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_SOFTWARE_REVISION_STRING)) { + handleSoftwareRevision(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_SYSTEM_ID)) { + handleSystemId(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST)) { + handleRegulatoryCertificationData(gatt, characteristic); + } else if (charUuid.equals(UUID_CHARACTERISTIC_PNP_ID)) { + handlePnpId(gatt, characteristic); + } else { + LOG.info("Unexpected onCharacteristicRead: " + GattCharacteristic.toString(characteristic)); + } + } else { + LOG.warn("error reading from characteristic:" + GattCharacteristic.toString(characteristic)); + } + } + + + private void handleManufacturerName(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String name = characteristic.getStringValue(0); + deviceInfo.setManufacturerName(name); + notify(createIntent(deviceInfo)); + } + + private void handleModelNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String modelNumber = characteristic.getStringValue(0); + deviceInfo.setModelNumber(modelNumber); + notify(createIntent(deviceInfo)); + } + private void handleSerialNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String serialNumber = characteristic.getStringValue(0); + deviceInfo.setSerialNumber(serialNumber); + notify(createIntent(deviceInfo)); + } + + private void handleHardwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String hardwareRevision = characteristic.getStringValue(0); + deviceInfo.setHardwareRevision(hardwareRevision); + notify(createIntent(deviceInfo)); + } + + private void handleFirmwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String firmwareRevision = characteristic.getStringValue(0); + deviceInfo.setFirmwareRevision(firmwareRevision); + notify(createIntent(deviceInfo)); + } + + private void handleSoftwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String softwareRevision = characteristic.getStringValue(0); + deviceInfo.setSoftwareRevision(softwareRevision); + notify(createIntent(deviceInfo)); + } + + private void handleSystemId(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String systemId = characteristic.getStringValue(0); + deviceInfo.setSystemId(systemId); + notify(createIntent(deviceInfo)); + } + + private void handleRegulatoryCertificationData(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + // TODO: regulatory certification data list not supported yet +// String regulatoryCertificationData = characteristic.getStringValue(0); +// deviceInfo.setRegulatoryCertificationDataList(regulatoryCertificationData); +// notify(createIntent(deviceInfo)); + } + + private void handlePnpId(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + String pnpId = characteristic.getStringValue(0); + deviceInfo.setPnpId(pnpId); + notify(createIntent(deviceInfo)); + } + + private Intent createIntent(DeviceInfo deviceInfo) { + Intent intent = new Intent(ACTION_DEVICE_INFO); + intent.putExtra(EXTRA_DEVICE_INFO, deviceInfo); // TODO: broadcast a clone of the info + return intent; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java new file mode 100644 index 00000000..6f053929 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -0,0 +1,1185 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.support.v4.content.LocalBroadcastManager; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; +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.BtLEAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfoProfile; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COUNT; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_DURATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_ORIGINAL_COLOUR; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_COUNT; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_DURATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_PAUSE; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_PROFILE; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_COLOUR; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_COUNT; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_DURATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_ORIGINAL_COLOUR; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_GENERIC; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_K9MAIL; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_PEBBLEMSG; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_SMS; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_COUNT; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_DURATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_PAUSE; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_PROFILE; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefIntValue; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefStringValue; + +public class MiBand2Support extends AbstractBTLEDeviceSupport { + + private static final Logger LOG = LoggerFactory.getLogger(MiBand2Support.class); + private final DeviceInfoProfile deviceInfoProfile; + private final BatteryInfoProfile batteryInfoProfile; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String s = intent.getAction(); + if (s.equals(DeviceInfoProfile.ACTION_DEVICE_INFO)) { + handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO)); + + } else if (s.equals(BatteryInfoProfile.ACTION_BATTERY_INFO)) { + handleBatteryInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo) intent.getParcelableExtra(BatteryInfoProfile.EXTRA_BATTERY_INFO)); + } + } + }; + + private volatile boolean telephoneRinging; + private volatile boolean isLocatingDevice; + + private DeviceInfo mDeviceInfo; + + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + + public MiBand2Support() { + addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); + addSupportedService(GattService.UUID_SERVICE_HEART_RATE); + addSupportedService(GattService.UUID_SERVICE_IMMEDIATE_ALERT); + addSupportedService(GattService.UUID_SERVICE_DEVICE_INFORMATION); + addSupportedService(GattService.UUID_SERVICE_ALERT_NOTIFICATION); + + addSupportedService(MiBandService.UUID_SERVICE_MIBAND_SERVICE); + addSupportedService(MiBandService.UUID_SERVICE_MIBAND2_SERVICE); + + deviceInfoProfile = new DeviceInfoProfile<>(this); + batteryInfoProfile = new BatteryInfoProfile<>(this); + addSupportedProfile(deviceInfoProfile); + addSupportedProfile(batteryInfoProfile); + + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(DeviceInfoProfile.ACTION_DEVICE_INFO); + intentFilter.addAction(BatteryInfoProfile.ACTION_BATTERY_INFO); + broadcastManager.registerReceiver(mReceiver, intentFilter); + } + + @Override + public void dispose() { + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + broadcastManager.unregisterReceiver(mReceiver); + super.dispose(); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext())); + enableNotifications(builder, true) + .setLowLatency(builder) + .readDate(builder) // without reading the data, we get sporadic connection problems, especially directly after turning on BT +// this is apparently not needed anymore, and actually causes problems when bonding is not used/does not work +// so we simply not use the UUID_PAIR characteristic. +// .pair(builder) + .requestDeviceInfo(builder) + .requestBatteryInfo(builder); +// .sendUserInfo(builder) +// .checkAuthenticationNeeded(builder, getDevice()) +// .setWearLocation(builder) +// .setHeartrateSleepSupport(builder) +// .setFitnessGoal(builder) +// .enableFurtherNotifications(builder, true) +// .setCurrentTime(builder) +// .requestBatteryInfo(builder) +// .setHighLatency(builder) +// .setInitialized(builder); + return builder; + } + + private MiBand2Support readDate(TransactionBuilder builder) { + // NAVL +// builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_DATE_TIME)); + // TODO: handle result + builder.read(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME)); + return this; + } + + // NAVL + public MiBand2Support setLowLatency(TransactionBuilder builder) { +// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getLowLatency()); + return this; + } + // NAVL + public MiBand2Support setHighLatency(TransactionBuilder builder) { +// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getHighLatency()); + return this; + } + + private MiBand2Support checkAuthenticationNeeded(TransactionBuilder builder, GBDevice device) { + builder.add(new CheckAuthenticationNeededAction(device)); + return this; + } + + /** + * Last action of initialization sequence. Sets the device to initialized. + * It is only invoked if all other actions were successfully run, so the device + * must be initialized, then. + * + * @param builder + */ + private void setInitialized(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZED, getContext())); + } + + // MB2: AVL + // TODO: tear down the notifications on quit + private MiBand2Support enableNotifications(TransactionBuilder builder, boolean enable) { + builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable); + return this; + } + + private MiBand2Support enableFurtherNotifications(TransactionBuilder builder, boolean enable) { + builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable) + .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable) + .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_BATTERY), enable) + .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); + // cannot use supportsHeartrate() here because we don't have that information yet + BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT); + if (heartrateCharacteristic != null) { + builder.notify(heartrateCharacteristic, enable); + } + + return this; + } + + @Override + public boolean useAutoConnect() { + return true; + } + + @Override + public void pair() { + for (int i = 0; i < 5; i++) { + if (connect()) { + return; + } + } + } + + public DeviceInfo getDeviceInfo() { + return mDeviceInfo; + } + + private MiBand2Support sendDefaultNotification(TransactionBuilder builder, short repeat, BtLEAction extraAction) { + LOG.info("Sending notification to MiBand: (" + repeat + " times)"); + NotificationStrategy strategy = getNotificationStrategy(); + for (short i = 0; i < repeat; i++) { + strategy.sendDefaultNotification(builder, extraAction); + } + return this; + } + + /** + * Adds a custom notification to the given transaction builder + * + * @param vibrationProfile specifies how and how often the Band shall vibrate. + * @param flashTimes + * @param flashColour + * @param originalColour + * @param flashDuration + * @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example. + * @param builder + */ + private MiBand2Support sendCustomNotification(VibrationProfile vibrationProfile, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { + getNotificationStrategy().sendCustomNotification(vibrationProfile, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); + LOG.info("Sending notification to MiBand"); + return this; + } + + private NotificationStrategy getNotificationStrategy() { + if (mDeviceInfo == null) { + // not initialized yet? + return new NoNotificationStrategy(); + } + if (mDeviceInfo.getFirmwareVersion() < MiBandFWHelper.FW_16779790) { + return new V1NotificationStrategy(this); + } else { + //use the new alert characteristic + return new V2NotificationStrategy(this); + } + } + + static final byte[] reboot = new byte[]{MiBandService.COMMAND_REBOOT}; + + static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1}; + static final byte[] stopHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 0}; + static final byte[] startHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 1}; + static final byte[] stopHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 0}; + static final byte[] startHeartMeasurementSleep = new byte[]{0x15, MiBandService.COMMAND_SET_HR_SLEEP, 1}; + static final byte[] stopHeartMeasurementSleep = new byte[]{0x15, MiBandService.COMMAND_SET_HR_SLEEP, 0}; + + static final byte[] startRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 1}; + static final byte[] stopRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 0}; + + /** + * Part of device initialization process. Do not call manually. + * + * @param builder + * @return + */ + private MiBand2Support sendUserInfo(TransactionBuilder builder) { + LOG.debug("Writing User Info!"); + // Use a custom action instead of just builder.write() because mDeviceInfo + // is set by handleDeviceInfo *after* this action is created. + builder.add(new BtLEAction(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_USER_INFO)) { + @Override + public boolean expectsResult() { + return true; + } + + @Override + public boolean run(BluetoothGatt gatt) { + // at this point, mDeviceInfo should be set + return new WriteAction(getCharacteristic(), + MiBandCoordinator.getAnyUserInfo(getDevice().getAddress()).getData(mDeviceInfo) + ).run(gatt); + } + }); + return this; + } + + private MiBand2Support requestBatteryInfo(TransactionBuilder builder) { + LOG.debug("Requesting Battery Info!"); + batteryInfoProfile.requestBatteryInfo(builder); + return this; + } + + private MiBand2Support requestDeviceInfo(TransactionBuilder builder) { + LOG.debug("Requesting Device Info!"); + deviceInfoProfile.requestDeviceInfo(builder); + return this; + } + + /* private MiBandSupport requestHRInfo(TransactionBuilder builder) { + LOG.debug("Requesting HR Info!"); + BluetoothGattCharacteristic HRInfo = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT); + builder.read(HRInfo); + BluetoothGattCharacteristic HR_Point = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); + builder.read(HR_Point); + return this; + } + *//** + * Part of HR test. Do not call manually. + * + * @param transaction + * @return + *//* + private MiBandSupport heartrate(TransactionBuilder transaction) { + LOG.info("Attempting to read HR ..."); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT); + if (characteristic != null) { + transaction.write(characteristic, new byte[]{MiBandService.COMMAND_SET__HR_CONTINUOUS}); + } else { + LOG.info("Unable to read HR from MI device -- characteristic not available"); + } + return this; + }*/ + + /** + * Part of device initialization process. Do not call manually. + * + * @param transaction + * @return + */ + private MiBand2Support pair(TransactionBuilder transaction) { + LOG.info("Attempting to pair MI device..."); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_PAIR); + if (characteristic != null) { + transaction.write(characteristic, new byte[]{2}); + } else { + LOG.info("Unable to pair MI device -- characteristic not available"); + } + return this; + } + + /** + * Part of device initialization process. Do not call manually. + * + * @param transaction + * @return + */ + + private MiBand2Support setFitnessGoal(TransactionBuilder transaction) { + LOG.info("Attempting to set Fitness Goal..."); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + if (characteristic != null) { + int fitnessGoal = MiBandCoordinator.getFitnessGoal(getDevice().getAddress()); + transaction.write(characteristic, new byte[]{ + MiBandService.COMMAND_SET_FITNESS_GOAL, + 0, + (byte) (fitnessGoal & 0xff), + (byte) ((fitnessGoal >>> 8) & 0xff) + }); + } else { + LOG.info("Unable to set Fitness Goal"); + } + return this; + } + + /** + * Part of device initialization process. Do not call manually. + * + * @param transaction + * @return + */ + private MiBand2Support setWearLocation(TransactionBuilder transaction) { + LOG.info("Attempting to set wear location..."); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + if (characteristic != null) { + transaction.add(new ConditionalWriteAction(characteristic) { + @Override + protected byte[] checkCondition() { + if (getDeviceInfo() != null && getDeviceInfo().isAmazFit()) { + return null; + } + int location = MiBandCoordinator.getWearLocation(getDevice().getAddress()); + return new byte[]{ + MiBandService.COMMAND_SET_WEAR_LOCATION, + (byte) location + }; + } + }); + } else { + LOG.info("Unable to set Wear Location"); + } + return this; + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + try { + TransactionBuilder builder = performInitialized("enable heart rate sleep support: " + enable); + setHeartrateSleepSupport(builder); + builder.queue(getQueue()); + } catch (IOException e) { + GB.toast(getContext(), "Error toggling heart rate sleep support: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + // not supported + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + // not supported + } + + /** + * Part of device initialization process. Do not call manually. + * + * @param builder + */ + private MiBand2Support setHeartrateSleepSupport(TransactionBuilder builder) { + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); + if (characteristic != null) { + builder.add(new ConditionalWriteAction(characteristic) { + @Override + protected byte[] checkCondition() { + if (!supportsHeartRate()) { + return null; + } + if (MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { + LOG.info("Enabling heartrate sleep support..."); + return startHeartMeasurementSleep; + } else { + LOG.info("Disabling heartrate sleep support..."); + return stopHeartMeasurementSleep; + } + } + }); + } + return this; + } + + private void performDefaultNotification(String task, short repeat, BtLEAction extraAction) { + try { + TransactionBuilder builder = performInitialized(task); + sendDefaultNotification(builder, repeat, extraAction); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to send notification to MI device", ex); + } + } + + private void performPreferredNotification(String task, String notificationOrigin, BtLEAction extraAction) { + try { + TransactionBuilder builder = performInitialized(task); + Prefs prefs = GBApplication.getPrefs(); + int vibrateDuration = getPreferredVibrateDuration(notificationOrigin, prefs); + int vibratePause = getPreferredVibratePause(notificationOrigin, prefs); + short vibrateTimes = getPreferredVibrateCount(notificationOrigin, prefs); + VibrationProfile profile = getPreferredVibrateProfile(notificationOrigin, prefs, vibrateTimes); + + int flashTimes = getPreferredFlashCount(notificationOrigin, prefs); + int flashColour = getPreferredFlashColour(notificationOrigin, prefs); + int originalColour = getPreferredOriginalColour(notificationOrigin, prefs); + int flashDuration = getPreferredFlashDuration(notificationOrigin, prefs); + + sendCustomNotification(profile, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); +// sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to send notification to MI device", ex); + } + } + + private int getPreferredFlashDuration(String notificationOrigin, Prefs prefs) { + return getNotificationPrefIntValue(FLASH_DURATION, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_DURATION); + } + + private int getPreferredOriginalColour(String notificationOrigin, Prefs prefs) { + return getNotificationPrefIntValue(FLASH_ORIGINAL_COLOUR, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_ORIGINAL_COLOUR); + } + + private int getPreferredFlashColour(String notificationOrigin, Prefs prefs) { + return getNotificationPrefIntValue(FLASH_COLOUR, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_COLOUR); + } + + private int getPreferredFlashCount(String notificationOrigin, Prefs prefs) { + return getNotificationPrefIntValue(FLASH_COUNT, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_COUNT); + } + + private int getPreferredVibratePause(String notificationOrigin, Prefs prefs) { + return getNotificationPrefIntValue(VIBRATION_PAUSE, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_PAUSE); + } + + private short getPreferredVibrateCount(String notificationOrigin, Prefs prefs) { + return (short) Math.min(Short.MAX_VALUE, getNotificationPrefIntValue(VIBRATION_COUNT, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_COUNT)); + } + + private int getPreferredVibrateDuration(String notificationOrigin, Prefs prefs) { + return getNotificationPrefIntValue(VIBRATION_DURATION, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_DURATION); + } + + private VibrationProfile getPreferredVibrateProfile(String notificationOrigin, Prefs prefs, short repeat) { + String profileId = getNotificationPrefStringValue(VIBRATION_PROFILE, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_PROFILE); + return VibrationProfile.getProfile(profileId, repeat); + } + + @Override + public void onSetAlarms(ArrayList alarms) { + try { + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + TransactionBuilder builder = performInitialized("Set alarm"); + boolean anyAlarmEnabled = false; + for (Alarm alarm : alarms) { + anyAlarmEnabled |= alarm.isEnabled(); + queueAlarm(alarm, builder, characteristic); + } + builder.queue(getQueue()); + if (anyAlarmEnabled) { + GB.toast(getContext(), getContext().getString(R.string.user_feedback_miband_set_alarms_ok), Toast.LENGTH_SHORT, GB.INFO); + } else { + GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO); + } + } catch (IOException ex) { + GB.toast(getContext(), getContext().getString(R.string.user_feedback_miband_set_alarms_failed), Toast.LENGTH_LONG, GB.ERROR, ex); + } + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + // FIXME: these ORIGIN contants do not really make sense anymore + switch (notificationSpec.type) { + case SMS: + performPreferredNotification("sms received", ORIGIN_SMS, null); + break; + case EMAIL: + performPreferredNotification("email received", ORIGIN_K9MAIL, null); + break; + case CHAT: + performPreferredNotification("chat message received", ORIGIN_PEBBLEMSG, null); + break; + default: + performPreferredNotification("generic notification received", ORIGIN_GENERIC, null); + } + } + + @Override + public void onSetTime() { + try { + TransactionBuilder builder = performInitialized("Set date and time"); + setCurrentTime(builder); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to set time on MI device", ex); + } + //TODO: once we have a common strategy for sending events (e.g. EventHandler), remove this call from here. Meanwhile it does no harm. + sendCalendarEvents(); + } + + /** + * Sets the current time to the Mi device using the given builder. + * + * @param builder + */ + private MiBand2Support setCurrentTime(TransactionBuilder builder) { + Calendar now = GregorianCalendar.getInstance(); + Date date = now.getTime(); + LOG.info("Sending current time to Mi Band: " + DateTimeUtils.formatDate(date) + " (" + date.toGMTString() + ")"); + byte[] nowBytes = MiBandDateConverter.calendarToRawBytes(now); + byte[] time = new byte[]{ + nowBytes[0], + nowBytes[1], + nowBytes[2], + nowBytes[3], + nowBytes[4], + nowBytes[5], + (byte) 0x0f, + (byte) 0x0f, + (byte) 0x0f, + (byte) 0x0f, + (byte) 0x0f, + (byte) 0x0f + }; + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_DATE_TIME); + if (characteristic != null) { + builder.write(characteristic, time); + } else { + LOG.info("Unable to set time -- characteristic not available"); + } + return this; + } + + @Override + public void onSetCallState(CallSpec callSpec) { + if (callSpec.command == CallSpec.CALL_INCOMING) { + telephoneRinging = true; + AbortTransactionAction abortAction = new AbortTransactionAction() { + @Override + protected boolean shouldAbort() { + return !isTelephoneRinging(); + } + }; + performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, abortAction); + } else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) { + telephoneRinging = false; + } + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + } + + private boolean isTelephoneRinging() { + // don't synchronize, this is not really important + return telephoneRinging; + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + // not supported + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + // not supported + } + + @Override + public void onReboot() { + try { + TransactionBuilder builder = performInitialized("Reboot"); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), reboot); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to reboot MI", ex); + } + } + + @Override + public void onHeartRateTest() { + if (supportsHeartRate()) { + try { + TransactionBuilder builder = performInitialized("HeartRateTest"); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to read HearRate in MI1S", ex); + } + } else { + GB.toast(getContext(), "Heart rate is not supported on this device", Toast.LENGTH_LONG, GB.ERROR); + } + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + if (supportsHeartRate()) { + try { + TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); + if (enable) { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); + } else { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); + } + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); + } + } + } + + public boolean supportsHeartRate() { + return getDeviceInfo() != null && getDeviceInfo().supportsHeartrate(); + } + + @Override + public void onFindDevice(boolean start) { + isLocatingDevice = start; + + if (start) { + AbortTransactionAction abortAction = new AbortTransactionAction() { + @Override + protected boolean shouldAbort() { + return !isLocatingDevice; + } + }; + performDefaultNotification("locating device", (short) 255, abortAction); + } + } + + @Override + public void onFetchActivityData() { +// TODO: onFetchActivityData +// try { +// new FetchActivityOperation(this).perform(); +// } catch (IOException ex) { +// LOG.error("Unable to fetch MI activity data", ex); +// } + } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + try { + BluetoothGattCharacteristic controlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + if (enable) { + TransactionBuilder builder = performInitialized("Read realtime steps"); + builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS)).queue(getQueue()); + } + performInitialized(enable ? "Enabling realtime steps notifications" : "Disabling realtime steps notifications") + .write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), enable ? getLowLatency() : getHighLatency()) + .write(controlPoint, enable ? startRealTimeStepsNotifications : stopRealTimeStepsNotifications).queue(getQueue()); + } catch (IOException e) { + LOG.error("Unable to change realtime steps notification to: " + enable, e); + } + } + + private byte[] getHighLatency() { + int minConnectionInterval = 460; + int maxConnectionInterval = 500; + int latency = 0; + int timeout = 500; + int advertisementInterval = 0; + + return getLatency(minConnectionInterval, maxConnectionInterval, latency, timeout, advertisementInterval); + } + + private byte[] getLatency(int minConnectionInterval, int maxConnectionInterval, int latency, int timeout, int advertisementInterval) { + byte result[] = new byte[12]; + result[0] = (byte) (minConnectionInterval & 0xff); + result[1] = (byte) (0xff & minConnectionInterval >> 8); + result[2] = (byte) (maxConnectionInterval & 0xff); + result[3] = (byte) (0xff & maxConnectionInterval >> 8); + result[4] = (byte) (latency & 0xff); + result[5] = (byte) (0xff & latency >> 8); + result[6] = (byte) (timeout & 0xff); + result[7] = (byte) (0xff & timeout >> 8); + result[8] = 0; + result[9] = 0; + result[10] = (byte) (advertisementInterval & 0xff); + result[11] = (byte) (0xff & advertisementInterval >> 8); + + return result; + } + + private byte[] getLowLatency() { + int minConnectionInterval = 39; + int maxConnectionInterval = 49; + int latency = 0; + int timeout = 500; + int advertisementInterval = 0; + + return getLatency(minConnectionInterval, maxConnectionInterval, latency, timeout, advertisementInterval); + } + + @Override + public void onInstallApp(Uri uri) { +// TODO: onInstallApp (firmware update) +// try { +// new UpdateFirmwareOperation(uri, this).perform(); +// } catch (IOException ex) { +// GB.toast(getContext(), "Firmware cannot be installed: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex); +// } + } + + @Override + public void onAppInfoReq() { + // not supported + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + // not supported + } + + @Override + public void onAppDelete(UUID uuid) { + // not supported + } + + @Override + public void onAppConfiguration(UUID uuid, String config) { + // not supported + } + + @Override + public void onAppReorder(UUID[] uuids) { + // not supported + } + + @Override + public void onScreenshotReq() { + // not supported + } + + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + super.onCharacteristicChanged(gatt, characteristic); + + UUID characteristicUUID = characteristic.getUuid(); + if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { + handleBatteryInfo(characteristic.getValue(), BluetoothGatt.GATT_SUCCESS); + } else if (MiBandService.UUID_CHARACTERISTIC_NOTIFICATION.equals(characteristicUUID)) { + handleNotificationNotif(characteristic.getValue()); + } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { + handleRealtimeSteps(characteristic.getValue()); + } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { + handleRealtimeSteps(characteristic.getValue()); + } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { + handleHeartrate(characteristic.getValue()); + } else { + LOG.info("Unhandled characteristic changed: " + characteristicUUID); + logMessageContent(characteristic.getValue()); + } + } + + @Override + public void onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + super.onCharacteristicRead(gatt, characteristic, status); + + UUID characteristicUUID = characteristic.getUuid(); + if (MiBandService.UUID_CHARACTERISTIC_DEVICE_INFO.equals(characteristicUUID)) { + handleDeviceInfo(characteristic.getValue(), status); + } else if (GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME.equals(characteristicUUID)) { + handleDeviceName(characteristic.getValue(), status); + } else if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { + handleBatteryInfo(characteristic.getValue(), status); + } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { + logHeartrate(characteristic.getValue(), status); + } else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) { + logDate(characteristic.getValue(), status); + } else { + LOG.info("Unhandled characteristic read: " + characteristicUUID); + logMessageContent(characteristic.getValue()); + } + } + + @Override + public void onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + UUID characteristicUUID = characteristic.getUuid(); + if (MiBandService.UUID_CHARACTERISTIC_PAIR.equals(characteristicUUID)) { + handlePairResult(characteristic.getValue(), status); + } else if (MiBandService.UUID_CHARACTERISTIC_USER_INFO.equals(characteristicUUID)) { + handleUserInfoResult(characteristic.getValue(), status); + } else if (MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT.equals(characteristicUUID)) { + handleControlPointResult(characteristic.getValue(), status); + } + } + + /** + * Utility method that may be used to log incoming messages when we don't know how to deal with them yet. + * + * @param value + */ + public void logMessageContent(byte[] value) { + LOG.info("RECEIVED DATA WITH LENGTH: " + ((value != null) ? value.length : "(null)")); + if (value != null) { + for (byte b : value) { + LOG.warn("DATA: " + String.format("0x%2x", b)); + } + } + } + + public void logDate(byte[] value, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value); + LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime())); + } else { + logMessageContent(value); + } + } + + public void logHeartrate(byte[] value, int status) { + if (status == BluetoothGatt.GATT_SUCCESS && value != null) { + LOG.info("Got heartrate:"); + if (value.length == 2 && value[0] == 6) { + int hrValue = (value[1] & 0xff); + GB.toast(getContext(), "Heart Rate measured: " + hrValue, Toast.LENGTH_LONG, GB.INFO); + } + return; + } + logMessageContent(value); + } + + private void handleHeartrate(byte[] value) { + if (value.length == 2 && value[0] == 6) { + int hrValue = (value[1] & 0xff); + if (LOG.isDebugEnabled()) { + LOG.debug("heart rate: " + hrValue); + } + Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT) + .putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, hrValue) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + } + + private void handleRealtimeSteps(byte[] value) { + int steps = 0xff & value[0] | (0xff & value[1]) << 8; + if (LOG.isDebugEnabled()) { + LOG.debug("realtime steps: " + steps); + } + Intent intent = new Intent(DeviceService.ACTION_REALTIME_STEPS) + .putExtra(DeviceService.EXTRA_REALTIME_STEPS, steps) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + + /** + * React to unsolicited messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION + * characteristic, + * These messages appear to be always 1 byte long, with values that are listed in MiBandService. + * It is not excluded that there are further values which are still unknown. + *

+ * Upon receiving known values that request further action by GB, the appropriate method is called. + * + * @param value + */ + private void handleNotificationNotif(byte[] value) { + if (value.length != 1) { + LOG.error("Notifications should be 1 byte long."); + LOG.info("RECEIVED DATA WITH LENGTH: " + value.length); + for (byte b : value) { + LOG.warn("DATA: " + String.format("0x%2x", b)); + } + return; + } + switch (value[0]) { + case MiBandService.NOTIFY_AUTHENTICATION_FAILED: + // we get first FAILED, then NOTIFY_STATUS_MOTOR_AUTH (0x13) + // which means, we need to authenticate by tapping + getDevice().setState(State.AUTHENTICATION_REQUIRED); + getDevice().sendDeviceUpdateIntent(getContext()); + GB.toast(getContext(), "Band needs pairing", Toast.LENGTH_LONG, GB.ERROR); + break; + case MiBandService.NOTIFY_AUTHENTICATION_SUCCESS: // fall through -- not sure which one we get + case MiBandService.NOTIFY_RESET_AUTHENTICATION_SUCCESS: // for Mi 1A + case MiBandService.NOTIFY_STATUS_MOTOR_AUTH_SUCCESS: + LOG.info("Band successfully authenticated"); + // maybe we can perform the rest of the initialization from here + doInitialize(); + break; + + case MiBandService.NOTIFY_STATUS_MOTOR_AUTH: + LOG.info("Band needs authentication (MOTOR_AUTH)"); + getDevice().setState(State.AUTHENTICATING); + getDevice().sendDeviceUpdateIntent(getContext()); + break; + + case MiBandService.NOTIFY_SET_LATENCY_SUCCESS: + LOG.info("Setting latency succeeded."); + break; + default: + for (byte b : value) { + LOG.warn("DATA: " + String.format("0x%2x", b)); + } + } + } + + private void doInitialize() { + try { + TransactionBuilder builder = performInitialized("just initializing after authentication"); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to initialize device after authentication", ex); + } + } + + private void handleDeviceInfo(byte[] value, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + mDeviceInfo = new DeviceInfo(value); + if (getDeviceInfo().supportsHeartrate()) { + getDevice().addDeviceInfo(new GenericItem( + getContext().getString(R.string.DEVINFO_HR_VER), + MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getHeartrateFirmwareVersion()))); + } + LOG.warn("Device info: " + mDeviceInfo); + versionCmd.hwVersion = mDeviceInfo.getHwVersion(); + versionCmd.fwVersion = MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getFirmwareVersion()); + handleGBDeviceEvent(versionCmd); + } + } + + private void handleDeviceName(byte[] value, int status) { +// if (status == BluetoothGatt.GATT_SUCCESS) { +// versionCmd.hwVersion = new String(value); +// handleGBDeviceEvent(versionCmd); +// } + } + + /** + * Convert an alarm from the GB internal structure to a Mi Band message and put on the specified + * builder queue as a write message for the passed characteristic + * + * @param alarm + * @param builder + * @param characteristic + */ + private void queueAlarm(Alarm alarm, TransactionBuilder builder, BluetoothGattCharacteristic characteristic) { + byte[] alarmCalBytes = MiBandDateConverter.calendarToRawBytes(alarm.getAlarmCal()); + + byte[] alarmMessage = new byte[]{ + MiBandService.COMMAND_SET_TIMER, + (byte) alarm.getIndex(), + (byte) (alarm.isEnabled() ? 1 : 0), + alarmCalBytes[0], + alarmCalBytes[1], + alarmCalBytes[2], + alarmCalBytes[3], + alarmCalBytes[4], + alarmCalBytes[5], + (byte) (alarm.isSmartWakeup() ? 30 : 0), + (byte) alarm.getRepetitionMask() + }; + builder.write(characteristic, alarmMessage); + } + + private void handleControlPointResult(byte[] value, int status) { + if (status != BluetoothGatt.GATT_SUCCESS) { + LOG.warn("Could not write to the control point."); + } + LOG.info("handleControlPoint write status:" + status + "; length: " + (value != null ? value.length : "(null)")); + + if (value != null) { + for (byte b : value) { + LOG.info("handleControlPoint WROTE DATA:" + String.format("0x%8x", b)); + } + } else { + LOG.warn("handleControlPoint WROTE null"); + } + } + + private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { +// if (getDeviceInfo().supportsHeartrate()) { +// getDevice().addDeviceInfo(new GenericItem( +// getContext().getString(R.string.DEVINFO_HR_VER), +// info.getSoftwareRevision())); +// } + LOG.warn("Device info: " + info); + versionCmd.hwVersion = info.getHardwareRevision(); + versionCmd.fwVersion = info.getFirmwareRevision(); + handleGBDeviceEvent(versionCmd); + } + + private void handleBatteryInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo info) { + batteryCmd.level = (short) info.getPercentCharged(); +// batteryCmd.state = info.getState(); +// batteryCmd.lastChargeTime = info.getLastChargeTime(); +// batteryCmd.numCharges = info.getNumCharges(); + handleGBDeviceEvent(batteryCmd); + } + + + private void handleBatteryInfo(byte[] value, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + BatteryInfo info = new BatteryInfo(value); + batteryCmd.level = ((short) info.getLevelInPercent()); + batteryCmd.state = info.getState(); + batteryCmd.lastChargeTime = info.getLastChargeTime(); + batteryCmd.numCharges = info.getNumCharges(); + handleGBDeviceEvent(batteryCmd); + } + } + + private void handleUserInfoResult(byte[] value, int status) { + // successfully transferred user info means we're initialized +// commented out, because we have SetDeviceStateAction which sets initialized +// state on every successful initialization. +// if (status == BluetoothGatt.GATT_SUCCESS) { +// setConnectionState(State.INITIALIZED); +// } + } + + private void setConnectionState(State newState) { + getDevice().setState(newState); + getDevice().sendDeviceUpdateIntent(getContext()); + } + + private void handlePairResult(byte[] pairResult, int status) { + if (status != BluetoothGatt.GATT_SUCCESS) { + LOG.info("Pairing MI device failed: " + status); + return; + } + + String value = null; + if (pairResult != null) { + if (pairResult.length == 1) { + try { + if (pairResult[0] == 2) { + LOG.info("Successfully paired MI device"); + return; + } + } catch (Exception ex) { + LOG.warn("Error identifying pairing result", ex); + return; + } + } + value = Arrays.toString(pairResult); + } + LOG.info("MI Band pairing result: " + value); + } + + /** + * Fetch the events from the android device calendars and set the alarms on the miband. + */ + private void sendCalendarEvents() { + try { + TransactionBuilder builder = performInitialized("Send upcoming events"); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + + Prefs prefs = GBApplication.getPrefs(); + int availableSlots = prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0); + + if (availableSlots > 0) { + CalendarEvents upcomingEvents = new CalendarEvents(); + List mEvents = upcomingEvents.getCalendarEventList(getContext()); + + int iteration = 0; + for (CalendarEvents.CalendarEvent mEvt : mEvents) { + if (iteration >= availableSlots || iteration > 2) { + break; + } + int slotToUse = 2 - iteration; + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(mEvt.getBegin()); + byte[] calBytes = MiBandDateConverter.calendarToRawBytes(calendar); + + byte[] alarmMessage = new byte[]{ + MiBandService.COMMAND_SET_TIMER, + (byte) slotToUse, + (byte) 1, + calBytes[0], + calBytes[1], + calBytes[2], + calBytes[3], + calBytes[4], + calBytes[5], + (byte) 0, + (byte) 0 + }; + builder.write(characteristic, alarmMessage); + iteration++; + } + builder.queue(getQueue()); + } + } catch (IOException ex) { + LOG.error("Unable to send Events to MI device", ex); + } + } + + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java index 132ec182..e9f23097 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java @@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -16,9 +17,9 @@ public class V1NotificationStrategy implements NotificationStrategy { static final byte[] startVibrate = new byte[]{MiBandService.COMMAND_SEND_NOTIFICATION, 1}; static final byte[] stopVibrate = new byte[]{MiBandService.COMMAND_STOP_MOTOR_VIBRATE}; - private final MiBandSupport support; + private final AbstractBTLEDeviceSupport support; - public V1NotificationStrategy(MiBandSupport support) { + public V1NotificationStrategy(AbstractBTLEDeviceSupport support) { this.support = support; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java index 32ed1275..c9730360 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java @@ -3,14 +3,15 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import android.bluetooth.BluetoothGattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; public class V2NotificationStrategy implements NotificationStrategy { - private final MiBandSupport support; + private final AbstractBTLEDeviceSupport support; - public V2NotificationStrategy(MiBandSupport support) { + public V2NotificationStrategy(AbstractBTLEDeviceSupport support) { this.support = support; } From 726f7675763e386111d350090c8b8bdf919c16b5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 25 Jul 2016 22:19:39 +0200 Subject: [PATCH 138/569] work towards a Pebble Misfit raw sample table --- .../gadgetbridge/daogen/GBDaoGenerator.java | 37 +++++++++---- .../devices/pebble/MisfitSampleProvider.java | 52 +++++++++++++++++-- .../gadgetbridge/model/ActivitySample.java | 11 +--- .../gadgetbridge/model/HeartRateSample.java | 2 +- .../gadgetbridge/model/Sample.java | 12 +++++ .../pebble/AppMessageHandlerMisfit.java | 11 ++-- 6 files changed, 97 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 63c8c9f1..8610cb47 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -34,7 +34,7 @@ public class GBDaoGenerator { private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(7, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(8, MAIN_PACKAGE + ".entities"); addActivityDescription(schema); @@ -46,6 +46,7 @@ public class GBDaoGenerator { addMiBandActivitySample(schema, user, device); addPebbleActivitySample(schema, user, device); + addPebbleMisfitActivitySample(schema, user, device); new DaoGenerator().generateAll(schema, "app/src/main/java"); } @@ -130,7 +131,9 @@ public class GBDaoGenerator { private static Entity addMiBandActivitySample(Schema schema, Entity user, Entity device) { // public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { Entity activitySample = addEntity(schema, "MiBandActivitySample"); - addCommonActivitySampleProperties(schema, activitySample, user, device); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + addDefaultActivitySampleAttributes(activitySample); + addCommonActivitySampleProperties2(activitySample, user, device); addHeartRateProperties(activitySample); return activitySample; } @@ -144,12 +147,22 @@ public class GBDaoGenerator { private static Entity addPebbleActivitySample(Schema schema, Entity user, Entity device) { // public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { Entity activitySample = addEntity(schema, "PebbleActivitySample"); - addCommonActivitySampleProperties(schema, activitySample, user, device); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + addDefaultActivitySampleAttributes(activitySample); + addCommonActivitySampleProperties2(activitySample, user, device); return activitySample; } - private static void addCommonActivitySampleProperties(Schema schema, Entity activitySample, Entity user, Entity device) { - activitySample.setSuperclass("AbstractActivitySample"); + private static Entity addPebbleMisfitActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "PebbleMisfitSample"); + addCommonActivitySampleProperties("AbstractPebbleMisfitActivitySample", activitySample, user, device); + activitySample.addIntProperty("rawPebbleMisfitSample").notNull(); + addCommonActivitySampleProperties2(activitySample, user, device); + return activitySample; + } + + private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) { + activitySample.setSuperclass(superClass); activitySample.addImport(MODEL_PACKAGE + ".ActivitySample"); activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider"); activitySample.implementsInterface("ActivitySample"); @@ -158,10 +171,10 @@ public class GBDaoGenerator { "intensity, are device specific. Normalized values can be retrieved through the\n" + "corresponding {@link SampleProvider}."); activitySample.addIdProperty(); + } + + private static void addCommonActivitySampleProperties2(Entity activitySample, Entity user, Entity device) { Property timestamp = activitySample.addIntProperty("timestamp").notNull().getProperty(); - activitySample.addIntProperty("rawIntensity").notNull(); - activitySample.addIntProperty("steps").notNull(); - activitySample.addIntProperty("rawKind").notNull(); Property userId = activitySample.addLongProperty("userId").getProperty(); activitySample.addToOne(user, userId); Property deviceId = activitySample.addLongProperty("deviceId").getProperty(); @@ -173,7 +186,13 @@ public class GBDaoGenerator { indexUnique.makeUnique(); activitySample.addIndex(indexUnique); } - + + private static void addDefaultActivitySampleAttributes(Entity activitySample) { + activitySample.addIntProperty("rawIntensity").notNull(); + activitySample.addIntProperty("steps").notNull(); + activitySample.addIntProperty("rawKind").notNull(); + } + private static Entity addEntity(Schema schema, String className) { Entity entity = schema.addEntity(className); entity.addImport("de.greenrobot.dao.AbstractDao"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java index 1fa44f8d..0156e18e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java @@ -1,15 +1,18 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import java.util.List; + import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -public class MisfitSampleProvider extends AbstractPebbleSampleProvider { +public class MisfitSampleProvider implements SampleProvider { protected final float movementDivisor = 300f; public MisfitSampleProvider(GBDevice device, DaoSession session) { - super(device, session); + } @Override @@ -22,12 +25,55 @@ public class MisfitSampleProvider extends AbstractPebbleSampleProvider { return (byte) activityKind; } - @Override public float normalizeIntensity(int rawIntensity) { return rawIntensity / movementDivisor; } + @Override + public List getAllActivitySamples(int timestamp_from, int timestamp_to) { + return null; + } + + @Override + public List getActivitySamples(int timestamp_from, int timestamp_to) { + return null; + } + + @Override + public List getSleepSamples(int timestamp_from, int timestamp_to) { + return null; + } + + @Override + public void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind) { + + } + + @Override + public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind) { + + } + + @Override + public int fetchLatestTimestamp() { + return 0; + } + + @Override + public void addGBActivitySample(AbstractActivitySample activitySample) { + + } + + @Override + public void addGBActivitySamples(AbstractActivitySample[] activitySamples) { + + } + + @Override + public AbstractActivitySample createActivitySample() { + return null; + } @Override public int getID() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java index 24569d30..a7408420 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java @@ -1,15 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.model; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; - -public interface ActivitySample extends TimeStamped { - /** - * Returns the provider of the data. - * - * @return who created the sample data - */ - SampleProvider getProvider(); - +public interface ActivitySample extends Sample { /** * Returns the raw activity kind value as recorded by the SampleProvider */ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java index dcbb5be5..375a6679 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java @@ -1,6 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.model; -public interface HeartRateSample extends TimeStamped { +public interface HeartRateSample extends Sample { /** * Returns the heart rate measured at the corresponding timestamp. * The value is returned in heart beats per minute, in the range from diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java new file mode 100644 index 00000000..e6249552 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java @@ -0,0 +1,12 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; + +public interface Sample extends TimeStamped { + /** + * Returns the provider of the data. + * + * @return who created the sample data + */ + SampleProvider getProvider(); +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 538a21b5..9a88dcc6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -13,15 +13,13 @@ import java.util.SimpleTimeZone; 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.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MisfitSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; -import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -81,6 +79,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { int totalSteps = 0; PebbleActivitySample[] activitySamples = new PebbleActivitySample[samples]; + PebbleMisfitSample[] misfitSamples = new PebbleMisfitSample[samples]; try (DBHandler db = GBApplication.acquireDB()) { MisfitSampleProvider sampleProvider = new MisfitSampleProvider(device, db.getDaoSession()); Long userId = DBHelper.getUser(db.getDaoSession()).getId(); @@ -115,8 +114,10 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { totalSteps += steps; LOG.info("got steps for sample " + i + " : " + steps + "(" + Integer.toHexString(sample & 0xffff) + ")"); - activitySamples[i] = new PebbleActivitySample(null, timestamp + i * 60, intensity, steps, activityKind, userId, deviceId); - activitySamples[i].setProvider(sampleProvider); + //activitySamples[i] = new PebbleActivitySample(null, timestamp + i * 60, intensity, steps, activityKind, userId, deviceId); + //activitySamples[i].setProvider(sampleProvider); + misfitSamples[i] = new PebbleMisfitSample(null, sample & 0xffff, timestamp + i * 60, userId, deviceId); + misfitSamples[i].setProvider(sampleProvider); } LOG.info("total steps for above period: " + totalSteps); From dd5c80c2e7ac5baad6cf80ab129df596b28aa295 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 25 Jul 2016 22:28:40 +0200 Subject: [PATCH 139/569] forgot to add file --- .../AbstractPebbleMisfitActivitySample.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java new file mode 100644 index 00000000..4ecde61a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java @@ -0,0 +1,70 @@ +package nodomain.freeyourgadget.gadgetbridge.entities; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public abstract class AbstractPebbleMisfitActivitySample extends AbstractActivitySample { + abstract public int getRawPebbleMisfitSample(); + + private transient int intensity = 0; + private transient int steps = 0; + private transient int activityKind = ActivityKind.TYPE_UNKNOWN; + + private void calculate() { + int sample = getRawPebbleMisfitSample(); + + if (((sample & 0x83ff) == 0x0001) && ((sample & 0xff00) <= 0x4800)) { + // sleep seems to be from 0x2401 to 0x4801 (0b0IIIII0000000001) where I = intensity ? + intensity = (sample & 0x7c00) >>> 10; + // 9-18 decimal after shift + if (intensity <= 13) { + activityKind = ActivityKind.TYPE_DEEP_SLEEP; + } else { + // FIXME: this leads to too much false positives, ignore for now + //activityKind = ActivityKind.TYPE_LIGHT_SLEEP; + //intensity *= 2; // better visual distinction + } + } else { + if ((sample & 0x0001) == 0) { // 16-??? steps encoded in bits 1-7 + steps = (sample & 0x00fe); + } else { // 0-14 steps encoded in bits 1-3, most of the time fc71 bits are set in that case + steps = (sample & 0x000e); + } + intensity = steps; + activityKind = ActivityKind.TYPE_ACTIVITY; + } + } + + @Override + public int getSteps() { + calculate(); + return steps; + } + + @Override + public int getRawKind() { + calculate(); + return activityKind; + } + + @Override + public int getRawIntensity() { + calculate(); + return intensity; + } + + @Override + public void setRawKind(int kind) { + + } + + @Override + public void setRawIntensity(int intensity) { + + } + + @Override + public void setSteps(int steps) { + + } + +} \ No newline at end of file From 8ea29e6e1d59c216377b47d3a7f4670bc0879d50 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 27 Jul 2016 23:34:13 +0200 Subject: [PATCH 140/569] Refactor database / sample access (#206) We now have separate tables for each provider's samples but a common interface. --- .../gadgetbridge/daogen/GBDaoGenerator.java | 8 +- .../charts/AbstractChartFragment.java | 5 +- .../gadgetbridge/database/DBHelper.java | 5 +- .../gadgetbridge/database/DBOpenHelper.java | 22 +-- .../devices/pebble/MisfitSampleProvider.java | 141 ++++++++++++------ .../entities/AbstractActivitySample.java | 36 ++++- .../gadgetbridge/impl/GBActivitySample.java | 12 +- .../gadgetbridge/model/ActivityKind.java | 1 + .../gadgetbridge/model/ActivitySample.java | 31 +++- .../gadgetbridge/model/HeartRateSample.java | 18 --- .../gadgetbridge/model/Sample.java | 12 -- .../pebble/AppMessageHandlerMisfit.java | 37 +---- 12 files changed, 181 insertions(+), 147 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 8610cb47..24dc8261 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -34,7 +34,7 @@ public class GBDaoGenerator { private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(8, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(9, MAIN_PACKAGE + ".entities"); addActivityDescription(schema); @@ -139,9 +139,7 @@ public class GBDaoGenerator { } private static void addHeartRateProperties(Entity activitySample) { - activitySample.addImport(MODEL_PACKAGE + ".HeartRateSample"); - activitySample.implementsInterface("HeartRateSample"); - activitySample.addIntProperty("heartRate"); + activitySample.addIntProperty("heartRate").notNull(); } private static Entity addPebbleActivitySample(Schema schema, Entity user, Entity device) { @@ -163,9 +161,7 @@ public class GBDaoGenerator { private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) { activitySample.setSuperclass(superClass); - activitySample.addImport(MODEL_PACKAGE + ".ActivitySample"); activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider"); - activitySample.implementsInterface("ActivitySample"); activitySample.setJavaDoc( "This class represents a sample specific to the device. Values like activity kind or\n" + "intensity, are device specific. Normalized values can be retrieved through the\n" + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 914f5c76..49fd68a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -49,7 +49,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; -import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; @@ -465,13 +464,13 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { colors.add(akActivity.color); } activityEntries.add(createBarEntry(value, i)); - if (hr && isValidHeartRateValue(((HeartRateSample)sample).getHeartRate())) { + if (hr && isValidHeartRateValue(sample.getHeartRate())) { if (lastHrSampleIndex > -1 && i - lastHrSampleIndex > HeartRateUtils.MAX_HR_MEASUREMENTS_GAP_MINUTES) { heartrateEntries.add(createLineEntry(0, lastHrSampleIndex + 1)); heartrateEntries.add(createLineEntry(0, i - 1)); } - heartrateEntries.add(createLineEntry(((HeartRateSample)sample).getHeartRate(), i)); + heartrateEntries.add(createLineEntry(sample.getHeartRate(), i)); lastHrSampleIndex = i; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index fb59411f..8144d4ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -30,7 +30,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.entities.UserDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; -import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample; import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; @@ -383,9 +382,7 @@ public class DBHelper { newSample.setSteps(cursor.getInt(colSteps)); int hrValue = cursor.getInt(colCustomShort); - if (newSample instanceof HeartRateSample) { - ((HeartRateSample)newSample).setHeartRate(hrValue); - } + newSample.setHeartRate(hrValue); newSamples.add(newSample); } sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java index 5861f00b..9a61ae7b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java @@ -1,30 +1,10 @@ package nodomain.freeyourgadget.gadgetbridge.database; import android.content.Context; -import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.Nullable; - -import java.util.ArrayList; -import java.util.List; import nodomain.freeyourgadget.gadgetbridge.database.schema.SchemaMigration; -import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; -import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.User; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample; -import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; - -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_INTENSITY; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TYPE; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; public class DBOpenHelper extends DaoMaster.OpenHelper { private final String updaterClassNamePrefix; @@ -38,11 +18,13 @@ public class DBOpenHelper extends DaoMaster.OpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + DaoMaster.createAllTables(db, true); new SchemaMigration(updaterClassNamePrefix).onUpgrade(db, oldVersion, newVersion); } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + DaoMaster.createAllTables(db, true); new SchemaMigration(updaterClassNamePrefix).onDowngrade(db, oldVersion, newVersion); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java index 0156e18e..bce945c8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java @@ -1,18 +1,30 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import java.util.ArrayList; +import java.util.Collections; 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.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSample; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class MisfitSampleProvider implements SampleProvider { +public class MisfitSampleProvider implements SampleProvider { + private final DaoSession mSession; + private final GBDevice mDevice; protected final float movementDivisor = 300f; public MisfitSampleProvider(GBDevice device, DaoSession session) { - + mSession = session; + mDevice = device; } @Override @@ -22,7 +34,7 @@ public class MisfitSampleProvider implements SampleProvider { @Override public int toRawActivityKind(int activityKind) { - return (byte) activityKind; + return activityKind; } @Override @@ -31,47 +43,7 @@ public class MisfitSampleProvider implements SampleProvider { } @Override - public List getAllActivitySamples(int timestamp_from, int timestamp_to) { - return null; - } - - @Override - public List getActivitySamples(int timestamp_from, int timestamp_to) { - return null; - } - - @Override - public List getSleepSamples(int timestamp_from, int timestamp_to) { - return null; - } - - @Override - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind) { - - } - - @Override - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind) { - - } - - @Override - public int fetchLatestTimestamp() { - return 0; - } - - @Override - public void addGBActivitySample(AbstractActivitySample activitySample) { - - } - - @Override - public void addGBActivitySamples(AbstractActivitySample[] activitySamples) { - - } - - @Override - public AbstractActivitySample createActivitySample() { + public PebbleMisfitSample createActivitySample() { return null; } @@ -79,4 +51,83 @@ public class MisfitSampleProvider implements SampleProvider { public int getID() { return SampleProvider.PROVIDER_PEBBLE_MISFIT; } + + @Override + public List getAllActivitySamples(int timestamp_from, int timestamp_to) { + return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); + } + + @Override + public List getActivitySamples(int timestamp_from, int timestamp_to) { + return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ACTIVITY); + } + + @Override + public List getSleepSamples(int timestamp_from, int timestamp_to) { + return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_SLEEP); + } + + @Override + public int fetchLatestTimestamp() { + QueryBuilder qb = getSampleDao().queryBuilder(); + qb.orderDesc(getTimestampSampleProperty()); + qb.limit(1); + List list = qb.build().list(); + if (list.size() >= 1) { + return list.get(0).getTimestamp(); + } + return -1; + } + + @Override + public void addGBActivitySample(PebbleMisfitSample activitySample) { + getSampleDao().insertOrReplace(activitySample); + } + + @Override + public void addGBActivitySamples(PebbleMisfitSample[] activitySamples) { + getSampleDao().insertOrReplaceInTx(activitySamples); + } + + public void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind) { + } + + public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind) { + } + + protected List getGBActivitySamples(int timestamp_from, int timestamp_to, int activityType) { + QueryBuilder qb = getSampleDao().queryBuilder(); + Property timestampProperty = getTimestampSampleProperty(); + Device dbDevice = DBHelper.findDevice(mDevice, mSession); + if (dbDevice == null) { + // no device, no samples + return Collections.emptyList(); + } + Property deviceProperty = getDeviceIdentifierSampleProperty(); + qb.where(deviceProperty.eq(dbDevice.getId()), timestampProperty.ge(timestamp_from)) + .where(timestampProperty.le(timestamp_to)); + List samples = qb.build().list(); + List filteredSamples = new ArrayList<>(); + for (PebbleMisfitSample sample : samples) { + if ((sample.getRawKind() & activityType) != 0) { + sample.setProvider(this); + filteredSamples.add(sample); + } + } + + return filteredSamples; + } + + public AbstractDao getSampleDao() { + return mSession.getPebbleMisfitSampleDao(); + } + + protected Property getTimestampSampleProperty() { + return PebbleMisfitSampleDao.Properties.Timestamp; + } + + protected Property getDeviceIdentifierSampleProperty() { + return PebbleMisfitSampleDao.Properties.DeviceId; + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java index 57d607bc..77ec7508 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java @@ -21,33 +21,61 @@ public abstract class AbstractActivitySample implements ActivitySample { return getProvider().normalizeType(getRawKind()); } + @Override + public int getRawKind() { + return NOT_MEASURED; + } + @Override public float getIntensity() { return getProvider().normalizeIntensity(getRawIntensity()); } - public abstract void setRawKind(int kind); + public void setRawKind(int kind) { + } - public abstract void setRawIntensity(int intensity); + public void setRawIntensity(int intensity) { + } - public abstract void setSteps(int steps); + public void setSteps(int steps) { + } public abstract void setTimestamp(int timestamp); public abstract void setUserId(Long userId); - public abstract Long getUserId(); + @Override + public void setHeartRate(int heartRate) { + } + + @Override + public int getHeartRate() { + return NOT_MEASURED; + } public abstract void setDeviceId(Long deviceId); public abstract Long getDeviceId(); + public abstract Long getUserId(); + + @Override + public int getRawIntensity() { + return NOT_MEASURED; + } + + @Override + public int getSteps() { + return NOT_MEASURED; + } + @Override public String toString() { return getClass().getSimpleName() + "{" + "timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimeStamp(getTimestamp())) + ", intensity=" + getIntensity() + ", steps=" + getSteps() + + ", heartrate=" + getHeartRate() + ", type=" + getKind() + ", userId=" + getUserId() + ", deviceId=" + getDeviceId() + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java index dfaaadeb..ed9f171c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java @@ -13,7 +13,7 @@ public class GBActivitySample implements ActivitySample { private final int intensity; private final int steps; private final int type; - private final int customValue; + private int customValue; public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type) { this(provider, timestamp, intensity, steps, type, 0); @@ -69,6 +69,16 @@ public class GBActivitySample implements ActivitySample { return steps; } + @Override + public int getHeartRate() { + return customValue; + } + + @Override + public void setHeartRate(int value) { + customValue = value; + } + @Override public int getRawKind() { return type; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java index df7bbce3..e292f4b1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java @@ -5,6 +5,7 @@ import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; public class ActivityKind { + public static final int TYPE_NOT_MEASURED = -1; public static final int TYPE_UNKNOWN = 0; public static final int TYPE_ACTIVITY = 1; public static final int TYPE_LIGHT_SLEEP = 2; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java index a7408420..ef4438df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java @@ -1,6 +1,18 @@ package nodomain.freeyourgadget.gadgetbridge.model; -public interface ActivitySample extends Sample { +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; + +public interface ActivitySample extends TimeStamped { + + int NOT_MEASURED = -1; + + /** + * Returns the provider of the data. + * + * @return who created the sample data + */ + SampleProvider getProvider(); + /** * Returns the raw activity kind value as recorded by the SampleProvider */ @@ -27,4 +39,21 @@ public interface ActivitySample extends Sample { * Returns the number of steps performed during the period of this sample */ int getSteps(); + + /** + * Returns the heart rate measured at the corresponding timestamp. + * The value is returned in heart beats per minute, in the range from + * 0-255, where 255 is an illegal value (e.g. due to a bad measurement) + * + * @return the heart rate value in beats per minute, or -1 if none + */ + int getHeartRate(); + + /** + * Sets the heart rate value of this sample. Typically only used in + * generic db migration. + * + * @param value the value in bpm + */ + void setHeartRate(int value); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java deleted file mode 100644 index 375a6679..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/HeartRateSample.java +++ /dev/null @@ -1,18 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.model; - -public interface HeartRateSample extends Sample { - /** - * Returns the heart rate measured at the corresponding timestamp. - * The value is returned in heart beats per minute, in the range from - * 0-255, where 255 is an illegal value (e.g. due to a bad measurement) - * @return the heart rate value in beats per minute, or null if none - */ - Integer getHeartRate(); - - /** - * Sets the heart rate value of this sample. Typically only used in - * generic db migration. - * @param value the value in bpm - */ - void setHeartRate(Integer value); -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java deleted file mode 100644 index e6249552..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Sample.java +++ /dev/null @@ -1,12 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.model; - -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; - -public interface Sample extends TimeStamped { - /** - * Returns the provider of the data. - * - * @return who created the sample data - */ - SampleProvider getProvider(); -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 9a88dcc6..acfd123e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -18,10 +18,8 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MisfitSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class AppMessageHandlerMisfit extends AppMessageHandler { @@ -78,7 +76,6 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { LOG.info("got data from " + startDate + " to " + endDate); int totalSteps = 0; - PebbleActivitySample[] activitySamples = new PebbleActivitySample[samples]; PebbleMisfitSample[] misfitSamples = new PebbleMisfitSample[samples]; try (DBHandler db = GBApplication.acquireDB()) { MisfitSampleProvider sampleProvider = new MisfitSampleProvider(device, db.getDaoSession()); @@ -86,42 +83,16 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); for (int i = 0; i < samples; i++) { short sample = buf.getShort(); - int steps = 0; - int intensity = 0; - int activityKind = ActivityKind.TYPE_UNKNOWN; - - if (((sample & 0x83ff) == 0x0001) && ((sample & 0xff00) <= 0x4800)) { - // sleep seems to be from 0x2401 to 0x4801 (0b0IIIII0000000001) where I = intensity ? - intensity = (sample & 0x7c00) >>> 10; - // 9-18 decimal after shift - if (intensity <= 13) { - activityKind = ActivityKind.TYPE_DEEP_SLEEP; - } else { - // FIXME: this leads to too much false positives, ignore for now - //activityKind = ActivityKind.TYPE_LIGHT_SLEEP; - //intensity *= 2; // better visual distinction - } - } else { - if ((sample & 0x0001) == 0) { // 16-??? steps encoded in bits 1-7 - steps = (sample & 0x00fe); - } else { // 0-14 steps encoded in bits 1-3, most of the time fc71 bits are set in that case - steps = (sample & 0x000e); - } - intensity = steps; - activityKind = ActivityKind.TYPE_ACTIVITY; - } - + misfitSamples[i] = new PebbleMisfitSample(null, sample & 0xffff, timestamp + i * 60, userId, deviceId); + misfitSamples[i].setProvider(sampleProvider); + int steps = misfitSamples[i].getSteps(); totalSteps += steps; LOG.info("got steps for sample " + i + " : " + steps + "(" + Integer.toHexString(sample & 0xffff) + ")"); - //activitySamples[i] = new PebbleActivitySample(null, timestamp + i * 60, intensity, steps, activityKind, userId, deviceId); - //activitySamples[i].setProvider(sampleProvider); - misfitSamples[i] = new PebbleMisfitSample(null, sample & 0xffff, timestamp + i * 60, userId, deviceId); - misfitSamples[i].setProvider(sampleProvider); } LOG.info("total steps for above period: " + totalSteps); - sampleProvider.addGBActivitySamples(activitySamples); + sampleProvider.addGBActivitySamples(misfitSamples); } catch (Exception e) { LOG.error("Error acquiring database", e); return null; From b22111df9d51b5d8940a960361d6c8193590d564 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 28 Jul 2016 22:12:20 +0200 Subject: [PATCH 141/569] Fix field ordering of ActivitySample (c'tor!) and improve importer #206 --- .../gadgetbridge/daogen/GBDaoGenerator.java | 13 ++++++++-- .../gadgetbridge/database/DBHelper.java | 24 +++++++++++++------ .../pebble/AppMessageHandlerMisfit.java | 2 +- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 24dc8261..1c5d7fc8 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -167,22 +167,31 @@ public class GBDaoGenerator { "intensity, are device specific. Normalized values can be retrieved through the\n" + "corresponding {@link SampleProvider}."); activitySample.addIdProperty(); + activitySample.addIntProperty("timestamp").notNull(); } private static void addCommonActivitySampleProperties2(Entity activitySample, Entity user, Entity device) { - Property timestamp = activitySample.addIntProperty("timestamp").notNull().getProperty(); Property userId = activitySample.addLongProperty("userId").getProperty(); activitySample.addToOne(user, userId); Property deviceId = activitySample.addLongProperty("deviceId").getProperty(); activitySample.addToOne(device, deviceId); Index indexUnique = new Index(); - indexUnique.addProperty(timestamp); + indexUnique.addProperty(findProperty(activitySample, "timestamp")); indexUnique.addProperty(deviceId); indexUnique.makeUnique(); activitySample.addIndex(indexUnique); } + private static Property findProperty(Entity entity, String propertyName) { + for (Property prop : entity.getProperties()) { + if (propertyName.equals(prop.getPropertyName())) { + return prop; + } + } + throw new IllegalArgumentException("Property " + propertyName + " not found in Entity " + entity.getClassName()); + } + private static void addDefaultActivitySampleAttributes(Entity activitySample) { activitySample.addIntProperty("rawIntensity").notNull(); activitySample.addIntProperty("steps").notNull(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 8144d4ab..8369bcfb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.entities.UserDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; @@ -373,19 +374,28 @@ public class DBHelper { List newSamples = new ArrayList<>(cursor.getCount()); while (cursor.moveToNext()) { T newSample = sampleProvider.createActivitySample(); + newSample.setProvider(sampleProvider); newSample.setUserId(userId); newSample.setDeviceId(deviceId); newSample.setTimestamp(cursor.getInt(colTimeStamp)); - newSample.setRawKind(cursor.getInt(colType)); - newSample.setProvider(sampleProvider); - newSample.setRawIntensity(cursor.getInt(colIntensity)); - newSample.setSteps(cursor.getInt(colSteps)); - - int hrValue = cursor.getInt(colCustomShort); - newSample.setHeartRate(hrValue); + newSample.setRawKind(getNullableInt(cursor, colType, ActivitySample.NOT_MEASURED)); + newSample.setRawIntensity(getNullableInt(cursor, colIntensity, ActivitySample.NOT_MEASURED)); + newSample.setSteps(getNullableInt(cursor, colSteps, ActivitySample.NOT_MEASURED)); + if (colCustomShort > -1) { + newSample.setHeartRate(getNullableInt(cursor, colCustomShort, ActivitySample.NOT_MEASURED)); + } else { + newSample.setHeartRate(ActivitySample.NOT_MEASURED); + } newSamples.add(newSample); } sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); } } + + private int getNullableInt(Cursor cursor, int columnIndex, int defaultValue) { + if (cursor.isNull(columnIndex)) { + return defaultValue; + } + return cursor.getInt(columnIndex); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index acfd123e..ad74f3a0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -83,7 +83,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); for (int i = 0; i < samples; i++) { short sample = buf.getShort(); - misfitSamples[i] = new PebbleMisfitSample(null, sample & 0xffff, timestamp + i * 60, userId, deviceId); + misfitSamples[i] = new PebbleMisfitSample(null, timestamp + i * 60, sample & 0xffff, userId, deviceId); misfitSamples[i].setProvider(sampleProvider); int steps = misfitSamples[i].getSteps(); totalSteps += steps; From 493444a2a044369b37a14e4ccc51edb2747a9775 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 28 Jul 2016 22:28:29 +0200 Subject: [PATCH 142/569] Remove unused stuff --- .../database/schema/ActivityDBUpdate_X.java | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java deleted file mode 100644 index e23d13e5..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java +++ /dev/null @@ -1,31 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.database.schema; - -import android.database.sqlite.SQLiteDatabase; - -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; - -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_PROVIDER; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_STEPS_PER_DAY; - -/** - * Adds a table "STEPS_PER_DAY". - */ -public class ActivityDBUpdate_X implements DBUpdateScript { - @Override - public void upgradeSchema(SQLiteDatabase db) { - String CREATE_STEPS_PER_DAY_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_STEPS_PER_DAY + " (" - + KEY_TIMESTAMP + " INT," - + KEY_PROVIDER + " TINYINT," - + KEY_STEPS + " MEDIUMINT," - + " PRIMARY KEY (" + KEY_TIMESTAMP + "," + KEY_PROVIDER + ") ON CONFLICT REPLACE)" + DBHelper.getWithoutRowId(); - db.execSQL(CREATE_STEPS_PER_DAY_TABLE); - } - - @Override - public void downgradeSchema(SQLiteDatabase db) { - DBHelper.dropTable(TABLE_STEPS_PER_DAY, db); - } -} From c9a9566dad533ceb36cb4504a8e2daf630ca05b4 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 28 Jul 2016 22:42:16 +0200 Subject: [PATCH 143/569] Minor fixlet --- .../gadgetbridge/service/DeviceSupportFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 6bc07789..fceac174 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -90,7 +90,7 @@ public class DeviceSupportFactory { return deviceSupport; } } catch (Exception e) { - throw new GBException(mContext.getString(R.string.cannot_connect_bt_address_invalid_, e)); + throw new GBException(mContext.getString(R.string.cannot_connect_bt_address_invalid_), e); } } return null; From b43b7948b0bfbeeb3f24f04ba1bee04448a0dd96 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 28 Jul 2016 23:04:37 +0200 Subject: [PATCH 144/569] Let GallCallback return boolean values in order to mark an event as "consumed" (to avoid dispatching the event to further listeners (ble profiles) --- .../btle/AbstractBTLEDeviceSupport.java | 41 +++++++++++++------ .../service/btle/AbstractBTLEOperation.java | 20 ++++----- .../service/btle/AbstractGattCallback.java | 15 ++++--- .../service/btle/GattCallback.java | 14 ++++--- .../profiles/battery/BatteryInfoProfile.java | 5 ++- .../deviceinfo/DeviceInfoProfile.java | 14 +++++-- .../devices/miband/MiBand2Support.java | 28 ++++++++++--- .../service/devices/miband/MiBandSupport.java | 28 ++++++++++--- .../operations/FetchActivityOperation.java | 7 ++-- .../operations/UpdateFirmwareOperation.java | 5 ++- 10 files changed, 122 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java index bfa9f59b..ffc2eda9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java @@ -200,41 +200,56 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im } @Override - public void onCharacteristicRead(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { for (AbstractBleProfile profile : mSupportedProfiles) { - profile.onCharacteristicRead(gatt, characteristic, status); + if (profile.onCharacteristicRead(gatt, characteristic, status)) { + return true; + } } + return false; } @Override - public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { for (AbstractBleProfile profile : mSupportedProfiles) { - profile.onCharacteristicWrite(gatt, characteristic, status); + if (profile.onCharacteristicWrite(gatt, characteristic, status)) { + return true; + } } + return false; } @Override - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { for (AbstractBleProfile profile : mSupportedProfiles) { - profile.onDescriptorRead(gatt, descriptor, status); + if (profile.onDescriptorRead(gatt, descriptor, status)) { + return true; + } } + return false; } @Override - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { for (AbstractBleProfile profile : mSupportedProfiles) { - profile.onDescriptorWrite(gatt, descriptor, status); + if (profile.onDescriptorWrite(gatt, descriptor, status)) { + return true; + } } + return false; } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { for (AbstractBleProfile profile : mSupportedProfiles) { - profile.onCharacteristicChanged(gatt, characteristic); + if (profile.onCharacteristicChanged(gatt, characteristic)) { + return true; + } } + return false; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java index 7747567d..ab41d9a7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java @@ -135,28 +135,28 @@ public abstract class AbstractBTLEOperation } @Override - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - mSupport.onCharacteristicRead(gatt, characteristic, status); + public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + return mSupport.onCharacteristicRead(gatt, characteristic, status); } @Override - public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - mSupport.onCharacteristicWrite(gatt, characteristic, status); + public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + return mSupport.onCharacteristicWrite(gatt, characteristic, status); } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - mSupport.onCharacteristicChanged(gatt, characteristic); + public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + return mSupport.onCharacteristicChanged(gatt, characteristic); } @Override - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { - mSupport.onDescriptorRead(gatt, descriptor, status); + public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + return mSupport.onDescriptorRead(gatt, descriptor, status); } @Override - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { - mSupport.onDescriptorWrite(gatt, descriptor, status); + public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + return mSupport.onDescriptorWrite(gatt, descriptor, status); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java index 76f0a839..5ef65460 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java @@ -17,23 +17,28 @@ public abstract class AbstractGattCallback implements GattCallback { } @Override - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + return false; } @Override - public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + return false; } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + return false; } @Override - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + return false; } @Override - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + return false; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java index 3089856b..107af38d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java @@ -25,6 +25,10 @@ import android.bluetooth.BluetoothGattDescriptor; * Callback interface handling gatt events. * Pretty much the same as {@link BluetoothGattCallback}, except it's an interface * instead of an abstract class. Some handlers commented out, because not used (yet). + * + * Note: the boolean return values indicate whether this callback "consumed" this event + * or not. True means, the event was consumed by this instance and no further instances + * shall be notified. Fallse means, this instance could not handle the event. */ public interface GattCallback { @@ -48,7 +52,7 @@ public interface GattCallback { * @param status * @see BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, BluetoothGattCharacteristic, int) */ - void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status); + boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status); /** * @param gatt @@ -56,7 +60,7 @@ public interface GattCallback { * @param status * @see BluetoothGattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int) */ - void onCharacteristicWrite(BluetoothGatt gatt, + boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status); /** @@ -64,7 +68,7 @@ public interface GattCallback { * @param characteristic * @see BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, BluetoothGattCharacteristic) */ - void onCharacteristicChanged(BluetoothGatt gatt, + boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); /** @@ -73,7 +77,7 @@ public interface GattCallback { * @param status * @see BluetoothGattCallback#onDescriptorRead(BluetoothGatt, BluetoothGattDescriptor, int) */ - void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status); /** @@ -82,7 +86,7 @@ public interface GattCallback { * @param status * @see BluetoothGattCallback#onDescriptorWrite(BluetoothGatt, BluetoothGattDescriptor, int) */ - void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status); // // /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java index d471609b..10df7314 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java @@ -7,7 +7,6 @@ import android.content.Intent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; @@ -43,17 +42,19 @@ public class BatteryInfoProfile extends Abs } @Override - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { UUID charUuid = characteristic.getUuid(); if (charUuid.equals(UUID_CHARACTERISTIC_BATTERY_LEVEL)) { handleBatteryLevel(gatt, characteristic); + return true; } else { LOG.info("Unexpected onCharacteristicRead: " + GattCharacteristic.toString(characteristic)); } } else { LOG.warn("error reading from characteristic:" + GattCharacteristic.toString(characteristic)); } + return false; } private void handleBatteryLevel(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java index 7ae8b609..e7e56ea6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java @@ -7,7 +7,6 @@ import android.content.Intent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; @@ -15,7 +14,6 @@ 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.profiles.AbstractBleProfile; -import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo; public class DeviceInfoProfile extends AbstractBleProfile { private static final Logger LOG = LoggerFactory.getLogger(DeviceInfoProfile.class); @@ -55,33 +53,43 @@ public class DeviceInfoProfile extends Abst } @Override - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { UUID charUuid = characteristic.getUuid(); if (charUuid.equals(UUID_CHARACTERISTIC_MANUFACTURER_NAME_STRING)) { handleManufacturerName(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_MODEL_NUMBER_STRING)) { handleModelNumber(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_SERIAL_NUMBER_STRING)) { handleSerialNumber(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_HARDWARE_REVISION_STRING)) { handleHardwareRevision(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_FIRMWARE_REVISION_STRING)) { handleFirmwareRevision(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_SOFTWARE_REVISION_STRING)) { handleSoftwareRevision(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_SYSTEM_ID)) { handleSystemId(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST)) { handleRegulatoryCertificationData(gatt, characteristic); + return true; } else if (charUuid.equals(UUID_CHARACTERISTIC_PNP_ID)) { handlePnpId(gatt, characteristic); + return true; } else { LOG.info("Unexpected onCharacteristicRead: " + GattCharacteristic.toString(characteristic)); } } else { LOG.warn("error reading from characteristic:" + GattCharacteristic.toString(characteristic)); } + return false; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index 6f053929..d921ee5c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -824,60 +824,76 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { handleBatteryInfo(characteristic.getValue(), BluetoothGatt.GATT_SUCCESS); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_NOTIFICATION.equals(characteristicUUID)) { handleNotificationNotif(characteristic.getValue()); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { handleRealtimeSteps(characteristic.getValue()); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { handleRealtimeSteps(characteristic.getValue()); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { handleHeartrate(characteristic.getValue()); + return true; } else { LOG.info("Unhandled characteristic changed: " + characteristicUUID); logMessageContent(characteristic.getValue()); } + return false; } @Override - public void onCharacteristicRead(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_DEVICE_INFO.equals(characteristicUUID)) { handleDeviceInfo(characteristic.getValue(), status); + return true; } else if (GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME.equals(characteristicUUID)) { handleDeviceName(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { handleBatteryInfo(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { logHeartrate(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) { logDate(characteristic.getValue(), status); + return true; } else { LOG.info("Unhandled characteristic read: " + characteristicUUID); logMessageContent(characteristic.getValue()); } + return false; } @Override - public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_PAIR.equals(characteristicUUID)) { handlePairResult(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_USER_INFO.equals(characteristicUUID)) { handleUserInfoResult(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT.equals(characteristicUUID)) { handleControlPointResult(characteristic.getValue(), status); + return true; } + return false; } /** 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 44716f80..5de476cd 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 @@ -785,60 +785,76 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { handleBatteryInfo(characteristic.getValue(), BluetoothGatt.GATT_SUCCESS); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_NOTIFICATION.equals(characteristicUUID)) { handleNotificationNotif(characteristic.getValue()); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { handleRealtimeSteps(characteristic.getValue()); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { handleRealtimeSteps(characteristic.getValue()); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { handleHeartrate(characteristic.getValue()); + return true; } else { LOG.info("Unhandled characteristic changed: " + characteristicUUID); logMessageContent(characteristic.getValue()); } + return false; } @Override - public void onCharacteristicRead(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_DEVICE_INFO.equals(characteristicUUID)) { handleDeviceInfo(characteristic.getValue(), status); + return true; } else if (GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME.equals(characteristicUUID)) { handleDeviceName(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { handleBatteryInfo(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { logHeartrate(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) { logDate(characteristic.getValue(), status); + return true; } else { LOG.info("Unhandled characteristic read: " + characteristicUUID); logMessageContent(characteristic.getValue()); } + return false; } @Override - public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + public boolean onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_PAIR.equals(characteristicUUID)) { handlePairResult(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_USER_INFO.equals(characteristicUUID)) { handleUserInfoResult(characteristic.getValue(), status); + return true; } else if (MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT.equals(characteristicUUID)) { handleControlPointResult(characteristic.getValue(), status); + return true; } + return false; } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index a116ac59..4814f97f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -151,13 +151,14 @@ public class FetchActivityOperation extends AbstractMiBandOperation { } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA.equals(characteristicUUID)) { handleActivityNotif(characteristic.getValue()); + return true; } else { - super.onCharacteristicChanged(gatt, characteristic); + return super.onCharacteristicChanged(gatt, characteristic); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java index 32185d83..6bffc725 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java @@ -72,14 +72,15 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { UUID characteristicUUID = characteristic.getUuid(); if (MiBandService.UUID_CHARACTERISTIC_NOTIFICATION.equals(characteristicUUID)) { handleNotificationNotif(characteristic.getValue()); } else { super.onCharacteristicChanged(gatt, characteristic); } + return false; } /** From eabe625c47ac7e39697de684c2a99bebce3e32f2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 30 Jul 2016 23:22:27 +0200 Subject: [PATCH 145/569] rename some classes, remove obsolete ones --- .../gadgetbridge/daogen/GBDaoGenerator.java | 6 +- .../gadgetbridge/database/DBHelper.java | 2 +- .../gadgetbridge/devices/SampleProvider.java | 2 +- .../pebble/AbstractPebbleSampleProvider.java | 21 +++-- .../devices/pebble/PebbleCoordinator.java | 10 +-- .../PebbleGadgetBridgeSampleProvider.java | 17 ---- ...r.java => PebbleHealthSampleProvider.java} | 5 +- ...r.java => PebbleMisfitSampleProvider.java} | 4 +- ...java => PebbleMorpheuzSampleProvider.java} | 4 +- .../devices/pebble/AppMessageHandler.java | 8 +- .../pebble/AppMessageHandlerGBPebble.java | 87 ------------------- .../pebble/AppMessageHandlerMisfit.java | 4 +- .../pebble/AppMessageHandlerMorpheuz.java | 15 ++-- .../DatalogSessionHealthOverlayData.java | 4 +- .../pebble/DatalogSessionHealthSleep.java | 4 +- .../pebble/DatalogSessionHealthSteps.java | 10 +-- .../devices/pebble/PebbleProtocol.java | 1 - 17 files changed, 46 insertions(+), 158 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/{HealthSampleProvider.java => PebbleHealthSampleProvider.java} (88%) rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/{MisfitSampleProvider.java => PebbleMisfitSampleProvider.java} (96%) rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/{MorpheuzSampleProvider.java => PebbleMorpheuzSampleProvider.java} (91%) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 1c5d7fc8..3858e5d5 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -45,7 +45,7 @@ public class GBDaoGenerator { Entity device = addDevice(schema, deviceAttributes); addMiBandActivitySample(schema, user, device); - addPebbleActivitySample(schema, user, device); + addPebbleHealthActivitySample(schema, user, device); addPebbleMisfitActivitySample(schema, user, device); new DaoGenerator().generateAll(schema, "app/src/main/java"); @@ -142,9 +142,9 @@ public class GBDaoGenerator { activitySample.addIntProperty("heartRate").notNull(); } - private static Entity addPebbleActivitySample(Schema schema, Entity user, Entity device) { + private static Entity addPebbleHealthActivitySample(Schema schema, Entity user, Entity device) { // public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { - Entity activitySample = addEntity(schema, "PebbleActivitySample"); + Entity activitySample = addEntity(schema, "PebbleHealthActivitySample"); addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); addDefaultActivitySampleAttributes(activitySample); addCommonActivitySampleProperties2(activitySample, user, device); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 8369bcfb..a89f0fa5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -345,7 +345,7 @@ public class DBHelper { private boolean isEmpty(DaoSession session) { long totalSamplesCount = session.getMiBandActivitySampleDao().count(); - totalSamplesCount += session.getPebbleActivitySampleDao().count(); + totalSamplesCount += session.getPebbleHealthActivitySampleDao().count(); return totalSamplesCount == 0; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java index c81745d0..553ace1d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -8,7 +8,7 @@ public interface SampleProvider { // TODO: these constants can all be removed int PROVIDER_MIBAND = 0; int PROVIDER_PEBBLE_MORPHEUZ = 1; - int PROVIDER_PEBBLE_GADGETBRIDGE = 2; + int PROVIDER_PEBBLE_GADGETBRIDGE = 2; // removed int PROVIDER_PEBBLE_MISFIT = 3; int PROVIDER_PEBBLE_HEALTH = 4; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java index ce9f969d..b69b251d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java @@ -4,38 +4,37 @@ import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -public abstract class AbstractPebbleSampleProvider extends AbstractSampleProvider { +public abstract class AbstractPebbleSampleProvider extends AbstractSampleProvider { protected AbstractPebbleSampleProvider(GBDevice device, DaoSession session) { super(device, session); } @Override - public AbstractDao getSampleDao() { - return getSession().getPebbleActivitySampleDao(); + public AbstractDao getSampleDao() { + return getSession().getPebbleHealthActivitySampleDao(); } @Override protected Property getTimestampSampleProperty() { - return PebbleActivitySampleDao.Properties.Timestamp; + return PebbleHealthActivitySampleDao.Properties.Timestamp; } @Override protected Property getRawKindSampleProperty() { - return PebbleActivitySampleDao.Properties.RawKind; + return PebbleHealthActivitySampleDao.Properties.RawKind; } @Override protected Property getDeviceIdentifierSampleProperty() { - return PebbleActivitySampleDao.Properties.DeviceId; + return PebbleHealthActivitySampleDao.Properties.DeviceId; } @Override - public PebbleActivitySample createActivitySample() { - return new PebbleActivitySample(); + public PebbleHealthActivitySample createActivitySample() { + return new PebbleHealthActivitySample(); } } 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 a1425951..9b9c6eb2 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 @@ -52,15 +52,13 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { int activityTracker = prefs.getInt("pebble_activitytracker", SampleProvider.PROVIDER_PEBBLE_HEALTH); switch (activityTracker) { case SampleProvider.PROVIDER_PEBBLE_HEALTH: - return new HealthSampleProvider(device, session); + return new PebbleHealthSampleProvider(device, session); case SampleProvider.PROVIDER_PEBBLE_MISFIT: - return new MisfitSampleProvider(device, session); + return new PebbleMisfitSampleProvider(device, session); case SampleProvider.PROVIDER_PEBBLE_MORPHEUZ: - return new MorpheuzSampleProvider(device, session); - case SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE: - return new PebbleGadgetBridgeSampleProvider(device, session); + return new PebbleMorpheuzSampleProvider(device, session); default: - return new HealthSampleProvider(device, session); + return new PebbleHealthSampleProvider(device, session); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java deleted file mode 100644 index f04c70b4..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleGadgetBridgeSampleProvider.java +++ /dev/null @@ -1,17 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.devices.pebble; - -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; - -public class PebbleGadgetBridgeSampleProvider extends MorpheuzSampleProvider { - public PebbleGadgetBridgeSampleProvider(GBDevice device, DaoSession session) { - super(device, session); - movementDivisor = 63.0f; - } - - @Override - public int getID() { - return SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java similarity index 88% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index b5509903..df82de8d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -2,18 +2,17 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class HealthSampleProvider extends AbstractPebbleSampleProvider { +public class PebbleHealthSampleProvider extends AbstractPebbleSampleProvider { public static final int TYPE_DEEP_SLEEP = 5; public static final int TYPE_LIGHT_SLEEP = 4; public static final int TYPE_ACTIVITY = -1; protected final float movementDivisor = 8000f; - public HealthSampleProvider(GBDevice device, DaoSession session) { + public PebbleHealthSampleProvider(GBDevice device, DaoSession session) { super(device, session); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java similarity index 96% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java index bce945c8..f543a457 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MisfitSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java @@ -16,13 +16,13 @@ import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class MisfitSampleProvider implements SampleProvider { +public class PebbleMisfitSampleProvider implements SampleProvider { private final DaoSession mSession; private final GBDevice mDevice; protected final float movementDivisor = 300f; - public MisfitSampleProvider(GBDevice device, DaoSession session) { + public PebbleMisfitSampleProvider(GBDevice device, DaoSession session) { mSession = session; mDevice = device; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MorpheuzSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java similarity index 91% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MorpheuzSampleProvider.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java index 0e82e608..cee3c3a0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/MorpheuzSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java @@ -5,7 +5,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class MorpheuzSampleProvider extends AbstractPebbleSampleProvider { +public class PebbleMorpheuzSampleProvider extends AbstractPebbleSampleProvider { // raw types public static final int TYPE_DEEP_SLEEP = 5; public static final int TYPE_LIGHT_SLEEP = 4; @@ -14,7 +14,7 @@ public class MorpheuzSampleProvider extends AbstractPebbleSampleProvider { protected float movementDivisor = 5000f; - public MorpheuzSampleProvider(GBDevice device, DaoSession session) { + public PebbleMorpheuzSampleProvider(GBDevice device, DaoSession session) { super(device, session); } 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 6f57458e..956b703a 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 @@ -6,11 +6,9 @@ import android.util.Pair; import java.util.ArrayList; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -39,8 +37,8 @@ public class AppMessageHandler { return null; } - protected PebbleActivitySample createSample(int timestamp, int intensity, int steps, int type, User user, Device device) { - return new PebbleActivitySample(null, timestamp, intensity, steps, type, user.getId(), device.getId()); + protected PebbleHealthActivitySample createSample(int timestamp, int intensity, int steps, int type, User user, Device device) { + return new PebbleHealthActivitySample(null, timestamp, intensity, steps, type, user.getId(), device.getId()); } protected GBDevice getDevice() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java deleted file mode 100644 index 02713c43..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java +++ /dev/null @@ -1,87 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; - -import android.util.Pair; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.SimpleTimeZone; -import java.util.TimeZone; -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.GBDeviceEvent; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleGadgetBridgeSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.User; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; - -public class AppMessageHandlerGBPebble extends AppMessageHandler { - - public static final int KEY_TIMESTAMP = 1; - public static final int KEY_SAMPLES = 2; - - private static final Logger LOG = LoggerFactory.getLogger(AppMessageHandlerGBPebble.class); - - AppMessageHandlerGBPebble(UUID uuid, PebbleProtocol pebbleProtocol) { - super(uuid, pebbleProtocol); - } - - @Override - public GBDeviceEvent[] handleMessage(ArrayList> pairs) { - int timestamp = 0; - for (Pair pair : pairs) { - switch (pair.first) { - case KEY_TIMESTAMP: - TimeZone tz = SimpleTimeZone.getDefault(); - timestamp = (int) pair.second - (tz.getOffset(System.currentTimeMillis())) / 1000; - LOG.info("got timestamp " + timestamp); - break; - case KEY_SAMPLES: - byte[] samples = (byte[]) pair.second; - ByteBuffer samplesBuffer = ByteBuffer.wrap(samples); - samplesBuffer.order(ByteOrder.LITTLE_ENDIAN); - int samples_remaining = samples.length / 2; - LOG.info("got " + samples_remaining + " samples"); - int offset_seconds = 0; - try (DBHandler db = GBApplication.acquireDB()) { - User user = DBHelper.getUser(db.getDaoSession()); - Device device = DBHelper.getDevice(getDevice(), db.getDaoSession()); - PebbleGadgetBridgeSampleProvider sampleProvider = new PebbleGadgetBridgeSampleProvider(getDevice(), db.getDaoSession()); - PebbleActivitySample[] activitySamples = new PebbleActivitySample[samples_remaining]; - int i = 0; - while (samples_remaining-- > 0) { - short sample = samplesBuffer.getShort(); - int type = ((sample & 0xe000) >>> 13); - int intensity = ((sample & 0x1f80) >>> 7); - int steps = (sample & 0x007f); - activitySamples[i++] = createSample(timestamp + offset_seconds, intensity, steps, type, user, device); - activitySamples[i].setProvider(sampleProvider); - offset_seconds += 60; - } - sampleProvider.addGBActivitySamples(activitySamples); - } catch (Exception e) { - LOG.error("Error acquiring database", e); - } - break; - default: - LOG.info("unhandled key: " + pair.first); - break; - } - } - GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(mUUID, mPebbleProtocol.last_id); - return new GBDeviceEvent[]{sendBytes}; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index ad74f3a0..11134de1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -17,7 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MisfitSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleMisfitSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -78,7 +78,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { int totalSteps = 0; PebbleMisfitSample[] misfitSamples = new PebbleMisfitSample[samples]; try (DBHandler db = GBApplication.acquireDB()) { - MisfitSampleProvider sampleProvider = new MisfitSampleProvider(device, db.getDaoSession()); + PebbleMisfitSampleProvider sampleProvider = new PebbleMisfitSampleProvider(device, db.getDaoSession()); Long userId = DBHelper.getUser(db.getDaoSession()).getId(); Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); for (int i = 0; i < samples; i++) { 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 116153e9..fbd9d857 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 @@ -16,10 +16,9 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSleepMonitorResult; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MorpheuzSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleMorpheuzSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -93,18 +92,18 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { int index = ((int) pair.second >> 16); int intensity = ((int) pair.second & 0xffff); LOG.info("got point:" + index + " " + intensity); - int type = MorpheuzSampleProvider.TYPE_UNKNOWN; + int type = PebbleMorpheuzSampleProvider.TYPE_UNKNOWN; if (intensity <= 120) { - type = MorpheuzSampleProvider.TYPE_DEEP_SLEEP; + type = PebbleMorpheuzSampleProvider.TYPE_DEEP_SLEEP; } else if (intensity <= 1000) { - type = MorpheuzSampleProvider.TYPE_LIGHT_SLEEP; + type = PebbleMorpheuzSampleProvider.TYPE_LIGHT_SLEEP; } if (index >= 0) { try (DBHandler db = GBApplication.acquireDB()) { User user = DBHelper.getUser(db.getDaoSession()); Device device = DBHelper.getDevice(getDevice(), db.getDaoSession()); - MorpheuzSampleProvider sampleProvider = new MorpheuzSampleProvider(getDevice(), db.getDaoSession()); - PebbleActivitySample sample = createSample(recording_base_timestamp + index * 600, intensity, 0, type, user, device); + PebbleMorpheuzSampleProvider sampleProvider = new PebbleMorpheuzSampleProvider(getDevice(), db.getDaoSession()); + PebbleHealthActivitySample sample = createSample(recording_base_timestamp + index * 600, intensity, 0, type, user, device); sample.setProvider(sampleProvider); sampleProvider.addGBActivitySample(sample); } catch (Exception e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index 5555835f..d9c1002c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -9,7 +9,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -60,7 +60,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { private boolean store(OverlayRecord[] overlayRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - SampleProvider sampleProvider = new HealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + SampleProvider sampleProvider = new PebbleHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); int latestTimestamp = sampleProvider.fetchLatestTimestamp(); for (OverlayRecord overlayRecord : overlayRecords) { if (latestTimestamp < (overlayRecord.timestampStart + overlayRecord.durationSeconds)) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 3b6561c5..377c4fa0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -9,7 +9,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -59,7 +59,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { private boolean store(SleepRecord[] sleepRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - SampleProvider sampleProvider = new HealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + SampleProvider sampleProvider = new PebbleHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); int latestTimestamp = sampleProvider.fetchLatestTimestamp(); for (SleepRecord sleepRecord : sleepRecords) { if (latestTimestamp < sleepRecord.bedTimeEnd) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index c03c3ab7..f2167505 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -10,8 +10,8 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -75,14 +75,14 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { private void store(StepsRecord[] stepsRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - HealthSampleProvider sampleProvider = new HealthSampleProvider(getDevice(), dbHandler.getDaoSession()); - PebbleActivitySample[] samples = new PebbleActivitySample[stepsRecords.length]; + PebbleHealthSampleProvider sampleProvider = new PebbleHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + PebbleHealthActivitySample[] samples = new PebbleHealthActivitySample[stepsRecords.length]; // TODO: user and device Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); for (int j = 0; j < stepsRecords.length; j++) { StepsRecord stepsRecord = stepsRecords[j]; - samples[j] = new PebbleActivitySample( + samples[j] = new PebbleHealthActivitySample( null, stepsRecord.timestamp, stepsRecord.intensity, 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 fcaabe2f..61a974d4 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 @@ -378,7 +378,6 @@ public class PebbleProtocol extends GBDeviceProtocol { public PebbleProtocol(GBDevice device) { super(device); - mAppMessageHandlers.put(UUID_GBPEBBLE, new AppMessageHandlerGBPebble(UUID_GBPEBBLE, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_MORPHEUZ, new AppMessageHandlerMorpheuz(UUID_MORPHEUZ, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_WHETHERNEAT, new AppMessageHandlerWeatherNeat(UUID_WHETHERNEAT, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_MISFIT, new AppMessageHandlerMisfit(UUID_MISFIT, PebbleProtocol.this)); From 8ba1ae3f3e7e4f0af5e11823251c2abf1d7adf6b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 31 Jul 2016 00:06:26 +0200 Subject: [PATCH 146/569] create extra table for Morpheuz, remove more unused stuff Pebble activity tracker now do not share a common base anymore. TODO: consider creating a custom way of querying activity types like in Misfit. The activity kind stored in the database now is solely based on the intensity, so it is redundant. (#206) --- .../gadgetbridge/daogen/GBDaoGenerator.java | 12 +++++- .../pebble/AbstractPebbleSampleProvider.java | 40 ------------------- .../pebble/PebbleHealthSampleProvider.java | 32 ++++++++++++++- .../pebble/PebbleMorpheuzSampleProvider.java | 35 ++++++++++++++-- .../devices/pebble/AppMessageHandler.java | 7 ---- .../pebble/AppMessageHandlerMorpheuz.java | 12 +++--- 6 files changed, 79 insertions(+), 59 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 3858e5d5..30613461 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -34,7 +34,7 @@ public class GBDaoGenerator { private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(9, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(10, MAIN_PACKAGE + ".entities"); addActivityDescription(schema); @@ -47,6 +47,7 @@ public class GBDaoGenerator { addMiBandActivitySample(schema, user, device); addPebbleHealthActivitySample(schema, user, device); addPebbleMisfitActivitySample(schema, user, device); + addPebbleMorpheuzActivitySample(schema, user, device); new DaoGenerator().generateAll(schema, "app/src/main/java"); } @@ -159,6 +160,15 @@ public class GBDaoGenerator { return activitySample; } + private static Entity addPebbleMorpheuzActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "PebbleMorpheuzSample"); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addIntProperty("rawIntensity").notNull(); + activitySample.addIntProperty("rawKind").notNull(); + addCommonActivitySampleProperties2(activitySample, user, device); + return activitySample; + } + private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) { activitySample.setSuperclass(superClass); activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java deleted file mode 100644 index b69b251d..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/AbstractPebbleSampleProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.devices.pebble; - -import de.greenrobot.dao.AbstractDao; -import de.greenrobot.dao.Property; -import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySampleDao; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; - -public abstract class AbstractPebbleSampleProvider extends AbstractSampleProvider { - protected AbstractPebbleSampleProvider(GBDevice device, DaoSession session) { - super(device, session); - } - - @Override - public AbstractDao getSampleDao() { - return getSession().getPebbleHealthActivitySampleDao(); - } - - @Override - protected Property getTimestampSampleProperty() { - return PebbleHealthActivitySampleDao.Properties.Timestamp; - } - - @Override - protected Property getRawKindSampleProperty() { - return PebbleHealthActivitySampleDao.Properties.RawKind; - } - - @Override - protected Property getDeviceIdentifierSampleProperty() { - return PebbleHealthActivitySampleDao.Properties.DeviceId; - } - - @Override - public PebbleHealthActivitySample createActivitySample() { - return new PebbleHealthActivitySample(); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index df82de8d..84be3712 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -1,11 +1,16 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class PebbleHealthSampleProvider extends AbstractPebbleSampleProvider { +public class PebbleHealthSampleProvider extends AbstractSampleProvider { public static final int TYPE_DEEP_SLEEP = 5; public static final int TYPE_LIGHT_SLEEP = 4; public static final int TYPE_ACTIVITY = -1; @@ -16,6 +21,31 @@ public class PebbleHealthSampleProvider extends AbstractPebbleSampleProvider { super(device, session); } + @Override + public AbstractDao getSampleDao() { + return getSession().getPebbleHealthActivitySampleDao(); + } + + @Override + protected Property getTimestampSampleProperty() { + return PebbleHealthActivitySampleDao.Properties.Timestamp; + } + + @Override + protected Property getRawKindSampleProperty() { + return PebbleHealthActivitySampleDao.Properties.RawKind; + } + + @Override + protected Property getDeviceIdentifierSampleProperty() { + return PebbleHealthActivitySampleDao.Properties.DeviceId; + } + + @Override + public PebbleHealthActivitySample createActivitySample() { + return new PebbleHealthActivitySample(); + } + @Override public int normalizeType(int rawType) { switch (rawType) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java index cee3c3a0..8cd3223e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java @@ -1,15 +1,20 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMorpheuzSample; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMorpheuzSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class PebbleMorpheuzSampleProvider extends AbstractPebbleSampleProvider { +public class PebbleMorpheuzSampleProvider extends AbstractSampleProvider { // raw types public static final int TYPE_DEEP_SLEEP = 5; public static final int TYPE_LIGHT_SLEEP = 4; - public static final int TYPE_ACTIVITY = -1; + public static final int TYPE_ACTIVITY = 1; public static final int TYPE_UNKNOWN = -1; protected float movementDivisor = 5000f; @@ -18,6 +23,31 @@ public class PebbleMorpheuzSampleProvider extends AbstractPebbleSampleProvider { super(device, session); } + @Override + public AbstractDao getSampleDao() { + return getSession().getPebbleMorpheuzSampleDao(); + } + + @Override + protected Property getTimestampSampleProperty() { + return PebbleMorpheuzSampleDao.Properties.Timestamp; + } + + @Override + protected Property getRawKindSampleProperty() { + return PebbleMorpheuzSampleDao.Properties.RawKind; + } + + @Override + protected Property getDeviceIdentifierSampleProperty() { + return PebbleMorpheuzSampleDao.Properties.DeviceId; + } + + @Override + public PebbleMorpheuzSample createActivitySample() { + return new PebbleMorpheuzSample(); + } + @Override public int normalizeType(int rawType) { switch (rawType) { @@ -28,7 +58,6 @@ public class PebbleMorpheuzSampleProvider extends AbstractPebbleSampleProvider { case TYPE_ACTIVITY: return ActivityKind.TYPE_ACTIVITY; default: -// case TYPE_UNKNOWN: // fall through return ActivityKind.TYPE_UNKNOWN; } } 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 956b703a..1944f2d5 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,9 +7,6 @@ import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; public class AppMessageHandler { @@ -37,10 +34,6 @@ public class AppMessageHandler { return null; } - protected PebbleHealthActivitySample createSample(int timestamp, int intensity, int steps, int type, User user, Device device) { - return new PebbleHealthActivitySample(null, timestamp, intensity, steps, type, user.getId(), device.getId()); - } - protected GBDevice getDevice() { return mPebbleProtocol.getDevice(); } 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 fbd9d857..f61f377c 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 @@ -17,9 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSleepMonitorResult; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleMorpheuzSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMorpheuzSample; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class AppMessageHandlerMorpheuz extends AppMessageHandler { @@ -92,7 +90,7 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { int index = ((int) pair.second >> 16); int intensity = ((int) pair.second & 0xffff); LOG.info("got point:" + index + " " + intensity); - int type = PebbleMorpheuzSampleProvider.TYPE_UNKNOWN; + int type = PebbleMorpheuzSampleProvider.TYPE_ACTIVITY; if (intensity <= 120) { type = PebbleMorpheuzSampleProvider.TYPE_DEEP_SLEEP; } else if (intensity <= 1000) { @@ -100,10 +98,10 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { } if (index >= 0) { try (DBHandler db = GBApplication.acquireDB()) { - User user = DBHelper.getUser(db.getDaoSession()); - Device device = DBHelper.getDevice(getDevice(), db.getDaoSession()); + Long userId = DBHelper.getUser(db.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); PebbleMorpheuzSampleProvider sampleProvider = new PebbleMorpheuzSampleProvider(getDevice(), db.getDaoSession()); - PebbleHealthActivitySample sample = createSample(recording_base_timestamp + index * 600, intensity, 0, type, user, device); + PebbleMorpheuzSample sample = new PebbleMorpheuzSample(null, recording_base_timestamp + index * 600, intensity, type, userId, deviceId); sample.setProvider(sampleProvider); sampleProvider.addGBActivitySample(sample); } catch (Exception e) { From 4fe498efc2843eac7389d2fb968c67c6a695bb3b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 31 Jul 2016 23:49:19 +0200 Subject: [PATCH 147/569] Pebble: delay 100ms after writing a pebble packet to the output steam This fixes a problem on newer firmwares, probably from 3.0 on, where sending an appmessage packet right after acknowledging a previous incoming appmessage packet results in our outgoing appmessage packet to be NACKed by the pebble firmware and not even reaching the app running on the pebble. --- .../gadgetbridge/service/devices/pebble/PebbleIoThread.java | 4 ++++ 1 file changed, 4 insertions(+) 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 89337b88..a1ac5b33 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 @@ -457,6 +457,10 @@ public class PebbleIoThread extends GBDeviceIoThread { } catch (IOException e) { LOG.error("Error writing.", e); } + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } } @Override From 43d7566c0b305be030e232292b009d0a6396e222 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 1 Aug 2016 22:18:57 +0200 Subject: [PATCH 148/569] some more microsteps #206 --- .../devices/miband/MiBand2Service.java | 254 ++++++++++++++++++ .../service/btle/BLETypeConversions.java | 112 ++++++++ .../btle/profiles/deviceinfo/DeviceInfo.java | 15 ++ 3 files changed, 381 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java new file mode 100644 index 00000000..f73f6ad0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -0,0 +1,254 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.miband; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID; + +public class MiBand2Service { + + + public static final UUID UUID_SERVICE_MIBAND_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE0")); + public static final UUID UUID_SERVICE_MIBAND2_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE1")); + public static final UUID UUID_SERVICE_HEART_RATE = UUID.fromString(String.format(BASE_UUID, "180D")); + public static final UUID UUID_SERVICE_WEIGHT_SERVICE = UUID.fromString("00001530-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOQN_CHARACTERISTIC0 = UUID.fromString("00000000-0000-3512-2118-0009af100700"); + + + +// public static final UUID UUID_CHARACTERISTIC_DEVICE_INFO = UUID.fromString(String.format(BASE_UUID, "FF01")); +// +// public static final UUID UUID_CHARACTERISTIC_DEVICE_NAME = UUID.fromString(String.format(BASE_UUID, "FF02")); +// +// public static final UUID UUID_CHARACTERISTIC_NOTIFICATION = UUID.fromString(String.format(BASE_UUID, "FF03")); +// +// public static final UUID UUID_CHARACTERISTIC_USER_INFO = UUID.fromString(String.format(BASE_UUID, "FF04")); +// +// public static final UUID UUID_CHARACTERISTIC_CONTROL_POINT = UUID.fromString(String.format(BASE_UUID, "FF05")); +// +// public static final UUID UUID_CHARACTERISTIC_REALTIME_STEPS = UUID.fromString(String.format(BASE_UUID, "FF06")); +// +// public static final UUID UUID_CHARACTERISTIC_ACTIVITY_DATA = UUID.fromString(String.format(BASE_UUID, "FF07")); +// +// public static final UUID UUID_CHARACTERISTIC_FIRMWARE_DATA = UUID.fromString(String.format(BASE_UUID, "FF08")); +// +// public static final UUID UUID_CHARACTERISTIC_LE_PARAMS = UUID.fromString(String.format(BASE_UUID, "FF09")); +// +// public static final UUID UUID_CHARACTERISTIC_DATE_TIME = UUID.fromString(String.format(BASE_UUID, "FF0A")); +// +// public static final UUID UUID_CHARACTERISTIC_STATISTICS = UUID.fromString(String.format(BASE_UUID, "FF0B")); +// +// public static final UUID UUID_CHARACTERISTIC_BATTERY = UUID.fromString(String.format(BASE_UUID, "FF0C")); +// +// public static final UUID UUID_CHARACTERISTIC_TEST = UUID.fromString(String.format(BASE_UUID, "FF0D")); +// +// public static final UUID UUID_CHARACTERISTIC_SENSOR_DATA = UUID.fromString(String.format(BASE_UUID, "FF0E")); +// +// public static final UUID UUID_CHARACTERISTIC_PAIR = UUID.fromString(String.format(BASE_UUID, "FF0F")); +// +// public static final UUID UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT = UUID.fromString(String.format(BASE_UUID, "2A39")); +// public static final UUID UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT = UUID.fromString(String.format(BASE_UUID, "2A37")); +// +// +// +// /* FURTHER UUIDS that were mixed with the other params below. The base UUID for these is unknown */ +// +// public static final byte ALIAS_LEN = 0xa; +// +// /*NOTIFICATIONS: usually received on the UUID_CHARACTERISTIC_NOTIFICATION characteristic */ +// +// public static final byte NOTIFY_NORMAL = 0x0; +// +// public static final byte NOTIFY_FIRMWARE_UPDATE_FAILED = 0x1; +// +// public static final byte NOTIFY_FIRMWARE_UPDATE_SUCCESS = 0x2; +// +// public static final byte NOTIFY_CONN_PARAM_UPDATE_FAILED = 0x3; +// +// public static final byte NOTIFY_CONN_PARAM_UPDATE_SUCCESS = 0x4; +// +// public static final byte NOTIFY_AUTHENTICATION_SUCCESS = 0x5; +// +// public static final byte NOTIFY_AUTHENTICATION_FAILED = 0x6; +// +// public static final byte NOTIFY_FITNESS_GOAL_ACHIEVED = 0x7; +// +// public static final byte NOTIFY_SET_LATENCY_SUCCESS = 0x8; +// +// public static final byte NOTIFY_RESET_AUTHENTICATION_FAILED = 0x9; +// +// public static final byte NOTIFY_RESET_AUTHENTICATION_SUCCESS = 0xa; +// +// public static final byte NOTIFY_FW_CHECK_FAILED = 0xb; +// +// public static final byte NOTIFY_FW_CHECK_SUCCESS = 0xc; +// +// public static final byte NOTIFY_STATUS_MOTOR_NOTIFY = 0xd; +// +// public static final byte NOTIFY_STATUS_MOTOR_CALL = 0xe; +// +// public static final byte NOTIFY_STATUS_MOTOR_DISCONNECT = 0xf; +// +// public static final byte NOTIFY_STATUS_MOTOR_SMART_ALARM = 0x10; +// +// public static final byte NOTIFY_STATUS_MOTOR_ALARM = 0x11; +// +// public static final byte NOTIFY_STATUS_MOTOR_GOAL = 0x12; +// +// public static final byte NOTIFY_STATUS_MOTOR_AUTH = 0x13; +// +// public static final byte NOTIFY_STATUS_MOTOR_SHUTDOWN = 0x14; +// +// public static final byte NOTIFY_STATUS_MOTOR_AUTH_SUCCESS = 0x15; +// +// public static final byte NOTIFY_STATUS_MOTOR_TEST = 0x16; +// +// // 0x18 is returned when we cancel data sync, perhaps is an ack for this message +// +// public static final byte NOTIFY_UNKNOWN = -0x1; +// +// public static final int NOTIFY_PAIR_CANCEL = 0xef; +// +// public static final int NOTIFY_DEVICE_MALFUNCTION = 0xff; +// +// +// /* MESSAGES: unknown */ +// +// public static final byte MSG_CONNECTED = 0x0; +// +// public static final byte MSG_DISCONNECTED = 0x1; +// +// public static final byte MSG_CONNECTION_FAILED = 0x2; +// +// public static final byte MSG_INITIALIZATION_FAILED = 0x3; +// +// public static final byte MSG_INITIALIZATION_SUCCESS = 0x4; +// +// public static final byte MSG_STEPS_CHANGED = 0x5; +// +// public static final byte MSG_DEVICE_STATUS_CHANGED = 0x6; +// +// public static final byte MSG_BATTERY_STATUS_CHANGED = 0x7; +// +// /* COMMANDS: usually sent to UUID_CHARACTERISTIC_CONTROL_POINT characteristic */ +// +// public static final byte COMMAND_SET_TIMER = 0x4; +// +// public static final byte COMMAND_SET_FITNESS_GOAL = 0x5; +// +// public static final byte COMMAND_FETCH_DATA = 0x6; +// +// public static final byte COMMAND_SEND_FIRMWARE_INFO = 0x7; +// +// public static final byte COMMAND_SEND_NOTIFICATION = 0x8; +// +// public static final byte COMMAND_CONFIRM_ACTIVITY_DATA_TRANSFER_COMPLETE = 0xa; +// +// public static final byte COMMAND_SYNC = 0xb; +// +// public static final byte COMMAND_REBOOT = 0xc; +// +// public static final byte COMMAND_SET_WEAR_LOCATION = 0xf; +// +// public static final byte COMMAND_STOP_SYNC_DATA = 0x11; +// +// public static final byte COMMAND_STOP_MOTOR_VIBRATE = 0x13; +// +// public static final byte COMMAND_SET_REALTIME_STEPS_NOTIFICATION = 0x3; +// +// public static final byte COMMAND_SET_REALTIME_STEP = 0x10; +// +// // Test HR +// public static final byte COMMAND_SET_HR_SLEEP = 0x0; +// public static final byte COMMAND_SET__HR_CONTINUOUS = 0x1; +// public static final byte COMMAND_SET_HR_MANUAL = 0x2; +// +// +// /* FURTHER COMMANDS: unchecked therefore left commented +// +// +// public static final byte COMMAND_FACTORY_RESET = 0x9t; +// +// public static final int COMMAND_SET_COLOR_THEME = et; +// +// public static final byte COMMAND_GET_SENSOR_DATA = 0x12t +// +// */ +// +// /* CONNECTION: unknown +// +// public static final CONNECTION_LATENCY_LEVEL_LOW = 0x0t; +// +// public static final CONNECTION_LATENCY_LEVEL_MEDIUM = 0x1t; +// +// public static final CONNECTION_LATENCY_LEVEL_HIGH = 0x2t; +// +// */ +// +// /* MODES: probably related to the sample data structure +// */ +// +// public static final byte MODE_REGULAR_DATA_LEN_BYTE = 0x0; +// +// // was MODE_REGULAR_DATA_LEN_MINITE +// public static final byte MODE_REGULAR_DATA_LEN_MINUTE = 0x1; +// +// /* PROFILE: unknown +// +// public static final PROFILE_STATE_UNKNOWN:I = 0x0 +// +// public static final PROFILE_STATE_INITIALIZATION_SUCCESS:I = 0x1 +// +// public static final PROFILE_STATE_INITIALIZATION_FAILED:I = 0x2 +// +// public static final PROFILE_STATE_AUTHENTICATION_SUCCESS:I = 0x3 +// +// public static final PROFILE_STATE_AUTHENTICATION_FAILED:I = 0x4 +// +// */ +// +// // TEST_*: sent to UUID_CHARACTERISTIC_TEST characteristic +// +// public static final byte TEST_DISCONNECTED_REMINDER = 0x5; +// +// public static final byte TEST_NOTIFICATION = 0x3; +// +// public static final byte TEST_REMOTE_DISCONNECT = 0x1; +// +// public static final byte TEST_SELFTEST = 0x2; + + private static final Map MIBAND_DEBUG; + + static { + MIBAND_DEBUG = new HashMap<>(); + MIBAND_DEBUG.put(UUID_SERVICE_MIBAND_SERVICE, "MiBand Service"); + MIBAND_DEBUG.put(UUID_SERVICE_HEART_RATE, "MiBand HR Service"); + +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_INFO, "Device Info"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_NAME, "Device Name"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_NOTIFICATION, "Notification"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_USER_INFO, "User Info"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_CONTROL_POINT, "Control Point"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_REALTIME_STEPS, "Realtime Steps"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_ACTIVITY_DATA, "Activity Data"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_FIRMWARE_DATA, "Firmware Data"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_LE_PARAMS, "LE Params"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DATE_TIME, "Date/Time"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_STATISTICS, "Statistics"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_BATTERY, "Battery"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_TEST, "Test"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_SENSOR_DATA, "Sensor Data"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_PAIR, "Pair"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT, "Heart Rate Control Point"); +// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT, "Heart Rate Measure"); + } + + public static String lookup(UUID uuid, String fallback) { + String name = MIBAND_DEBUG.get(uuid); + if (name == null) { + name = fallback; + } + return name; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java new file mode 100644 index 00000000..9119b54c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -0,0 +1,112 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; + +/** + * Provides methods to convert standard BLE units to byte sequences and vice versa. + */ +public class BLETypeConversions { + /** + * Converts a timestamp to the byte sequence to be sent to the current time characteristic + * + * @param timestamp + * @return + * @see GattCharacteristic#UUID_CHARACTERISTIC_CURRENT_TIME + */ + public static byte[] calendarToRawBytes(Calendar timestamp, boolean honorDeviceTimeOffset) { + + // The mi-band device currently records sleep + // only if it happens after 10pm and before 7am. + // The offset is used to trick the device to record sleep + // in non-standard hours. + // If you usually sleep, say, from 6am to 2pm, set the + // shift to -8, so at 6am the device thinks it's still 10pm + // of the day before. + if (honorDeviceTimeOffset) { + int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(); + if (offsetInHours != 0) { + timestamp.add(Calendar.HOUR_OF_DAY, offsetInHours); + } + } + + byte[] year = fromUint16(timestamp.get(Calendar.YEAR)); + return new byte[] { + year[0], + year[1], + fromUint8(timestamp.get(Calendar.MONTH) + 1), + fromUint8(timestamp.get(Calendar.DATE)), + fromUint8(timestamp.get(Calendar.HOUR_OF_DAY)), + fromUint8(timestamp.get(Calendar.MINUTE)), + fromUint8(timestamp.get(Calendar.SECOND)) + }; + } + + /** + * uses the standard algorithm to convert bytes received from the MiBand to a Calendar object + * + * @param value + * @return + */ + public static GregorianCalendar rawBytesToCalendar(byte[] value, boolean honorDeviceTimeOffset) { + if (value.length >= 7) { + int year = toUint16(value[0], value[1]); + GregorianCalendar timestamp = new GregorianCalendar( + year, + value[2], + value[3], + value[4], + value[5], + value[6] + ); + + if (honorDeviceTimeOffset) { + int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(); + if (offsetInHours != 0) { + timestamp.add(Calendar.HOUR_OF_DAY,-offsetInHours); + } + } + + return timestamp; + } + + return createCalendar(); + } + + public static int toUint16(byte... bytes) { + return bytes[0] | (bytes[1] << 8); + } + + public static byte[] fromUint16(int value) { + return new byte[] { + (byte) (value & 0xff), + (byte) ((value >> 8) & 0xff), + }; + } + public static byte fromUint8(int value) { + return (byte) (value & 0xff); + } + + /** + * Creates a calendar object representing the current date and time. + */ + public static GregorianCalendar createCalendar() { + return new GregorianCalendar(); + } + + public static byte[] join(byte[] start, byte[] end) { + if (start == null || start.length == 0) { + return end; + } + if (end == null || end.length == 0) { + return start; + } + + byte[] result = new byte[start.length + end.length]; + System.arraycopy(start, 0, result, 0, start.length); + System.arraycopy(end, 0, result, start.length, end.length); + return result; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java index 2dac1fb9..de83b567 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java @@ -130,4 +130,19 @@ public class DeviceInfo implements Parcelable{ public void setPnpId(String pnpId) { this.pnpId = pnpId; } + + @Override + public String toString() { + return "DeviceInfo{" + + "manufacturerName='" + manufacturerName + '\'' + + ", modelNumber='" + modelNumber + '\'' + + ", serialNumber='" + serialNumber + '\'' + + ", hardwareRevision='" + hardwareRevision + '\'' + + ", firmwareRevision='" + firmwareRevision + '\'' + + ", softwareRevision='" + softwareRevision + '\'' + + ", systemId='" + systemId + '\'' + + ", regulatoryCertificationDataList='" + regulatoryCertificationDataList + '\'' + + ", pnpId='" + pnpId + '\'' + + '}'; + } } From 9520e23439ab3324ec83f6809d42580eff7824b3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 2 Aug 2016 00:30:05 +0200 Subject: [PATCH 149/569] fix negative steps in Charts with Morpheuz --- .../gadgetbridge/activities/charts/ActivityAnalysis.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java index d73d963d..8d0719f2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java @@ -17,7 +17,7 @@ public class ActivityAnalysis { ActivityAmount previousAmount = null; ActivitySample previousSample = null; for (ActivitySample sample : samples) { - ActivityAmount amount = null; + ActivityAmount amount; switch (sample.getKind()) { case ActivityKind.TYPE_DEEP_SLEEP: amount = deepSleep; @@ -43,8 +43,6 @@ public class ActivityAnalysis { previousAmount.addSeconds(sharedTimeDifference); amount.addSeconds(sharedTimeDifference); } - } else { - // nothing to do, we can only calculate when we have the next sample } previousAmount = amount; @@ -69,7 +67,10 @@ public class ActivityAnalysis { public int calculateTotalSteps(List samples) { int totalSteps = 0; for (ActivitySample sample : samples) { - totalSteps += sample.getSteps(); + int steps = sample.getSteps(); + if (steps > 0) { + totalSteps += sample.getSteps(); + } } return totalSteps; } From 43f3913669ec056f6396ea46969de26fe4aa5f9d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 2 Aug 2016 00:43:37 +0200 Subject: [PATCH 150/569] update Japanese, Ukranian and Spanish from transifex (thanks!) --- app/src/main/res/values-es/strings.xml | 2 ++ app/src/main/res/values-ja/strings.xml | 2 ++ app/src/main/res/values-uk/strings.xml | 17 +++++++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 975150f4..40b0cd16 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -74,6 +74,8 @@ Buscar localización Latitud Longitud + Por favor, activa la localización por red + localización encontrada Forzar protocolo de notificación Esta opción fuerza el uso del último protocolo de notificación dependiendo de la versión de firmware. ¡HABILÍTALO SOLO SI SABES LO QUE ESTÁS HACIENDO! Habilitar características no probadas diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 91c193b1..bb04e7ca 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -74,6 +74,8 @@ 場所の取得 緯度 経度 + ネットワークの場所を有効にしてください + 場所を取得しました 通知プロトコルを強制する このオプションを指定すると、ファームウェアのバージョンに応じて強制的に最新の通知プロトコルを使用します。何をしているかわかっている場合のみ有効にしてください! 未テストの機能を有効にする diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 766b99bb..a577c611 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -2,7 +2,7 @@ Gadgetbridge Gadgetbridge - Параметри + Налаштування Зневадження Вихід Синхронізувати @@ -26,6 +26,7 @@ Параметри Загальні параметри З\'єднання із пристроєм при активації Bluetooth + Перепід\'єднуватись автоматично Переважний музичний програвач Типовий Дата і час @@ -44,16 +45,28 @@ Підтримка додатків, які надсилають сповіщення на Pebble за допомогою Intent. Може використовуватись для Conversations. Підтримка звичайних сповіщень … навіть коли екран увімкнено + Не тубрувати завжди коли екран вимкнуто ніколи Перелік заблокованих додатків + Відповіді + Загальний суфікс Параметри для розробників Адреса Mi—Band Параметри Pebble + Трекер активності Переважний трекер активності + Синхронізувати Pebble Health + Синхронізувати Misfit + Синхронізувати Morpheuz Дозволити доступ іншим особам Додати експериментальну підтримку додатків Android, які використовують PebbleKit + Схід і захід сонця + Надіслати час сходу і заходу сонця оснований на місцезнаходженні до розкладу Pebble + Місцезнаходження + Широта + Довгота Примусовий протокол сповіщень Цей параметр примусово вмикає новий протокол сповіщень, який залежить від версії мікропрограми. ВМИКАЙТЕ, ЯКЩО ВИ ЗНАЄТЕ НА ЩО ЙДЕТЕ. Увімкнути не перевірені можливості @@ -118,7 +131,7 @@ Крапля води Дзвінок Будильник - Вібро + Вібрація SMS-сповіщення Параметри вібро Загальні сповіщення From a7b9ae5596dbed8d717fd960dfc1e94fc981bb5c Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 4 Aug 2016 00:05:01 +0200 Subject: [PATCH 151/569] whitelist fw 4.15.12.10 #369 --- .../gadgetbridge/devices/miband/MiBandFWHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index 41ba4451..59dd8bda 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -47,6 +47,7 @@ public class MiBandFWHelper { 16779782, //1.0.10.6 reported on the wiki 16779787, //1.0.10.11 tested by developer //FW_16779790, //1.0.10.14 reported on the wiki (vibration does not work currently) + 68094986, // 4.15.12.10 tested by developer 68158215, // 4.16.3.7 tested by developer 84870926, // 5.15.7.14 tested by developer }; From e05d40dc7edc9f2581db7094a382aa785d5bc3fe Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 6 Aug 2016 01:19:38 +0200 Subject: [PATCH 152/569] Pebble: Support for latest version of Morpheuz (4.6) Also a few simplifications and minor fixes. Morpheuz 3.7 should still work (last version for FW 2.x) --- .../pebble/AppMessageHandlerMorpheuz.java | 65 +++++++++++++++---- 1 file changed, 51 insertions(+), 14 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 f61f377c..b81b345d 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 @@ -23,22 +23,37 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class AppMessageHandlerMorpheuz extends AppMessageHandler { public static final int KEY_POINT = 1; + public static final int KEY_POINT_46 = 10000; public static final int KEY_CTRL = 2; + public static final int KEY_CTRL_46 = 10001; public static final int KEY_FROM = 3; + public static final int KEY_FROM_46 = 10002; public static final int KEY_TO = 4; + public static final int KEY_TO_46 = 10003; public static final int KEY_BASE = 5; + public static final int KEY_BASE_46 = 10004; public static final int KEY_VERSION = 6; + public static final int KEY_VERSION_46 = 10005; public static final int KEY_GONEOFF = 7; + public static final int KEY_GONEOFF_46 = 10006; public static final int KEY_TRANSMIT = 8; + public static final int KEY_TRANSMIT_46 = 10007; public static final int KEY_AUTO_RESET = 9; + public static final int KEY_AUTO_RESET_46 = 10008; + public static final int KEY_SNOOZES = 10; + public static final int KEY_SNOOZES_46 = 10009; + public static final int KEY_FAULT_46 = 10010; public static final int CTRL_TRANSMIT_DONE = 1; public static final int CTRL_VERSION_DONE = 2; public static final int CTRL_GONEOFF_DONE = 4; public static final int CTRL_DO_NEXT = 8; public static final int CTRL_SET_LAST_SENT = 16; + public static final int CTRL_LAZARUS = 32; + public static final int CTRL_SNOOZES_DONE = 64; // 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 @@ -71,21 +86,25 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { for (Pair pair : pairs) { switch (pair.first) { case KEY_TRANSMIT: - case KEY_GONEOFF: - if (pair.first == KEY_GONEOFF) { - alarm_gone_off = (int) pair.second; - LOG.info("got gone off: " + alarm_gone_off / 60 + ":" + alarm_gone_off % 60); - } + 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 = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_GONEOFF_DONE | AppMessageHandlerMorpheuz.CTRL_TRANSMIT_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT; + 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); @@ -109,32 +128,46 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { } } - ctrl_message = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT | AppMessageHandlerMorpheuz.CTRL_DO_NEXT; + 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 = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT | AppMessageHandlerMorpheuz.CTRL_DO_NEXT; + 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 = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT | AppMessageHandlerMorpheuz.CTRL_DO_NEXT; + ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; break; case KEY_VERSION: - LOG.info("got version: " + ((float) ((int) pair.second) / 10.0f)); - ctrl_message = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE; + 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 = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT | AppMessageHandlerMorpheuz.CTRL_DO_NEXT; + ctrl_message |= CTRL_SET_LAST_SENT | CTRL_DO_NEXT; break; case KEY_AUTO_RESET: - ctrl_message = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT | AppMessageHandlerMorpheuz.CTRL_DO_NEXT; + 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); @@ -150,7 +183,11 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { GBDeviceEventSendBytes sendBytesCtrl = null; if (ctrl_message > 0) { sendBytesCtrl = new GBDeviceEventSendBytes(); - sendBytesCtrl.encodedBytes = encodeMorpheuzMessage(AppMessageHandlerMorpheuz.KEY_CTRL, ctrl_message); + int ctrlkey = KEY_CTRL; + if (version >= 46) { + ctrlkey = KEY_CTRL_46; + } + sendBytesCtrl.encodedBytes = encodeMorpheuzMessage(ctrlkey, ctrl_message); } // ctrl and sleep monitor might be null, thats okay From 6b2565e4c9dd9fa46da0f3c57bd0a9ea35ea9b57 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 7 Aug 2016 01:47:15 +0200 Subject: [PATCH 153/569] DB refactoring: remove activity type from Morpheuz database, determinate it in PebbleMorpheuzSampleProvider instead --- .../gadgetbridge/daogen/GBDaoGenerator.java | 3 +- .../devices/AbstractSampleProvider.java | 4 ++ .../pebble/PebbleMorpheuzSampleProvider.java | 40 +++++++++++++++++-- .../pebble/AppMessageHandlerMorpheuz.java | 8 +--- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 30613461..64a3a8d2 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -34,7 +34,7 @@ public class GBDaoGenerator { private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(10, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(11, MAIN_PACKAGE + ".entities"); addActivityDescription(schema); @@ -164,7 +164,6 @@ public class GBDaoGenerator { Entity activitySample = addEntity(schema, "PebbleMorpheuzSample"); addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); activitySample.addIntProperty("rawIntensity").notNull(); - activitySample.addIntProperty("rawKind").notNull(); addCommonActivitySampleProperties2(activitySample, user, device); return activitySample; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index 227543b5..3721e74c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -91,6 +91,10 @@ public abstract class AbstractSampleProvider i } protected List getGBActivitySamples(int timestamp_from, int timestamp_to, int activityType) { + if (getRawKindSampleProperty() == null && activityType != ActivityKind.TYPE_ALL) { + // if we do not have a raw kind property we cannot query anything else then TYPE_ALL + return Collections.emptyList(); + } QueryBuilder qb = getSampleDao().queryBuilder(); Property timestampProperty = getTimestampSampleProperty(); Device dbDevice = DBHelper.findDevice(getmDevice(), getSession()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java index 8cd3223e..c43a58c9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java @@ -1,5 +1,8 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import java.util.ArrayList; +import java.util.List; + import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; @@ -15,7 +18,7 @@ public class PebbleMorpheuzSampleProvider extends AbstractSampleProvider getActivitySamples(int timestamp_from, int timestamp_to) { + List samples = getAllActivitySamples(timestamp_from, timestamp_to); + List filteredSamples = new ArrayList<>(); + for (PebbleMorpheuzSample sample : samples) { + if (sample.getRawIntensity() > 1000) { + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); + filteredSamples.add(sample); + } + } + + return filteredSamples; + } + + @Override + public List getSleepSamples(int timestamp_from, int timestamp_to) { + List samples = getAllActivitySamples(timestamp_from, timestamp_to); + List filteredSamples = new ArrayList<>(); + for (PebbleMorpheuzSample sample : samples) { + if (sample.getRawIntensity() < 1000) { + if (sample.getRawIntensity() <= 120) { + sample.setRawKind(ActivityKind.TYPE_DEEP_SLEEP); + } else { + sample.setRawKind(ActivityKind.TYPE_LIGHT_SLEEP); + } + filteredSamples.add(sample); + } + } + + return filteredSamples; + } + @Override public int getID() { return SampleProvider.PROVIDER_PEBBLE_MORPHEUZ; 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 b81b345d..2ec80438 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 @@ -109,18 +109,12 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { int index = ((int) pair.second >> 16); int intensity = ((int) pair.second & 0xffff); LOG.info("got point:" + index + " " + intensity); - int type = PebbleMorpheuzSampleProvider.TYPE_ACTIVITY; - if (intensity <= 120) { - type = PebbleMorpheuzSampleProvider.TYPE_DEEP_SLEEP; - } else if (intensity <= 1000) { - type = PebbleMorpheuzSampleProvider.TYPE_LIGHT_SLEEP; - } 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(null, recording_base_timestamp + index * 600, intensity, type, userId, deviceId); + PebbleMorpheuzSample sample = new PebbleMorpheuzSample(null, recording_base_timestamp + index * 600, intensity, userId, deviceId); sample.setProvider(sampleProvider); sampleProvider.addGBActivitySample(sample); } catch (Exception e) { From c4f83d68cdff3b53d505f2d9cb10919a2e9c2fd0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 7 Aug 2016 11:45:09 +0200 Subject: [PATCH 154/569] refactoring: add generic support for manually filtering samples by acticty kind in AbstractSampleProvider This allows a lot of simplifications for Morpheuz and Misfit --- .../gadgetbridge/daogen/GBDaoGenerator.java | 2 +- .../devices/AbstractSampleProvider.java | 25 +++++- .../pebble/PebbleMisfitSampleProvider.java | 89 ++----------------- .../pebble/PebbleMorpheuzSampleProvider.java | 78 +++------------- .../AbstractPebbleMisfitActivitySample.java | 18 +--- .../AbstractPebbleMorpheuzActivitySample.java | 17 ++++ 6 files changed, 61 insertions(+), 168 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 64a3a8d2..a934cd9c 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -162,7 +162,7 @@ public class GBDaoGenerator { private static Entity addPebbleMorpheuzActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "PebbleMorpheuzSample"); - addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + addCommonActivitySampleProperties("AbstractPebbleMorpheuzActivitySample", activitySample, user, device); activitySample.addIntProperty("rawIntensity").notNull(); addCommonActivitySampleProperties2(activitySample, user, device); return activitySample; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index 3721e74c..b1425832 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -1,5 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -44,12 +45,20 @@ public abstract class AbstractSampleProvider i @Override public List getActivitySamples(int timestamp_from, int timestamp_to) { - return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ACTIVITY); + if (getRawKindSampleProperty() != null) { + return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ACTIVITY); + } else { + return getActivitySamplesByActivityFilter(timestamp_from, timestamp_to, ActivityKind.TYPE_ACTIVITY); + } } @Override public List getSleepSamples(int timestamp_from, int timestamp_to) { - return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_SLEEP); + if (getRawKindSampleProperty() != null) { + return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_SLEEP); + } else { + return getActivitySamplesByActivityFilter(timestamp_from, timestamp_to, ActivityKind.TYPE_SLEEP); + } } @Override @@ -147,6 +156,18 @@ public abstract class AbstractSampleProvider i trailingConditions); } + private List getActivitySamplesByActivityFilter(int timestamp_from, int timestamp_to, int activityFilter) { + List samples = getAllActivitySamples(timestamp_from, timestamp_to); + List filteredSamples = new ArrayList<>(); + + for (T sample : samples) { + if ((sample.getKind() & activityFilter) != 0) { + filteredSamples.add(sample); + } + } + return filteredSamples; + } + public abstract AbstractDao getSampleDao(); protected abstract Property getRawKindSampleProperty(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java index f543a457..7615e0a5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java @@ -1,30 +1,20 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; -import java.util.ArrayList; -import java.util.Collections; -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.PebbleMisfitSample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMisfitSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public class PebbleMisfitSampleProvider implements SampleProvider { - private final DaoSession mSession; - private final GBDevice mDevice; +public class PebbleMisfitSampleProvider extends AbstractSampleProvider { protected final float movementDivisor = 300f; public PebbleMisfitSampleProvider(GBDevice device, DaoSession session) { - mSession = session; - mDevice = device; + super(device, session); } @Override @@ -53,73 +43,13 @@ public class PebbleMisfitSampleProvider implements SampleProvider getAllActivitySamples(int timestamp_from, int timestamp_to) { - return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); - } - - @Override - public List getActivitySamples(int timestamp_from, int timestamp_to) { - return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ACTIVITY); - } - - @Override - public List getSleepSamples(int timestamp_from, int timestamp_to) { - return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_SLEEP); - } - - @Override - public int fetchLatestTimestamp() { - QueryBuilder qb = getSampleDao().queryBuilder(); - qb.orderDesc(getTimestampSampleProperty()); - qb.limit(1); - List list = qb.build().list(); - if (list.size() >= 1) { - return list.get(0).getTimestamp(); - } - return -1; - } - - @Override - public void addGBActivitySample(PebbleMisfitSample activitySample) { - getSampleDao().insertOrReplace(activitySample); - } - - @Override - public void addGBActivitySamples(PebbleMisfitSample[] activitySamples) { - getSampleDao().insertOrReplaceInTx(activitySamples); - } - - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind) { - } - - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind) { - } - - protected List getGBActivitySamples(int timestamp_from, int timestamp_to, int activityType) { - QueryBuilder qb = getSampleDao().queryBuilder(); - Property timestampProperty = getTimestampSampleProperty(); - Device dbDevice = DBHelper.findDevice(mDevice, mSession); - if (dbDevice == null) { - // no device, no samples - return Collections.emptyList(); - } - Property deviceProperty = getDeviceIdentifierSampleProperty(); - qb.where(deviceProperty.eq(dbDevice.getId()), timestampProperty.ge(timestamp_from)) - .where(timestampProperty.le(timestamp_to)); - List samples = qb.build().list(); - List filteredSamples = new ArrayList<>(); - for (PebbleMisfitSample sample : samples) { - if ((sample.getRawKind() & activityType) != 0) { - sample.setProvider(this); - filteredSamples.add(sample); - } - } - - return filteredSamples; - } - public AbstractDao getSampleDao() { - return mSession.getPebbleMisfitSampleDao(); + return getSession().getPebbleMisfitSampleDao(); + } + + @Override + protected Property getRawKindSampleProperty() { + return null; } protected Property getTimestampSampleProperty() { @@ -129,5 +59,4 @@ public class PebbleMisfitSampleProvider implements SampleProvider { - // raw types - public static final int TYPE_DEEP_SLEEP = 5; - public static final int TYPE_LIGHT_SLEEP = 4; - public static final int TYPE_ACTIVITY = 1; - public static final int TYPE_UNKNOWN = 0; protected float movementDivisor = 5000f; @@ -51,73 +42,24 @@ public class PebbleMorpheuzSampleProvider extends AbstractSampleProvider getActivitySamples(int timestamp_from, int timestamp_to) { - List samples = getAllActivitySamples(timestamp_from, timestamp_to); - List filteredSamples = new ArrayList<>(); - for (PebbleMorpheuzSample sample : samples) { - if (sample.getRawIntensity() > 1000) { - sample.setRawKind(ActivityKind.TYPE_ACTIVITY); - filteredSamples.add(sample); - } - } - - return filteredSamples; - } - - @Override - public List getSleepSamples(int timestamp_from, int timestamp_to) { - List samples = getAllActivitySamples(timestamp_from, timestamp_to); - List filteredSamples = new ArrayList<>(); - for (PebbleMorpheuzSample sample : samples) { - if (sample.getRawIntensity() < 1000) { - if (sample.getRawIntensity() <= 120) { - sample.setRawKind(ActivityKind.TYPE_DEEP_SLEEP); - } else { - sample.setRawKind(ActivityKind.TYPE_LIGHT_SLEEP); - } - filteredSamples.add(sample); - } - } - - return filteredSamples; - } - @Override public int getID() { return SampleProvider.PROVIDER_PEBBLE_MORPHEUZ; } + + @Override + public int normalizeType(int rawType) { + return rawType; + } + + @Override + public int toRawActivityKind(int activityKind) { + return activityKind; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java index 4ecde61a..53e47c1a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java @@ -41,7 +41,7 @@ public abstract class AbstractPebbleMisfitActivitySample extends AbstractActivit } @Override - public int getRawKind() { + public int getKind() { calculate(); return activityKind; } @@ -51,20 +51,4 @@ public abstract class AbstractPebbleMisfitActivitySample extends AbstractActivit calculate(); return intensity; } - - @Override - public void setRawKind(int kind) { - - } - - @Override - public void setRawIntensity(int intensity) { - - } - - @Override - public void setSteps(int steps) { - - } - } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java new file mode 100644 index 00000000..4a47d7b1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java @@ -0,0 +1,17 @@ +package nodomain.freeyourgadget.gadgetbridge.entities; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public abstract class AbstractPebbleMorpheuzActivitySample extends AbstractActivitySample { + + @Override + public int getKind() { + int rawIntensity = getRawIntensity(); + if (rawIntensity <= 120) { + return ActivityKind.TYPE_DEEP_SLEEP; + } else if (rawIntensity <= 1000) { + return ActivityKind.TYPE_LIGHT_SLEEP; + } + return ActivityKind.TYPE_ACTIVITY; + } +} From af58b4600d6b6be1e081d9439cde8638f4c96302 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 7 Aug 2016 12:15:40 +0200 Subject: [PATCH 155/569] whoops --- .../gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java index 7615e0a5..98bc244c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java @@ -34,7 +34,7 @@ public class PebbleMisfitSampleProvider extends AbstractSampleProvider Date: Mon, 8 Aug 2016 09:01:36 +0200 Subject: [PATCH 156/569] db refactoring: add proposed schema for pebble health overlays --- .../gadgetbridge/daogen/GBDaoGenerator.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index a934cd9c..10a07e09 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -46,6 +46,7 @@ public class GBDaoGenerator { addMiBandActivitySample(schema, user, device); addPebbleHealthActivitySample(schema, user, device); + addPebbleHealthActivityKindOverlay(schema, user, device); addPebbleMisfitActivitySample(schema, user, device); addPebbleMorpheuzActivitySample(schema, user, device); @@ -152,6 +153,28 @@ public class GBDaoGenerator { return activitySample; } + private static Entity addPebbleHealthActivityKindOverlay(Schema schema, Entity user, Entity device) { + Entity activityOverlay = addEntity(schema, "PebbleHealthActivityOverlay"); + activityOverlay.addIdProperty(); + Property timestampFrom = activityOverlay.addIntProperty("timestampFrom").notNull().getProperty(); + Property timestampTo = activityOverlay.addIntProperty("timestampTo").notNull().getProperty(); + activityOverlay.addIntProperty("rawKind").notNull(); + + Property userId = activityOverlay.addLongProperty("userId").getProperty(); + activityOverlay.addToOne(user, userId); + Property deviceId = activityOverlay.addLongProperty("deviceId").getProperty(); + activityOverlay.addToOne(device, deviceId); + + Index indexUnique = new Index(); + indexUnique.addProperty(deviceId); + indexUnique.addProperty(timestampFrom); + indexUnique.addProperty(timestampTo); + indexUnique.makeUnique(); + activityOverlay.addIndex(indexUnique); + + return activityOverlay; + } + private static Entity addPebbleMisfitActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "PebbleMisfitSample"); addCommonActivitySampleProperties("AbstractPebbleMisfitActivitySample", activitySample, user, device); From 5072d6b95914fb73922b27265486a0264bcbcb78 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 8 Aug 2016 19:16:53 +0200 Subject: [PATCH 157/569] Pebble: try to write to health activity overlay table --- .../pebble/DatalogSessionHealthOverlayData.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index d9c1002c..40be306a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -4,12 +4,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlay; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -60,7 +66,14 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { private boolean store(OverlayRecord[] overlayRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - SampleProvider sampleProvider = new PebbleHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); + DaoSession session = dbHandler.getDaoSession(); + Long userId = DBHelper.getUser(session).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), session).getId(); + + PebbleHealthActivityOverlayDao overlayDao = session.getPebbleHealthActivityOverlayDao(); + + SampleProvider sampleProvider = new PebbleHealthSampleProvider(getDevice(), session); + List overlayList = new ArrayList<>(); int latestTimestamp = sampleProvider.fetchLatestTimestamp(); for (OverlayRecord overlayRecord : overlayRecords) { if (latestTimestamp < (overlayRecord.timestampStart + overlayRecord.durationSeconds)) @@ -75,7 +88,9 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { default: //TODO: other values refer to unknown activity types. } + overlayList.add(new PebbleHealthActivityOverlay(null, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type, userId, deviceId)); } + overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { LOG.debug(ex.getMessage()); } From fe5ec74ca1a2dd3202e531d717070f97d1ac45ed Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 9 Aug 2016 11:56:05 +0200 Subject: [PATCH 158/569] Pebble Health: read overlay data in sample provider and patch in raw kind completely untested --- .../pebble/PebbleHealthSampleProvider.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index 84be3712..c34b19d9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -1,10 +1,17 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import java.util.Collections; +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.PebbleHealthActivityOverlay; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -21,6 +28,37 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider getAllActivitySamples(int timestamp_from, int timestamp_to) { + List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); + + Device dbDevice = DBHelper.findDevice(getmDevice(), getSession()); + if (dbDevice == null) { + // no device, no samples + return Collections.emptyList(); + } + Property timestampProperty = getTimestampSampleProperty(); + Property deviceProperty = getDeviceIdentifierSampleProperty(); + + QueryBuilder qb = getSession().getPebbleHealthActivityOverlayDao().queryBuilder(); + + // I assume it returns the records by id ascending ... (last overlay is dominant) + qb.where(deviceProperty.eq(dbDevice.getId()), timestampProperty.ge(timestamp_from)) + .where(timestampProperty.le(timestamp_to)); + List overlayRecords = qb.build().list(); + + for (PebbleHealthActivitySample sample : samples) { + for (PebbleHealthActivityOverlay overlay : overlayRecords) { + if (overlay.getTimestampFrom() <= sample.getTimestamp() && overlay.getTimestampTo() >= sample.getTimestamp()) { + // patch in the raw kind + sample.setRawKind(overlay.getRawKind()); + } + } + } + + return samples; + } + @Override public AbstractDao getSampleDao() { return getSession().getPebbleHealthActivitySampleDao(); @@ -33,7 +71,9 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider Date: Tue, 9 Aug 2016 17:52:07 +0200 Subject: [PATCH 159/569] Use the right properties to build the query and fix the comparison operators. Performance: iterate first on the smaller dataset. --- .../devices/pebble/PebbleHealthSampleProvider.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index c34b19d9..6cacb548 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -12,6 +12,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlay; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -37,25 +38,22 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider qb = getSession().getPebbleHealthActivityOverlayDao().queryBuilder(); // I assume it returns the records by id ascending ... (last overlay is dominant) - qb.where(deviceProperty.eq(dbDevice.getId()), timestampProperty.ge(timestamp_from)) - .where(timestampProperty.le(timestamp_to)); + qb.where(PebbleHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()), PebbleHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from)) + .where(PebbleHealthActivityOverlayDao.Properties.TimestampTo.le(timestamp_to)); List overlayRecords = qb.build().list(); - for (PebbleHealthActivitySample sample : samples) { - for (PebbleHealthActivityOverlay overlay : overlayRecords) { - if (overlay.getTimestampFrom() <= sample.getTimestamp() && overlay.getTimestampTo() >= sample.getTimestamp()) { + for (PebbleHealthActivityOverlay overlay : overlayRecords) { + for (PebbleHealthActivitySample sample : samples) { + if (overlay.getTimestampFrom() >= sample.getTimestamp() && overlay.getTimestampTo() <= sample.getTimestamp()) { // patch in the raw kind sample.setRawKind(overlay.getRawKind()); } } } - return samples; } From 4a7a34f4619546fbae5417ab36574bda233f5f98 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 9 Aug 2016 18:05:24 +0200 Subject: [PATCH 160/569] Use only the overlay table for storing overlay data. This removes the need to wait to have minute samples to store the overlay data, hence store has been made void instead of boolean. --- .../DatalogSessionHealthOverlayData.java | 25 ++--------- .../pebble/DatalogSessionHealthSleep.java | 44 +++++++++++-------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index 40be306a..677060a5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -11,13 +11,10 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlay; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { @@ -61,10 +58,11 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { overlayRecords[recordIdx] = new OverlayRecord(recordType, datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt()); } - return store(overlayRecords);//NACK if we cannot store the data yet, the watch will send the overlay records again. + store(overlayRecords); + return true; } - private boolean store(OverlayRecord[] overlayRecords) { + private void store(OverlayRecord[] overlayRecords) { try (DBHandler dbHandler = GBApplication.acquireDB()) { DaoSession session = dbHandler.getDaoSession(); Long userId = DBHelper.getUser(session).getId(); @@ -72,29 +70,14 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { PebbleHealthActivityOverlayDao overlayDao = session.getPebbleHealthActivityOverlayDao(); - SampleProvider sampleProvider = new PebbleHealthSampleProvider(getDevice(), session); List overlayList = new ArrayList<>(); - int latestTimestamp = sampleProvider.fetchLatestTimestamp(); for (OverlayRecord overlayRecord : overlayRecords) { - if (latestTimestamp < (overlayRecord.timestampStart + overlayRecord.durationSeconds)) - return false; - switch (overlayRecord.type) { - case 1: - sampleProvider.changeStoredSamplesType(overlayRecord.timestampStart, (overlayRecord.timestampStart + overlayRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP)); - break; - case 2: - sampleProvider.changeStoredSamplesType(overlayRecord.timestampStart, (overlayRecord.timestampStart + overlayRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP)); - break; - default: - //TODO: other values refer to unknown activity types. - } - overlayList.add(new PebbleHealthActivityOverlay(null, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type, userId, deviceId)); + overlayList.add(new PebbleHealthActivityOverlay(null, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type, userId, deviceId)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { LOG.debug(ex.getMessage()); } - return true; } private class OverlayRecord { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 377c4fa0..762ba706 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -4,14 +4,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlay; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { @@ -54,25 +57,30 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { datalogMessage.getInt()); } - return store(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. - } - - private boolean store(SleepRecord[] sleepRecords) { - try (DBHandler dbHandler = GBApplication.acquireDB()) { - SampleProvider sampleProvider = new PebbleHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); - int latestTimestamp = sampleProvider.fetchLatestTimestamp(); - for (SleepRecord sleepRecord : sleepRecords) { - if (latestTimestamp < sleepRecord.bedTimeEnd) - return false; - sampleProvider.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP)); - } - } catch (Exception ex) { - LOG.debug(ex.getMessage()); - } + store(sleepRecords); return true; } + private void store(SleepRecord[] sleepRecords) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + Long userId = DBHelper.getUser(session).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), session).getId(); + + PebbleHealthActivityOverlayDao overlayDao = session.getPebbleHealthActivityOverlayDao(); + + List overlayList = new ArrayList<>(); + for (SleepRecord sleepRecord : sleepRecords) { + overlayList.add(new PebbleHealthActivityOverlay(null, sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, sleepRecord.type, userId, deviceId)); //TODO: consider if "-1" is what we really want + } + overlayDao.insertOrReplaceInTx(overlayList); + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } + } + private class SleepRecord { + int type = 1; //sleep, hardcoded as we don't get other info int offsetUTC; //probably int bedTimeStart; int bedTimeEnd; From 22d0387f76af990b65ac4d8a987b9ce803cb631b Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 9 Aug 2016 20:05:42 +0200 Subject: [PATCH 161/569] Fix the comparison order again ,as it was correct originally. Partial revert of 5cfddbb7e9cd095a28950d9d6a37450e1e664f8e --- .../gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index 6cacb548..392744e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -48,7 +48,7 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider= sample.getTimestamp() && overlay.getTimestampTo() <= sample.getTimestamp()) { + if (overlay.getTimestampFrom() <= sample.getTimestamp() && sample.getTimestamp() <= overlay.getTimestampTo()) { // patch in the raw kind sample.setRawKind(overlay.getRawKind()); } From 23c289ce1a2f4b3823b8f6b7febcbcd1941ec2ee Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 9 Aug 2016 20:22:05 +0200 Subject: [PATCH 162/569] Use the real raw values as received by the device. Some types were also added, even though they are educated guesses. --- .../devices/pebble/PebbleHealthSampleProvider.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index 392744e5..57de0eae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -19,8 +19,11 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class PebbleHealthSampleProvider extends AbstractSampleProvider { - public static final int TYPE_DEEP_SLEEP = 5; - public static final int TYPE_LIGHT_SLEEP = 4; + public static final int TYPE_LIGHT_SLEEP = 1; + public static final int TYPE_DEEP_SLEEP = 2; + public static final int TYPE_LIGHT_NAP = 3; //probably + public static final int TYPE_DEEP_NAP = 4; //probably + public static final int TYPE_WALK = 5; //probably public static final int TYPE_ACTIVITY = -1; protected final float movementDivisor = 8000f; @@ -87,8 +90,10 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider Date: Wed, 10 Aug 2016 23:06:07 +0200 Subject: [PATCH 163/569] db refactoring: remove raw activity kind field from pebble health minute data, add a blob column for raw undecoded health minute data. also: - change column order for all pebble related minute data tables (mandatory stuff first, then custom columns, for easier addition of new columns) - remove unused code TODO: - fix column order in Mi Band table also --- .../gadgetbridge/daogen/GBDaoGenerator.java | 24 +++++++--------- .../devices/AbstractSampleProvider.java | 28 ------------------- .../gadgetbridge/devices/SampleProvider.java | 6 ---- .../devices/UnknownDeviceCoordinator.java | 14 ---------- .../AbstractPebbleHealthActivitySample.java | 19 +++++++++++++ .../pebble/AppMessageHandlerMisfit.java | 2 +- .../pebble/AppMessageHandlerMorpheuz.java | 2 +- .../pebble/DatalogSessionHealthSteps.java | 8 +++--- 8 files changed, 35 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 10a07e09..01091eb7 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -34,7 +34,7 @@ public class GBDaoGenerator { private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(11, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(12, MAIN_PACKAGE + ".entities"); addActivityDescription(schema); @@ -131,10 +131,11 @@ public class GBDaoGenerator { } private static Entity addMiBandActivitySample(Schema schema, Entity user, Entity device) { -// public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { Entity activitySample = addEntity(schema, "MiBandActivitySample"); addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); - addDefaultActivitySampleAttributes(activitySample); + activitySample.addIntProperty("rawIntensity").notNull(); + activitySample.addIntProperty("steps").notNull(); + activitySample.addIntProperty("rawKind").notNull(); addCommonActivitySampleProperties2(activitySample, user, device); addHeartRateProperties(activitySample); return activitySample; @@ -145,11 +146,12 @@ public class GBDaoGenerator { } private static Entity addPebbleHealthActivitySample(Schema schema, Entity user, Entity device) { -// public GBActivitySample(SampleProvider provider, int timestamp, int intensity, int steps, int type, int customValue) { Entity activitySample = addEntity(schema, "PebbleHealthActivitySample"); - addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); - addDefaultActivitySampleAttributes(activitySample); + addCommonActivitySampleProperties("AbstractPebbleHealthActivitySample", activitySample, user, device); addCommonActivitySampleProperties2(activitySample, user, device); + activitySample.addByteArrayProperty("rawPebbleHealthData"); + activitySample.addIntProperty("rawIntensity").notNull(); + activitySample.addIntProperty("steps").notNull(); return activitySample; } @@ -178,16 +180,16 @@ public class GBDaoGenerator { private static Entity addPebbleMisfitActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "PebbleMisfitSample"); addCommonActivitySampleProperties("AbstractPebbleMisfitActivitySample", activitySample, user, device); - activitySample.addIntProperty("rawPebbleMisfitSample").notNull(); addCommonActivitySampleProperties2(activitySample, user, device); + activitySample.addIntProperty("rawPebbleMisfitSample").notNull(); return activitySample; } private static Entity addPebbleMorpheuzActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "PebbleMorpheuzSample"); addCommonActivitySampleProperties("AbstractPebbleMorpheuzActivitySample", activitySample, user, device); - activitySample.addIntProperty("rawIntensity").notNull(); addCommonActivitySampleProperties2(activitySample, user, device); + activitySample.addIntProperty("rawIntensity").notNull(); return activitySample; } @@ -224,12 +226,6 @@ public class GBDaoGenerator { throw new IllegalArgumentException("Property " + propertyName + " not found in Entity " + entity.getClassName()); } - private static void addDefaultActivitySampleAttributes(Entity activitySample) { - activitySample.addIntProperty("rawIntensity").notNull(); - activitySample.addIntProperty("steps").notNull(); - activitySample.addIntProperty("rawKind").notNull(); - } - private static Entity addEntity(Schema schema, String className) { Entity entity = schema.addEntity(className); entity.addImport("de.greenrobot.dao.AbstractDao"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index b1425832..38586338 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -61,18 +61,6 @@ public abstract class AbstractSampleProvider i } } - @Override - public int fetchLatestTimestamp() { - QueryBuilder qb = getSampleDao().queryBuilder(); - qb.orderDesc(getTimestampSampleProperty()); - qb.limit(1); - List list = qb.build().list(); - if (list.size() >= 1) { - return list.get(0).getTimestamp(); - } - return -1; - } - @Override public void addGBActivitySample(T activitySample) { getSampleDao().insertOrReplace(activitySample); @@ -83,22 +71,6 @@ public abstract class AbstractSampleProvider i getSampleDao().insertOrReplaceInTx(activitySamples); } - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind) { - List samples = getAllActivitySamples(timestampFrom, timestampTo); - for (T sample : samples) { - sample.setRawKind(kind); - } - getSampleDao().updateInTx(samples); - } - - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind) { - List samples = getGBActivitySamples(timestampFrom, timestampTo, fromKind); - for (T sample : samples) { - sample.setRawKind(toKind); - } - getSampleDao().updateInTx(samples); - } - protected List getGBActivitySamples(int timestamp_from, int timestamp_to, int activityType) { if (getRawKindSampleProperty() == null && activityType != ActivityKind.TYPE_ALL) { // if we do not have a raw kind property we cannot query anything else then TYPE_ALL diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java index 553ace1d..ba931695 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -28,12 +28,6 @@ public interface SampleProvider { List getSleepSamples(int timestamp_from, int timestamp_to); - void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind); - - void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind); - - int fetchLatestTimestamp(); - void addGBActivitySample(T activitySample); void addGBActivitySamples(T[] activitySamples); 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 49df5658..764d3eb5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -7,7 +7,6 @@ import android.net.Uri; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -49,19 +48,6 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { return null; } - @Override - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind) { - } - - @Override - public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind) { - } - - @Override - public int fetchLatestTimestamp() { - return 0; - } - @Override public void addGBActivitySample(AbstractActivitySample activitySample) { } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java new file mode 100644 index 00000000..79f277b1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java @@ -0,0 +1,19 @@ +package nodomain.freeyourgadget.gadgetbridge.entities; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public abstract class AbstractPebbleHealthActivitySample extends AbstractActivitySample { + abstract public byte[] getRawPebbleHealthData(); + + private transient int rawActivityKind = ActivityKind.TYPE_UNKNOWN; + + @Override + public int getRawKind() { + return rawActivityKind; + } + + @Override + public void setRawKind(int kind) { + this.rawActivityKind = kind; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 11134de1..42acbcaf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -83,7 +83,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); for (int i = 0; i < samples; i++) { short sample = buf.getShort(); - misfitSamples[i] = new PebbleMisfitSample(null, timestamp + i * 60, sample & 0xffff, userId, deviceId); + misfitSamples[i] = new PebbleMisfitSample(null, timestamp + i * 60, userId, deviceId, sample & 0xffff); misfitSamples[i].setProvider(sampleProvider); int steps = misfitSamples[i].getSteps(); totalSteps += steps; 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 2ec80438..9b16cde9 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 @@ -114,7 +114,7 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { 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(null, recording_base_timestamp + index * 600, intensity, userId, deviceId); + PebbleMorpheuzSample sample = new PebbleMorpheuzSample(null, recording_base_timestamp + index * 600, userId, deviceId, intensity); sample.setProvider(sampleProvider); sampleProvider.addGBActivitySample(sample); } catch (Exception e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index f2167505..0ed128b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -13,7 +13,6 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { @@ -85,10 +84,11 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { samples[j] = new PebbleHealthActivitySample( null, stepsRecord.timestamp, + userId, deviceId, + null, // raw data here stepsRecord.intensity, - stepsRecord.steps, - sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), - userId, deviceId); + stepsRecord.steps + ); samples[j].setProvider(sampleProvider); } From 68b303246dad0da6db7970cfdf1852305b77b7d2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 10 Aug 2016 23:26:25 +0200 Subject: [PATCH 164/569] db refactoring: change column order for health overlay table also --- .../freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java | 7 ++++--- .../devices/pebble/DatalogSessionHealthOverlayData.java | 2 +- .../service/devices/pebble/DatalogSessionHealthSleep.java | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 01091eb7..fb3508b7 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -158,15 +158,16 @@ public class GBDaoGenerator { private static Entity addPebbleHealthActivityKindOverlay(Schema schema, Entity user, Entity device) { Entity activityOverlay = addEntity(schema, "PebbleHealthActivityOverlay"); activityOverlay.addIdProperty(); - Property timestampFrom = activityOverlay.addIntProperty("timestampFrom").notNull().getProperty(); - Property timestampTo = activityOverlay.addIntProperty("timestampTo").notNull().getProperty(); - activityOverlay.addIntProperty("rawKind").notNull(); Property userId = activityOverlay.addLongProperty("userId").getProperty(); activityOverlay.addToOne(user, userId); Property deviceId = activityOverlay.addLongProperty("deviceId").getProperty(); activityOverlay.addToOne(device, deviceId); + Property timestampFrom = activityOverlay.addIntProperty("timestampFrom").notNull().getProperty(); + Property timestampTo = activityOverlay.addIntProperty("timestampTo").notNull().getProperty(); + activityOverlay.addIntProperty("rawKind").notNull(); + Index indexUnique = new Index(); indexUnique.addProperty(deviceId); indexUnique.addProperty(timestampFrom); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index 677060a5..83b8c916 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -72,7 +72,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (OverlayRecord overlayRecord : overlayRecords) { - overlayList.add(new PebbleHealthActivityOverlay(null, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type, userId, deviceId)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(null, userId, deviceId, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 762ba706..bd1c15af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -71,7 +71,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (SleepRecord sleepRecord : sleepRecords) { - overlayList.add(new PebbleHealthActivityOverlay(null, sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, sleepRecord.type, userId, deviceId)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(null, userId, deviceId, sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, sleepRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { From 4280e9612d3d1eda7882b7e79f7fa8a4dee575e7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 11 Aug 2016 21:42:25 +0200 Subject: [PATCH 165/569] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bb3b138d..2cd4ac1d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +***THE ISSUE TRACKER AND WIKI HAVE BEEN DISABLED TILL THE NEXT RELEASE*** + Gadgetbridge ============ From f6629ad8e470a721262ba473e85d3b2a9fd70c12 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 12 Aug 2016 23:06:26 +0200 Subject: [PATCH 166/569] add TODO.md for 0.12.0 release Oh sweet silence on the issue tracker --- TODO.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..753066bc --- /dev/null +++ b/TODO.md @@ -0,0 +1,6 @@ +TODO before 0.12.0 release: + +* Patch GreenDAO to support composite primary keys +* Support importing Pebble Health data from old database +* Fix user attribute table being spammed + From 65d973401a7f04ce89af6d1284e9d3e0ebdb263f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 13 Aug 2016 00:17:36 +0200 Subject: [PATCH 167/569] Updated todo-list --- TODO.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 753066bc..8157f23a 100644 --- a/TODO.md +++ b/TODO.md @@ -3,4 +3,6 @@ TODO before 0.12.0 release: * Patch GreenDAO to support composite primary keys * Support importing Pebble Health data from old database * Fix user attribute table being spammed - +* Add back UUID_CHARACTERISTIC_PAIR support, at least optionally +* CSV Export +* Imoroved from device to device coordinator + support class From b3984a409c081421fdedb270039f0f3d736ba1f9 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 13 Aug 2016 00:27:38 +0200 Subject: [PATCH 168/569] Fix checking for up-to-date User and Device attributes --- .../freeyourgadget/gadgetbridge/database/DBHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index a89f0fa5..4bc649f9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -196,7 +196,7 @@ public class DBHelper { private static boolean hasUpToDateUserAttributes(List userAttributes, ActivityUser prefsUser) { for (UserAttributes attr : userAttributes) { if (!isValidNow(attr)) { - return false; + continue; } if (isEqual(attr, prefsUser)) { return true; @@ -311,7 +311,7 @@ public class DBHelper { private static boolean hasUpToDateDeviceAttributes(List deviceAttributes, GBDevice gbDevice) { for (DeviceAttributes attr : deviceAttributes) { if (!isValidNow(attr)) { - return false; + continue; } if (isEqual(attr, gbDevice)) { return true; From 7c060506cf4a59b7098d7b1402ca8fe4f261b7ac Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 13 Aug 2016 00:52:35 +0200 Subject: [PATCH 169/569] Invalidate UserAttributes and DeviceAttributes when new ones are created --- .../gadgetbridge/database/DBHelper.java | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 4bc649f9..4f3ddec3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -180,11 +180,16 @@ public class DBHelper { private static void ensureUserAttributes(User user, ActivityUser prefsUser, DaoSession session) { List userAttributes = user.getUserAttributesList(); - if (hasUpToDateUserAttributes(userAttributes, prefsUser)) { + UserAttributes[] previousUserAttributes = new UserAttributes[1]; + if (hasUpToDateUserAttributes(userAttributes, prefsUser, previousUserAttributes)) { return; } + + Calendar now = DateTimeUtils.getCalendarUTC(); + invalidateUserAttributes(previousUserAttributes[0], now, session); + UserAttributes attributes = new UserAttributes(); - attributes.setValidFromUTC(DateTimeUtils.todayUTC()); + attributes.setValidFromUTC(now.getTime()); attributes.setHeightCM(prefsUser.getHeightCm()); attributes.setWeightKG(prefsUser.getWeightKg()); attributes.setUserId(user.getId()); @@ -193,13 +198,24 @@ public class DBHelper { userAttributes.add(attributes); } - private static boolean hasUpToDateUserAttributes(List userAttributes, ActivityUser prefsUser) { + private static void invalidateUserAttributes(UserAttributes userAttributes, Calendar now, DaoSession session) { + if (userAttributes != null) { + Calendar invalid = (Calendar) now.clone(); + invalid.add(Calendar.MINUTE, -1); + userAttributes.setValidToUTC(invalid.getTime()); + session.update(userAttributes); + } + } + + private static boolean hasUpToDateUserAttributes(List userAttributes, ActivityUser prefsUser, UserAttributes[] outPreviousUserAttributes) { for (UserAttributes attr : userAttributes) { if (!isValidNow(attr)) { continue; } if (isEqual(attr, prefsUser)) { return true; + } else { + outPreviousUserAttributes[0] = attr; } } return false; @@ -293,13 +309,17 @@ public class DBHelper { private static void ensureDeviceAttributes(Device device, GBDevice gbDevice, DaoSession session) { List deviceAttributes = device.getDeviceAttributesList(); - if (hasUpToDateDeviceAttributes(deviceAttributes, gbDevice)) { + DeviceAttributes[] previousDeviceAttributes = new DeviceAttributes[1]; + if (hasUpToDateDeviceAttributes(deviceAttributes, gbDevice, previousDeviceAttributes)) { return; } - DeviceAttributes attributes = new DeviceAttributes(); + Calendar now = DateTimeUtils.getCalendarUTC(); + invalidateDeviceAttributes(previousDeviceAttributes[0], now, session); + + DeviceAttributes attributes = new DeviceAttributes(); attributes.setDeviceId(device.getId()); - attributes.setValidFromUTC(DateTimeUtils.todayUTC()); + attributes.setValidFromUTC(now.getTime()); attributes.setFirmwareVersion1(gbDevice.getFirmwareVersion()); attributes.setFirmwareVersion2(gbDevice.getFirmwareVersion2()); DeviceAttributesDao attributesDao = session.getDeviceAttributesDao(); @@ -308,13 +328,24 @@ public class DBHelper { deviceAttributes.add(attributes); } - private static boolean hasUpToDateDeviceAttributes(List deviceAttributes, GBDevice gbDevice) { + private static void invalidateDeviceAttributes(DeviceAttributes deviceAttributes, Calendar now, DaoSession session) { + if (deviceAttributes != null) { + Calendar invalid = (Calendar) now.clone(); + invalid.add(Calendar.MINUTE, -1); + deviceAttributes.setValidToUTC(invalid.getTime()); + session.update(deviceAttributes); + } + } + + private static boolean hasUpToDateDeviceAttributes(List deviceAttributes, GBDevice gbDevice, DeviceAttributes[] outPreviousAttributes) { for (DeviceAttributes attr : deviceAttributes) { if (!isValidNow(attr)) { continue; } if (isEqual(attr, gbDevice)) { return true; + } else { + outPreviousAttributes[0] = attr; } } return false; From b9df746ea6ac36ea33e3df82f03dce954dc5de66 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 13 Aug 2016 00:58:12 +0200 Subject: [PATCH 170/569] Add the missing word :) --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 8157f23a..7b32f769 100644 --- a/TODO.md +++ b/TODO.md @@ -5,4 +5,4 @@ TODO before 0.12.0 release: * Fix user attribute table being spammed * Add back UUID_CHARACTERISTIC_PAIR support, at least optionally * CSV Export -* Imoroved from device to device coordinator + support class +* Imorove mapping from device to device coordinator + support class From eb962c65f0a9b8450297f6aebdc91d3b72a0a50b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 13 Aug 2016 01:24:43 +0200 Subject: [PATCH 171/569] work towards composite keys (builds but does not run because greendao generates wrong table creation code - we need to fix that) --- .../gadgetbridge/daogen/GBDaoGenerator.java | 12 ++---------- .../miband/operations/FetchActivityOperation.java | 1 - .../devices/pebble/AppMessageHandlerMisfit.java | 2 +- .../devices/pebble/AppMessageHandlerMorpheuz.java | 2 +- .../pebble/DatalogSessionHealthOverlayData.java | 2 +- .../devices/pebble/DatalogSessionHealthSleep.java | 2 +- .../devices/pebble/DatalogSessionHealthSteps.java | 1 - 7 files changed, 6 insertions(+), 16 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index fb3508b7..05f6f387 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -157,7 +157,6 @@ public class GBDaoGenerator { private static Entity addPebbleHealthActivityKindOverlay(Schema schema, Entity user, Entity device) { Entity activityOverlay = addEntity(schema, "PebbleHealthActivityOverlay"); - activityOverlay.addIdProperty(); Property userId = activityOverlay.addLongProperty("userId").getProperty(); activityOverlay.addToOne(user, userId); @@ -201,21 +200,14 @@ public class GBDaoGenerator { "This class represents a sample specific to the device. Values like activity kind or\n" + "intensity, are device specific. Normalized values can be retrieved through the\n" + "corresponding {@link SampleProvider}."); - activitySample.addIdProperty(); - activitySample.addIntProperty("timestamp").notNull(); + activitySample.addIntProperty("timestamp").notNull().primaryKey(); } private static void addCommonActivitySampleProperties2(Entity activitySample, Entity user, Entity device) { Property userId = activitySample.addLongProperty("userId").getProperty(); activitySample.addToOne(user, userId); - Property deviceId = activitySample.addLongProperty("deviceId").getProperty(); + Property deviceId = activitySample.addLongProperty("deviceId").primaryKey().getProperty(); activitySample.addToOne(device, deviceId); - - Index indexUnique = new Index(); - indexUnique.addProperty(findProperty(activitySample, "timestamp")); - indexUnique.addProperty(deviceId); - indexUnique.makeUnique(); - activitySample.addIndex(indexUnique); } private static Property findProperty(Entity entity, String propertyName) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index 4814f97f..d058ae89 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -328,7 +328,6 @@ public class FetchActivityOperation extends AbstractMiBandOperation { } samples[minutes] = new MiBandActivitySample( - null, timestampInSeconds, intensity & 0xff, steps & 0xff, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 42acbcaf..02183fb8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -83,7 +83,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); for (int i = 0; i < samples; i++) { short sample = buf.getShort(); - misfitSamples[i] = new PebbleMisfitSample(null, timestamp + i * 60, userId, deviceId, sample & 0xffff); + misfitSamples[i] = new PebbleMisfitSample(timestamp + i * 60, userId, deviceId, sample & 0xffff); misfitSamples[i].setProvider(sampleProvider); int steps = misfitSamples[i].getSteps(); totalSteps += steps; 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 9b16cde9..d6e01890 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 @@ -114,7 +114,7 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { 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(null, recording_base_timestamp + index * 600, userId, deviceId, intensity); + PebbleMorpheuzSample sample = new PebbleMorpheuzSample(recording_base_timestamp + index * 600, userId, deviceId, intensity); sample.setProvider(sampleProvider); sampleProvider.addGBActivitySample(sample); } catch (Exception e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index 83b8c916..31f032c2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -72,7 +72,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (OverlayRecord overlayRecord : overlayRecords) { - overlayList.add(new PebbleHealthActivityOverlay(null, userId, deviceId, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(userId, deviceId, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index bd1c15af..8c8e475a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -71,7 +71,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (SleepRecord sleepRecord : sleepRecords) { - overlayList.add(new PebbleHealthActivityOverlay(null, userId, deviceId, sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, sleepRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(userId, deviceId, sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, sleepRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index 0ed128b8..f6b661c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -82,7 +82,6 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { for (int j = 0; j < stepsRecords.length; j++) { StepsRecord stepsRecord = stepsRecords[j]; samples[j] = new PebbleHealthActivitySample( - null, stepsRecord.timestamp, userId, deviceId, null, // raw data here From 69933c5e92f876ea0c378f0365c5c795e1c43231 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 14 Aug 2016 22:33:41 +0200 Subject: [PATCH 172/569] db refactoring: depend on latest -fyg patched greendao, change column order again (primary keys first), remove index from pebble health overlay also --- GBDaoGenerator/build.gradle | 3 +- .../gadgetbridge/daogen/GBDaoGenerator.java | 30 +++++-------------- app/build.gradle | 4 ++- .../operations/FetchActivityOperation.java | 4 +-- .../DatalogSessionHealthOverlayData.java | 2 +- .../pebble/DatalogSessionHealthSleep.java | 2 +- 6 files changed, 17 insertions(+), 28 deletions(-) diff --git a/GBDaoGenerator/build.gradle b/GBDaoGenerator/build.gradle index e95e421a..756314f8 100644 --- a/GBDaoGenerator/build.gradle +++ b/GBDaoGenerator/build.gradle @@ -7,7 +7,8 @@ archivesBaseName = 'gadgetbridge-daogenerator' dependencies { // compile 'org.greenrobot:greendao-generator:2.2.0' - compile 'com.github.freeyourgadget:greendao:c3830951e5dd3d1e63d7bac600d5f773b81df363' +// compile project(":DaoGenerator") + compile 'com.github.freeyourgadget:greendao:1998d7cd2d21f662c6044f6ccf3b3a251bbad341' } sourceSets { diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 05f6f387..313d9a47 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -34,7 +34,7 @@ public class GBDaoGenerator { private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate"; public static void main(String[] args) throws Exception { - Schema schema = new Schema(12, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(13, MAIN_PACKAGE + ".entities"); addActivityDescription(schema); @@ -136,7 +136,6 @@ public class GBDaoGenerator { activitySample.addIntProperty("rawIntensity").notNull(); activitySample.addIntProperty("steps").notNull(); activitySample.addIntProperty("rawKind").notNull(); - addCommonActivitySampleProperties2(activitySample, user, device); addHeartRateProperties(activitySample); return activitySample; } @@ -148,7 +147,6 @@ public class GBDaoGenerator { private static Entity addPebbleHealthActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "PebbleHealthActivitySample"); addCommonActivitySampleProperties("AbstractPebbleHealthActivitySample", activitySample, user, device); - addCommonActivitySampleProperties2(activitySample, user, device); activitySample.addByteArrayProperty("rawPebbleHealthData"); activitySample.addIntProperty("rawIntensity").notNull(); activitySample.addIntProperty("steps").notNull(); @@ -158,29 +156,21 @@ public class GBDaoGenerator { private static Entity addPebbleHealthActivityKindOverlay(Schema schema, Entity user, Entity device) { Entity activityOverlay = addEntity(schema, "PebbleHealthActivityOverlay"); - Property userId = activityOverlay.addLongProperty("userId").getProperty(); - activityOverlay.addToOne(user, userId); - Property deviceId = activityOverlay.addLongProperty("deviceId").getProperty(); + activityOverlay.addIntProperty("timestampFrom").notNull().primaryKey(); + activityOverlay.addIntProperty("timestampTo").notNull().primaryKey(); + Property deviceId = activityOverlay.addLongProperty("deviceId").primaryKey().getProperty(); activityOverlay.addToOne(device, deviceId); - Property timestampFrom = activityOverlay.addIntProperty("timestampFrom").notNull().getProperty(); - Property timestampTo = activityOverlay.addIntProperty("timestampTo").notNull().getProperty(); + Property userId = activityOverlay.addLongProperty("userId").getProperty(); + activityOverlay.addToOne(user, userId); activityOverlay.addIntProperty("rawKind").notNull(); - Index indexUnique = new Index(); - indexUnique.addProperty(deviceId); - indexUnique.addProperty(timestampFrom); - indexUnique.addProperty(timestampTo); - indexUnique.makeUnique(); - activityOverlay.addIndex(indexUnique); - return activityOverlay; } private static Entity addPebbleMisfitActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "PebbleMisfitSample"); addCommonActivitySampleProperties("AbstractPebbleMisfitActivitySample", activitySample, user, device); - addCommonActivitySampleProperties2(activitySample, user, device); activitySample.addIntProperty("rawPebbleMisfitSample").notNull(); return activitySample; } @@ -188,7 +178,6 @@ public class GBDaoGenerator { private static Entity addPebbleMorpheuzActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "PebbleMorpheuzSample"); addCommonActivitySampleProperties("AbstractPebbleMorpheuzActivitySample", activitySample, user, device); - addCommonActivitySampleProperties2(activitySample, user, device); activitySample.addIntProperty("rawIntensity").notNull(); return activitySample; } @@ -201,13 +190,10 @@ public class GBDaoGenerator { "intensity, are device specific. Normalized values can be retrieved through the\n" + "corresponding {@link SampleProvider}."); activitySample.addIntProperty("timestamp").notNull().primaryKey(); - } - - private static void addCommonActivitySampleProperties2(Entity activitySample, Entity user, Entity device) { - Property userId = activitySample.addLongProperty("userId").getProperty(); - activitySample.addToOne(user, userId); Property deviceId = activitySample.addLongProperty("deviceId").primaryKey().getProperty(); activitySample.addToOne(device, deviceId); + Property userId = activitySample.addLongProperty("userId").getProperty(); + activitySample.addToOne(user, userId); } private static Property findProperty(Entity entity, String propertyName) { diff --git a/app/build.gradle b/app/build.gradle index b9e2ea4a..65373c94 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -59,8 +59,10 @@ dependencies { compile 'com.github.pfichtner:durationformatter:0.1.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.2' compile 'net.e175.klaus:solarpositioning:0.0.9' - compile 'com.github.freeyourgadget:greendao:c3830951e5dd3d1e63d7bac600d5f773b81df363' + compile 'com.github.freeyourgadget:greendao:1998d7cd2d21f662c6044f6ccf3b3a251bbad341' compile 'com.github.woxthebox:draglistview:1.2.6' + +// compile project(":DaoCore") } preBuild.dependsOn(":GBDaoGenerator:genSources") diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index d058ae89..9fba71ff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -329,11 +329,11 @@ public class FetchActivityOperation extends AbstractMiBandOperation { samples[minutes] = new MiBandActivitySample( timestampInSeconds, + deviceId, + userId, intensity & 0xff, steps & 0xff, category & 0xff, - userId, - deviceId, heartrate & 0xff); samples[minutes].setProvider(provider); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index 31f032c2..e693895b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -72,7 +72,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (OverlayRecord overlayRecord : overlayRecords) { - overlayList.add(new PebbleHealthActivityOverlay(userId, deviceId, overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, overlayRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, deviceId, userId, overlayRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 8c8e475a..e57795b0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -71,7 +71,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (SleepRecord sleepRecord : sleepRecords) { - overlayList.add(new PebbleHealthActivityOverlay(userId, deviceId, sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, sleepRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, deviceId, userId, sleepRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { From 4ddbbfdfb02da08b1303c5d0daefaf454ee6be93 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 14 Aug 2016 22:36:50 +0200 Subject: [PATCH 173/569] change db name to test-db5 --- .../nodomain/freeyourgadget/gadgetbridge/GBApplication.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 8f756b4b..6ecca503 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -146,8 +146,7 @@ public class GBApplication extends Application { } static void setupDatabase(Context context) { -// DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "test-db", null); - DBOpenHelper helper = new DBOpenHelper(context, "test-db4", null); + DBOpenHelper helper = new DBOpenHelper(context, "test-db5", null); SQLiteDatabase db = helper.getWritableDatabase(); DaoMaster daoMaster = new DaoMaster(db); if (lockHandler == null) { From a38bea892a2d7f00378f3ccc244088770d6d46e6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 14 Aug 2016 23:21:09 +0200 Subject: [PATCH 174/569] Some logging of found devices/uuids --- .../activities/DiscoveryActivity.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 12a7f9e3..b3239153 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -13,6 +13,7 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.ParcelUuid; import android.os.Parcelable; import android.support.v4.app.ActivityCompat; import android.view.View; @@ -85,10 +86,20 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC private final BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { + LOG.warn(device.getName() + ": " + ((scanRecord != null) ? scanRecord.length : -1)); + logMessageContent(scanRecord); handleDeviceFound(device, (short) rssi); } }; + public void logMessageContent(byte[] value) { + if (value != null) { + for (byte b : value) { + LOG.warn("DATA: " + String.format("0x%2x", b) + " - " + (char) (b & 0xff)); + } + } + } + private final Runnable stopRunnable = new Runnable() { @Override public void run() { @@ -180,6 +191,14 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC private void handleDeviceFound(BluetoothDevice device, short rssi) { LOG.debug("found device: " + device.getName() + ", " + device.getAddress()); + if (LOG.isDebugEnabled()) { + ParcelUuid[] uuids = device.getUuids(); + if (uuids != null && uuids.length > 0) { + for (ParcelUuid uuid : uuids) { + LOG.debug(" supports uuid: " + uuid.toString()); + } + } + } if (device.getBondState() == BluetoothDevice.BOND_BONDED) { return; // ignore already bonded devices } From 8766fc5269b4aebd2bace55cb98160a9f94fbc3f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 14 Aug 2016 23:24:24 +0200 Subject: [PATCH 175/569] Handle MiBand2 device type --- .../freeyourgadget/gadgetbridge/model/DeviceType.java | 3 ++- .../gadgetbridge/service/DeviceSupportFactory.java | 4 ++++ 2 files changed, 6 insertions(+), 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 e7b357af..f76a4766 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -7,5 +7,6 @@ public enum DeviceType { UNKNOWN, PEBBLE, TEST, - MIBAND + MIBAND, + MIBAND2 } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index fceac174..0658b729 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -10,6 +10,7 @@ import java.util.EnumSet; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -84,6 +85,9 @@ public class DeviceSupportFactory { case MIBAND: deviceSupport = new ServiceDeviceSupport(new MiBandSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case MIBAND2: + deviceSupport = new ServiceDeviceSupport(new MiBand2Support(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); From 6843b5aa8fe10bb5acac79f794da537dbab6b520 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 15 Aug 2016 00:39:31 +0200 Subject: [PATCH 176/569] Add icon for Mi Band 2 --- .../gadgetbridge/adapter/DeviceCandidateAdapter.java | 1 + .../freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java | 1 + 2 files changed, 2 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java index 48252eb8..a80e3fac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java @@ -51,6 +51,7 @@ public class DeviceCandidateAdapter extends ArrayAdapter { deviceImageView.setImageResource(R.drawable.ic_device_pebble); break; case MIBAND: + case MIBAND2: deviceImageView.setImageResource(R.drawable.ic_device_miband); break; default: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java index 92ca09b5..a9ded158 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java @@ -120,6 +120,7 @@ public class GBDeviceAdapter extends ArrayAdapter { } break; case MIBAND: + case MIBAND2: if (device.isConnected()) { deviceImageView.setImageResource(R.drawable.ic_device_miband); } else { From 9b7e8e06d6d9b0740f8b99b58040147081a042fd Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 15 Aug 2016 00:40:35 +0200 Subject: [PATCH 177/569] Improved time conversion (0x2A0F) - support for org.bluetooth.characteristic.local_time_information - support for day of week in 0x2A2B Unfortunately Mi Band 2 does not support 0x2A0F ;( --- .../service/btle/BLETypeConversions.java | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index 9119b54c..5e1914bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.TimeZone; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; @@ -32,6 +33,9 @@ public class BLETypeConversions { } } + // MiBand2: + // year,year,month,dayofmonth,hour,minute,second,dayofweek,0,0,tz + byte[] year = fromUint16(timestamp.get(Calendar.YEAR)); return new byte[] { year[0], @@ -40,10 +44,22 @@ public class BLETypeConversions { fromUint8(timestamp.get(Calendar.DATE)), fromUint8(timestamp.get(Calendar.HOUR_OF_DAY)), fromUint8(timestamp.get(Calendar.MINUTE)), - fromUint8(timestamp.get(Calendar.SECOND)) + fromUint8(timestamp.get(Calendar.SECOND)), + dayOfWeekToRawBytes(timestamp), + 0 // fractions256 (not set) }; } + private static byte dayOfWeekToRawBytes(Calendar cal) { + int calValue = cal.get(Calendar.DAY_OF_WEEK); + switch (calValue) { + case Calendar.SUNDAY: + return 7; + default: + return (byte) (calValue - 1); + } + } + /** * uses the standard algorithm to convert bytes received from the MiBand to a Calendar object * @@ -109,4 +125,47 @@ public class BLETypeConversions { System.arraycopy(end, 0, result, start.length, end.length); return result; } + + public static byte[] calendarToLocalTimeBytes(GregorianCalendar now) { + byte[] result = new byte[2]; + result[0] = mapTimeZone(now.getTimeZone()); + result[1] = mapDstOffset(now); + return result; + } + + /** + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.time_zone.xml + * @param timeZone + * @return sint8 value from -48..+56 + */ + public static byte mapTimeZone(TimeZone timeZone) { + int utcOffsetInMinutes = (timeZone.getRawOffset() / (1000 * 60 * 60)); + return (byte) (utcOffsetInMinutes * 4); + } + + /** + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.dst_offset.xml + * @param Calendar + * @return the DST offset for the given time; 0 if none; 255 if unknown + */ + public static byte mapDstOffset(Calendar now) { + TimeZone timeZone = now.getTimeZone(); + int dstSavings = timeZone.getDSTSavings(); + if (dstSavings == 0) { + return 0; + } + if (timeZone.inDaylightTime(now.getTime())) { + int dstInMinutes = dstSavings / (1000 * 60); + switch (dstInMinutes) { + case 30: + return 2; + case 60: + return 4; + case 120: + return 8; + } + return fromUint8(255); // unknown + } + return 0; + } } From e0c52c7da5e2b4a9d79a3184292c5a53389aa68b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 16 Aug 2016 21:40:18 +0200 Subject: [PATCH 178/569] Update gradle --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 86fd2bad..29aa94b6 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.1.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4e749a60..e94f69bd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 10 15:27:10 PDT 2013 +#Tue Aug 16 21:39:39 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip From 26d490ffd6e31a9e6cd7c0495a90ded8c6419bdb Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 17 Aug 2016 00:34:19 +0200 Subject: [PATCH 179/569] Store the DeviceType in the Device entity (so that we can later recreate a GBDevice from a Device) --- .../gadgetbridge/daogen/GBDaoGenerator.java | 1 + .../gadgetbridge/database/DBHelper.java | 1 + .../gadgetbridge/model/DeviceType.java | 32 ++++++++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index fb3508b7..884aa3d3 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -112,6 +112,7 @@ public class GBDaoGenerator { device.addStringProperty("name").notNull(); device.addStringProperty("manufacturer").notNull(); device.addStringProperty("identifier").notNull().unique().javaDocGetterAndSetter("The fixed identifier, i.e. MAC address of the device."); + device.addIntProperty("type").notNull().javaDocGetterAndSetter("The DeviceType key, i.e. the GBDevice's type."); Property deviceId = deviceAttributes.addLongProperty("deviceId").notNull().getProperty(); // sorted by the from-date, newest first Property deviceAttributesSortProperty = getPropertyByName(deviceAttributes, VALID_FROM_UTC); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 4f3ddec3..23014966 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -302,6 +302,7 @@ public class DBHelper { device.setName(gbDevice.getName()); DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice); device.setManufacturer(coordinator.getManufacturer()); + device.setType(gbDevice.getType().getKey()); session.getDeviceDao().insert(device); return device; 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 f76a4766..caf561ee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -2,11 +2,33 @@ package nodomain.freeyourgadget.gadgetbridge.model; /** * For every supported device, a device type constant must exist. + * + * Note: they key of every constant is stored in the DB, so it is fixed forever, + * and may not be changed. */ public enum DeviceType { - UNKNOWN, - PEBBLE, - TEST, - MIBAND, - MIBAND2 + UNKNOWN(-1), + PEBBLE(1), + MIBAND(10), + MIBAND2(11), + TEST(1000); + + private final int key; + + DeviceType(int key) { + this.key = key; + } + + public int getKey() { + return key; + } + + public static DeviceType fromKey(int key) { + for (DeviceType type : values()) { + if (type.key == key) { + return type; + } + } + return DeviceType.UNKNOWN; + } } From fbf06c1fe36d98f73667f0e361e1398c7d6af5f1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 17 Aug 2016 00:53:16 +0200 Subject: [PATCH 180/569] Separate coordinator and support instances for Mi1 and Mi2 #323 + Some more testing stuff for Mi2 --- .../devices/AbstractDeviceCoordinator.java | 12 ++++++ .../devices/DeviceCoordinator.java | 3 ++ .../devices/UnknownDeviceCoordinator.java | 5 --- .../devices/miband/MiBand2Coordinator.java | 42 ++++++++++++++++++ .../devices/miband/MiBand2Service.java | 13 +++++- .../devices/miband/MiBandConst.java | 1 + .../devices/miband/MiBandCoordinator.java | 8 +--- .../devices/pebble/PebbleCoordinator.java | 5 --- .../service/btle/BLETypeConversions.java | 17 ++++++-- .../deviceinfo/DeviceInfoProfile.java | 12 ++++-- .../devices/miband/MiBand2Support.java | 43 +++++++++++++++++++ .../gadgetbridge/util/DeviceHelper.java | 6 ++- 12 files changed, 142 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index f27a8290..751b2790 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -7,10 +7,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviceCoordinator.class); + + @Override + public boolean supports(GBDevice device) { + return getDeviceType().equals(device.getType()); + } + + @Override + public GBDevice createDevice(GBDeviceCandidate candidate) { + return new GBDevice(candidate.getDevice().getAddress(), candidate.getName(), getDeviceType()); + } + public boolean allowFetchActivityData(GBDevice device) { return device.isInitialized() && !device.isBusy() && supportsActivityDataFetching(); } 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 e8161d06..c0f9d6d6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.app.Activity; +import android.bluetooth.BluetoothDevice; import android.content.Context; import android.net.Uri; @@ -40,6 +41,8 @@ public interface DeviceCoordinator { */ boolean supports(GBDevice device); + GBDevice createDevice(GBDeviceCandidate candidate); + /** * Returns the kind of device type this coordinator supports. * 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 764d3eb5..9116784f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -76,11 +76,6 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { return false; } - @Override - public boolean supports(GBDevice device) { - return getDeviceType().equals(device.getType()); - } - @Override public DeviceType getDeviceType() { return DeviceType.UNKNOWN; 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 new file mode 100644 index 00000000..fb680b3b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -0,0 +1,42 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.miband; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.net.Uri; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class MiBand2Coordinator extends MiBandCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(MiBand2Coordinator.class); + + @Override + public DeviceType getDeviceType() { + return DeviceType.MIBAND2; + } + + @Override + public boolean supports(GBDeviceCandidate candidate) { + // and a heuristic + try { + BluetoothDevice device = candidate.getDevice(); + if (isHealthWearable(device)) { + String name = device.getName(); + return name != null && name.equalsIgnoreCase(MiBandConst.MI_BAND2_NAME); + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return false; + + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; // not supported at the moment + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index f73f6ad0..ddaab80e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -13,8 +13,19 @@ public class MiBand2Service { public static final UUID UUID_SERVICE_MIBAND2_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE1")); public static final UUID UUID_SERVICE_HEART_RATE = UUID.fromString(String.format(BASE_UUID, "180D")); public static final UUID UUID_SERVICE_WEIGHT_SERVICE = UUID.fromString("00001530-0000-3512-2118-0009af100700"); - public static final UUID UUID_UNKNOQN_CHARACTERISTIC0 = UUID.fromString("00000000-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC0 = UUID.fromString("00000000-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC1 = UUID.fromString("00000001-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC2 = UUID.fromString("00000002-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC3 = UUID.fromString("00000003-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC4 = UUID.fromString("00000004-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC5 = UUID.fromString("00000005-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC6 = UUID.fromString("00000006-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC7 = UUID.fromString("00000007-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC8 = UUID.fromString("00000008-0000-3512-2118-0009af100700"); + public static final UUID UUID_UNKNOWN_CHARACTERISTIC10 = UUID.fromString("00000010-0000-3512-2118-0009af100700"); + // set metric distance + // set 12 hour time mode // public static final UUID UUID_CHARACTERISTIC_DEVICE_INFO = UUID.fromString(String.format(BASE_UUID, "FF01")); 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 8dca9b5f..267d9d66 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 @@ -25,6 +25,7 @@ public final class MiBandConst { public static final String ORIGIN_PEBBLEMSG = "pebblemsg"; public static final String ORIGIN_GENERIC = "generic"; public static final String MI_GENERAL_NAME_PREFIX = "MI"; + public static final String MI_BAND2_NAME = "MI Band 2"; public static final String MI_1 = "1"; public static final String MI_1A = "1A"; public static final String MI_1S = "1S"; 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 910c80f5..66780778 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 @@ -35,7 +35,8 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { || macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1S)) { return true; } - if (candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND_SERVICE)) { + if (candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND_SERVICE) + && !candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND2_SERVICE)) { return true; } // and a heuristic @@ -51,11 +52,6 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return false; } - @Override - public boolean supports(GBDevice device) { - return getDeviceType().equals(device.getType()); - } - @Override public DeviceType getDeviceType() { return DeviceType.MIBAND; 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 9b9c6eb2..35054631 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 @@ -27,11 +27,6 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { return name != null && name.startsWith("Pebble"); } - @Override - public boolean supports(GBDevice device) { - return getDeviceType().equals(device.getType()); - } - @Override public DeviceType getDeviceType() { return DeviceType.PEBBLE; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index 5e1914bd..ad36def4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -46,10 +46,21 @@ public class BLETypeConversions { fromUint8(timestamp.get(Calendar.MINUTE)), fromUint8(timestamp.get(Calendar.SECOND)), dayOfWeekToRawBytes(timestamp), - 0 // fractions256 (not set) + 0, // fractions256 (not set) + // 0 (DST offset?) Mi2 + // k (tz) Mi2 }; } + private static int getMiBand2TimeZone(int rawOffset) { + int offsetMinutes = rawOffset / 1000 / 60; + rawOffset = offsetMinutes < 0 ? -1 : 1; + offsetMinutes = Math.abs(offsetMinutes); + int offsetHours = offsetMinutes / 60; + rawOffset *= offsetMinutes % 60 / 15 + offsetHours * 4; + return rawOffset; + } + private static byte dayOfWeekToRawBytes(Calendar cal) { int calValue = cal.get(Calendar.DAY_OF_WEEK); switch (calValue) { @@ -139,8 +150,8 @@ public class BLETypeConversions { * @return sint8 value from -48..+56 */ public static byte mapTimeZone(TimeZone timeZone) { - int utcOffsetInMinutes = (timeZone.getRawOffset() / (1000 * 60 * 60)); - return (byte) (utcOffsetInMinutes * 4); + int utcOffsetInHours = (timeZone.getRawOffset() / (1000 * 60 * 60)); + return (byte) (utcOffsetInHours * 4); } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java index e7e56ea6..b312249f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java @@ -142,9 +142,15 @@ public class DeviceInfoProfile extends Abst } private void handlePnpId(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String pnpId = characteristic.getStringValue(0); - deviceInfo.setPnpId(pnpId); - notify(createIntent(deviceInfo)); + byte[] value = characteristic.getValue(); + if (value.length == 7) { +// int vendorSource +// +// deviceInfo.setPnpId(pnpId); + notify(createIntent(deviceInfo)); + } else { + // TODO: LOG warning + } } private Intent createIntent(DeviceInfo deviceInfo) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java index d921ee5c..eb21aad6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBand2Support.java @@ -26,6 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter; @@ -45,6 +46,7 @@ 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.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; @@ -147,6 +149,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // this is apparently not needed anymore, and actually causes problems when bonding is not used/does not work // so we simply not use the UUID_PAIR characteristic. // .pair(builder) + .testInit(builder) .requestDeviceInfo(builder) .requestBatteryInfo(builder); // .sendUserInfo(builder) @@ -162,6 +165,38 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return builder; } + private MiBand2Support testInit(TransactionBuilder builder) { + builder.read(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC6)); // example read value: 0f6200e0070804072b2c20e00708040625372064 + builder.read(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC7)); // example read value: 0019000000 + setCurrentTimeWithService(builder); + builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4), new byte[] { 0x01, 0x01, (byte) 0xe0, 0x07, 0x07, 0x17, 0x15, 0x04, 0x00, 0x04 }); + builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4), new byte[] { 0x02 }); + builder.read(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC6)); // probably superfluous + builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC8), new byte[] { 0x20, 0x00, 0x00, 0x02 }); + + return this; + } + +// private MiBand2Support maybeAuth(TransactionBuilder builder) { +// builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0), new byte[] {0x20, 0x00}); +// builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0), new byte[] {0x03,0x00,(byte)0x8e,(byte)0xce,0x5a,0x09,(byte)0xb3,(byte)0xd8,0x55,0x57,0x10,0x2a,(byte)0xed,0x7d,0x6b,0x78,(byte)0xc5,(byte)0xd2}); +// return this; +// } + + private MiBand2Support setCurrentTimeWithService(TransactionBuilder builder) { + GregorianCalendar now = BLETypeConversions.createCalendar(); + byte[] bytes = BLETypeConversions.calendarToRawBytes(now, true); + byte[] tail = new byte[] { 0, BLETypeConversions.mapTimeZone(now.getTimeZone()) }; // 0 = adjust reason bitflags? or DST offset?? , timezone +// byte[] tail = new byte[] { 0x2 }; // reason + byte[] all = BLETypeConversions.join(bytes, tail); + builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), all); +// byte[] localtime = BLETypeConversions.calendarToLocalTimeBytes(now); +// builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_LOCAL_TIME_INFORMATION), localtime); +// builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), new byte[] {0x2, 0x00}); +// builder.write(getCharacteristic(MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0), new byte[] {0x03,0x00,(byte)0x8e,(byte)0xce,0x5a,0x09,(byte)0xb3,(byte)0xd8,0x55,0x57,0x10,0x2a,(byte)0xed,0x7d,0x6b,0x78,(byte)0xc5,(byte)0xd2}); + return this; + } + private MiBand2Support readDate(TransactionBuilder builder) { // NAVL // builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_DATE_TIME)); @@ -201,6 +236,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // TODO: tear down the notifications on quit private MiBand2Support enableNotifications(TransactionBuilder builder, boolean enable) { builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable); + builder.notify(getCharacteristic(GattService.UUID_SERVICE_CURRENT_TIME), enable); return this; } @@ -844,6 +880,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { handleHeartrate(characteristic.getValue()); return true; +// } else if (MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0.equals(characteristicUUID)) { +// handleUnknownCharacteristic(characteristic.getValue()); +// return true; } else { LOG.info("Unhandled characteristic changed: " + characteristicUUID); logMessageContent(characteristic.getValue()); @@ -851,6 +890,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return false; } + private void handleUnknownCharacteristic(byte[] value) { + + } + @Override public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 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 21addeea..510eaa8d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -17,6 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; @@ -113,11 +114,11 @@ public class DeviceHelper { GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN); if (coordinator != null && coordinator.supports(candidate)) { - return new GBDevice(device.getAddress(), candidate.getName(), coordinator.getDeviceType()); + return coordinator.createDevice(candidate); } for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(candidate)) { - return new GBDevice(device.getAddress(), candidate.getName(), coordinator.getDeviceType()); + coordinator.createDevice(candidate); } } return null; @@ -162,6 +163,7 @@ public class DeviceHelper { private List createCoordinators() { List result = new ArrayList<>(2); + result.add(new MiBand2Coordinator()); // Note: MiBand2 must come before MiBand because detection is hacky, atm result.add(new MiBandCoordinator()); result.add(new PebbleCoordinator()); return result; From 6e98defe942da349e5eda4917ff2f610575e33ac Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 18 Aug 2016 20:29:20 +0200 Subject: [PATCH 181/569] Only import old activity data once per device, not for every provider --- .../freeyourgadget/gadgetbridge/database/DBHelper.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 23014966..9009a32c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -385,8 +385,11 @@ public class DBHelper { try (SQLiteDatabase oldDB = oldDbHandler.getReadableDatabase()) { User user = DBHelper.getUser(session); for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) { - AbstractSampleProvider sampleProvider = (AbstractSampleProvider) coordinator.getSampleProvider(targetDevice, session); - importActivitySamples(oldDB, targetDevice, session, sampleProvider, user); + if (coordinator.supports(targetDevice)) { + AbstractSampleProvider sampleProvider = (AbstractSampleProvider) coordinator.getSampleProvider(targetDevice, session); + importActivitySamples(oldDB, targetDevice, session, sampleProvider, user); + break; + } } } } From deeaa87df7e8f6cd6a3d74fe5aa02a03e0ce895b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 18 Aug 2016 20:38:48 +0200 Subject: [PATCH 182/569] Batch the import of old activity samples to save memory during import --- .../gadgetbridge/database/DBHelper.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 9009a32c..570039bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -398,6 +398,8 @@ public class DBHelper { String order = "timestamp"; final String where = "provider=" + sampleProvider.getID(); + final int BATCH_SIZE = 100000; // 100.000 samples = rougly 20 MB per batch + List newSamples; try (Cursor cursor = fromDb.query(TABLE_GBACTIVITYSAMPLES, null, where, null, null, null, order)) { int colTimeStamp = cursor.getColumnIndex(KEY_TIMESTAMP); int colIntensity = cursor.getColumnIndex(KEY_INTENSITY); @@ -406,7 +408,7 @@ public class DBHelper { int colCustomShort = cursor.getColumnIndex(KEY_CUSTOM_SHORT); Long deviceId = DBHelper.getDevice(targetDevice, targetSession).getId(); Long userId = user.getId(); - List newSamples = new ArrayList<>(cursor.getCount()); + newSamples = new ArrayList<>(Math.min(BATCH_SIZE, cursor.getCount())); while (cursor.moveToNext()) { T newSample = sampleProvider.createActivitySample(); newSample.setProvider(sampleProvider); @@ -422,8 +424,17 @@ public class DBHelper { newSample.setHeartRate(ActivitySample.NOT_MEASURED); } newSamples.add(newSample); + + if ((newSamples.size() % BATCH_SIZE) == 0) { + sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); + targetSession.clear(); + newSamples.clear(); + } + } + // and insert the remaining samples + if (!newSamples.isEmpty()) { + sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); } - sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); } } From 7a168344823856e998d53ff4939512642b11551f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 18 Aug 2016 21:29:26 +0200 Subject: [PATCH 183/569] *return* the created device! --- .../nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 510eaa8d..f28cff46 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -118,7 +118,7 @@ public class DeviceHelper { } for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(candidate)) { - coordinator.createDevice(candidate); + return coordinator.createDevice(candidate); } } return null; From 0126b90f20b1bb438104fe98d33718df4350ab96 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 18 Aug 2016 21:44:06 +0200 Subject: [PATCH 184/569] Store the timestamps as sent by the pebble. Use a strict inequality operator at the end of the period to exclude the last sample. --- .../gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java | 2 +- .../service/devices/pebble/DatalogSessionHealthOverlayData.java | 2 +- .../service/devices/pebble/DatalogSessionHealthSleep.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index 57de0eae..1623679f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -51,7 +51,7 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider overlayList = new ArrayList<>(); for (OverlayRecord overlayRecord : overlayRecords) { - overlayList.add(new PebbleHealthActivityOverlay(overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds - 1, deviceId, userId, overlayRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds, deviceId, userId, overlayRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index e57795b0..e1ff8d9f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -71,7 +71,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (SleepRecord sleepRecord : sleepRecords) { - overlayList.add(new PebbleHealthActivityOverlay(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd - 1, deviceId, userId, sleepRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, deviceId, userId, sleepRecord.type)); //TODO: consider if "-1" is what we really want } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { From 3fb558c53663773bb0e0c8f1f11d15c00f703d08 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 18 Aug 2016 22:06:26 +0200 Subject: [PATCH 185/569] db refactoring: add raw data column in health overlay table and make rawType part of the composite key --- .../freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java | 4 ++-- .../devices/pebble/DatalogSessionHealthOverlayData.java | 2 +- .../service/devices/pebble/DatalogSessionHealthSleep.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 24e2f53b..bc1eeefd 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -17,7 +17,6 @@ package nodomain.freeyourgadget.gadgetbridge.daogen; import de.greenrobot.daogenerator.DaoGenerator; import de.greenrobot.daogenerator.Entity; -import de.greenrobot.daogenerator.Index; import de.greenrobot.daogenerator.Property; import de.greenrobot.daogenerator.Schema; @@ -159,12 +158,13 @@ public class GBDaoGenerator { activityOverlay.addIntProperty("timestampFrom").notNull().primaryKey(); activityOverlay.addIntProperty("timestampTo").notNull().primaryKey(); + activityOverlay.addIntProperty("rawKind").notNull().primaryKey(); Property deviceId = activityOverlay.addLongProperty("deviceId").primaryKey().getProperty(); activityOverlay.addToOne(device, deviceId); Property userId = activityOverlay.addLongProperty("userId").getProperty(); activityOverlay.addToOne(user, userId); - activityOverlay.addIntProperty("rawKind").notNull(); + activityOverlay.addByteArrayProperty("rawPebbleHealthData"); return activityOverlay; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index 0c696335..db7c21fe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -72,7 +72,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (OverlayRecord overlayRecord : overlayRecords) { - overlayList.add(new PebbleHealthActivityOverlay(overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds, deviceId, userId, overlayRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds, overlayRecord.type, deviceId, userId, null)); } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index e1ff8d9f..5115be5b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -71,7 +71,7 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (SleepRecord sleepRecord : sleepRecords) { - overlayList.add(new PebbleHealthActivityOverlay(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, deviceId, userId, sleepRecord.type)); //TODO: consider if "-1" is what we really want + overlayList.add(new PebbleHealthActivityOverlay(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sleepRecord.type, deviceId, userId, null)); } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { From 6119f3501a478ae7ea1409337f1582c06eb993dd Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 19 Aug 2016 21:09:32 +0200 Subject: [PATCH 186/569] Import the old samples and map the ranges to overlays for pebble. --- TODO.md | 2 +- .../gadgetbridge/database/DBHelper.java | 62 +++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index 7b32f769..4f6b31ee 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,7 @@ TODO before 0.12.0 release: * Patch GreenDAO to support composite primary keys -* Support importing Pebble Health data from old database +* ~~Support importing Pebble Health data from old database~~ DONE, needs check. * Fix user attribute table being spammed * Add back UUID_CHARACTERISTIC_PAIR support, at least optionally * CSV Export diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 570039bf..98533403 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -19,16 +19,20 @@ import de.greenrobot.dao.query.Query; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributesDao; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceDao; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlay; +import nodomain.freeyourgadget.gadgetbridge.entities.PebbleHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.entities.UserDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate; @@ -46,7 +50,7 @@ import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GB /** * Provides utiliy access to some common entities, so you won't need to use * their DAO classes. - * + *

* Maybe this code should actually be in the DAO classes themselves, but then * these should be under revision control instead of 100% generated at build time. */ @@ -61,6 +65,7 @@ public class DBHelper { * Closes the database and returns its name. * Important: after calling this, you have to DBHandler#openDb() it again * to get it back to work. + * * @param dbHandler * @return * @throws IllegalStateException @@ -287,7 +292,7 @@ public class DBHelper { } public static Device getDevice(GBDevice gbDevice, DaoSession session) { - Device device = findDevice(gbDevice, session); + Device device = findDevice(gbDevice, session); if (device == null) { device = createDevice(session, gbDevice); } @@ -355,6 +360,7 @@ public class DBHelper { /** * Returns the old activity database handler if there is any content in that * db, or null otherwise. + * * @return the old activity db handler or null */ @Nullable @@ -398,8 +404,21 @@ public class DBHelper { String order = "timestamp"; final String where = "provider=" + sampleProvider.getID(); + boolean convertActivityTypeToRange = false; + int currentTypeRun, previousTypeRun, currentTimeStamp, currentTypeStartTimeStamp, currentTypeEndTimeStamp; + List overlayList = new ArrayList<>(); + final int BATCH_SIZE = 100000; // 100.000 samples = rougly 20 MB per batch List newSamples; + if (sampleProvider instanceof PebbleHealthSampleProvider) { + convertActivityTypeToRange = true; + previousTypeRun = ActivitySample.NOT_MEASURED; + currentTypeStartTimeStamp = -1; + currentTypeEndTimeStamp = -1; + + } else { + previousTypeRun = currentTypeStartTimeStamp = currentTypeEndTimeStamp = 0; + } try (Cursor cursor = fromDb.query(TABLE_GBACTIVITYSAMPLES, null, where, null, null, null, order)) { int colTimeStamp = cursor.getColumnIndex(KEY_TIMESTAMP); int colIntensity = cursor.getColumnIndex(KEY_INTENSITY); @@ -414,9 +433,33 @@ public class DBHelper { newSample.setProvider(sampleProvider); newSample.setUserId(userId); newSample.setDeviceId(deviceId); - newSample.setTimestamp(cursor.getInt(colTimeStamp)); - newSample.setRawKind(getNullableInt(cursor, colType, ActivitySample.NOT_MEASURED)); + currentTimeStamp = cursor.getInt(colTimeStamp); + newSample.setTimestamp(currentTimeStamp); newSample.setRawIntensity(getNullableInt(cursor, colIntensity, ActivitySample.NOT_MEASURED)); + currentTypeRun = getNullableInt(cursor, colType, ActivitySample.NOT_MEASURED); + newSample.setRawKind(currentTypeRun); + if (convertActivityTypeToRange) { + //at the beginning there is no start timestamp + if (currentTypeStartTimeStamp == -1) { + currentTypeStartTimeStamp = currentTypeEndTimeStamp = currentTimeStamp; + previousTypeRun = currentTypeRun; + } + + if (currentTypeRun != previousTypeRun) { + //if the Type has changed, the run has ended. Only store light and deep sleep data + if (previousTypeRun == 4) { + overlayList.add(new PebbleHealthActivityOverlay(currentTypeStartTimeStamp, currentTypeEndTimeStamp, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), deviceId, userId, null)); + } else if (previousTypeRun == 5) { + overlayList.add(new PebbleHealthActivityOverlay(currentTypeStartTimeStamp, currentTypeEndTimeStamp, sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP), deviceId, userId, null)); + } + currentTypeStartTimeStamp = currentTypeEndTimeStamp = currentTimeStamp; + previousTypeRun = currentTypeRun; + } else { + //just expand the run + currentTypeEndTimeStamp = currentTimeStamp; + } + + } newSample.setSteps(getNullableInt(cursor, colSteps, ActivitySample.NOT_MEASURED)); if (colCustomShort > -1) { newSample.setHeartRate(getNullableInt(cursor, colCustomShort, ActivitySample.NOT_MEASURED)); @@ -435,6 +478,17 @@ public class DBHelper { if (!newSamples.isEmpty()) { sampleProvider.getSampleDao().insertOrReplaceInTx(newSamples, true); } + // store the overlay records + if (!overlayList.isEmpty()) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + PebbleHealthActivityOverlayDao overlayDao = session.getPebbleHealthActivityOverlayDao(); + overlayDao.insertOrReplaceInTx(overlayList); + + } catch (Exception ex) { + //FIXME: this whole try catch is probably in the wrong place. + } + } } } From 0ae9955a6f23fb3f8798537a5508ea789e084e09 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 20 Aug 2016 21:38:39 +0200 Subject: [PATCH 187/569] Display Toast when trying to import old Misfit data (unsupported) ... and update TODO --- TODO.md | 5 ++++- .../freeyourgadget/gadgetbridge/database/DBHelper.java | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 4f6b31ee..3be014ed 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,11 @@ TODO before 0.12.0 release: -* Patch GreenDAO to support composite primary keys +* ~~Patch GreenDAO to support composite primary keys~~ * ~~Support importing Pebble Health data from old database~~ DONE, needs check. * Fix user attribute table being spammed + +Non blocking issues: + * Add back UUID_CHARACTERISTIC_PAIR support, at least optionally * CSV Export * Imorove mapping from device to device coordinator + support class diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 98533403..74066868 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -5,6 +5,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.support.annotation.Nullable; +import android.widget.Toast; import java.io.File; import java.io.IOException; @@ -20,6 +21,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleMisfitSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; @@ -39,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_INTENSITY; @@ -401,6 +404,11 @@ public class DBHelper { } private void importActivitySamples(SQLiteDatabase fromDb, GBDevice targetDevice, DaoSession targetSession, AbstractSampleProvider sampleProvider, User user) { + if (sampleProvider instanceof PebbleMisfitSampleProvider) { + GB.toast(context, "Migration of old Misfit data is not supported!", Toast.LENGTH_LONG, GB.WARN); + return; + } + String order = "timestamp"; final String where = "provider=" + sampleProvider.getID(); From 5a3a0495c9d86140473633b8cda34ae568b1c1db Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 21 Aug 2016 17:14:02 +0200 Subject: [PATCH 188/569] TODO: Onboarding activity before new release --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index 3be014ed..8c817aaa 100644 --- a/TODO.md +++ b/TODO.md @@ -3,6 +3,7 @@ TODO before 0.12.0 release: * ~~Patch GreenDAO to support composite primary keys~~ * ~~Support importing Pebble Health data from old database~~ DONE, needs check. * Fix user attribute table being spammed +* Add onboarding activity on first startup (to merge old data) Non blocking issues: From b617ba7264e5cedcaa2259b68ef3f90554628efb Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 21 Aug 2016 17:38:07 +0200 Subject: [PATCH 189/569] Fix the logic regarding the last sample of a run. Remove the unneded try-catch block and reuse the current DB session instead. --- .../gadgetbridge/database/DBHelper.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 74066868..eb9a8069 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -454,13 +454,15 @@ public class DBHelper { } if (currentTypeRun != previousTypeRun) { + //we used not to store the last sample, now we do the opposite and we need to round up + currentTypeEndTimeStamp = currentTimeStamp; //if the Type has changed, the run has ended. Only store light and deep sleep data if (previousTypeRun == 4) { overlayList.add(new PebbleHealthActivityOverlay(currentTypeStartTimeStamp, currentTypeEndTimeStamp, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), deviceId, userId, null)); } else if (previousTypeRun == 5) { overlayList.add(new PebbleHealthActivityOverlay(currentTypeStartTimeStamp, currentTypeEndTimeStamp, sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP), deviceId, userId, null)); } - currentTypeStartTimeStamp = currentTypeEndTimeStamp = currentTimeStamp; + currentTypeStartTimeStamp = currentTimeStamp; previousTypeRun = currentTypeRun; } else { //just expand the run @@ -488,14 +490,8 @@ public class DBHelper { } // store the overlay records if (!overlayList.isEmpty()) { - try (DBHandler dbHandler = GBApplication.acquireDB()) { - DaoSession session = dbHandler.getDaoSession(); - PebbleHealthActivityOverlayDao overlayDao = session.getPebbleHealthActivityOverlayDao(); - overlayDao.insertOrReplaceInTx(overlayList); - - } catch (Exception ex) { - //FIXME: this whole try catch is probably in the wrong place. - } + PebbleHealthActivityOverlayDao overlayDao = targetSession.getPebbleHealthActivityOverlayDao(); + overlayDao.insertOrReplaceInTx(overlayList); } } } From 2a2ad20aa3f9079373ba546df17a79678bcec6a0 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 21 Aug 2016 20:24:02 +0200 Subject: [PATCH 190/569] Store raw data in the DB for later interpretation. --- .../DatalogSessionHealthOverlayData.java | 34 +++++++++++-------- .../pebble/DatalogSessionHealthSleep.java | 34 ++++++++++++------- .../pebble/DatalogSessionHealthSteps.java | 27 +++++++++++---- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index db7c21fe..2e4e1273 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -4,6 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -44,18 +45,13 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { int recordCount = length / itemSize; OverlayRecord[] overlayRecords = new OverlayRecord[recordCount]; + byte[] tempRecord = new byte[itemSize]; for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) { beginOfRecordPosition = initialPosition + recordIdx * itemSize; datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record - recordVersion = datalogMessage.getShort(); - if ((recordVersion != 1) && (recordVersion != 3)) - return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it - - datalogMessage.getShort();//throwaway, unknown - recordType = datalogMessage.getShort(); - - overlayRecords[recordIdx] = new OverlayRecord(recordType, datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt()); + datalogMessage.get(tempRecord); + overlayRecords[recordIdx] = new OverlayRecord(tempRecord); } store(overlayRecords); @@ -72,7 +68,7 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (OverlayRecord overlayRecord : overlayRecords) { - overlayList.add(new PebbleHealthActivityOverlay(overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds, overlayRecord.type, deviceId, userId, null)); + overlayList.add(new PebbleHealthActivityOverlay(overlayRecord.timestampStart, overlayRecord.timestampStart + overlayRecord.durationSeconds, overlayRecord.type, deviceId, userId, overlayRecord.rawData)); } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { @@ -81,16 +77,26 @@ class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth { } private class OverlayRecord { + byte[] knownVersions = {1, 3}; + short version; int type; //1=sleep, 2=deep sleep int offsetUTC; //probably int timestampStart; int durationSeconds; + byte[] rawData; - public OverlayRecord(int type, int offsetUTC, int timestampStart, int durationSeconds) { - this.type = type; - this.offsetUTC = offsetUTC; - this.timestampStart = timestampStart; - this.durationSeconds = durationSeconds; + public OverlayRecord(byte[] rawData) { + this.rawData = rawData; + ByteBuffer record = ByteBuffer.wrap(rawData); + record.order(ByteOrder.LITTLE_ENDIAN); + + this.version = record.getShort(); + //TODO: check supported versions? + record.getShort();//throwaway, unknown + this.type = record.getShort(); + this.offsetUTC = record.getInt(); + this.timestampStart = record.getInt(); + this.durationSeconds = record.getInt(); } } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 5115be5b..7fca543e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -4,6 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -43,18 +44,14 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { int recordCount = length / itemSize; SleepRecord[] sleepRecords = new SleepRecord[recordCount]; + byte[] tempRecord = new byte[itemSize]; for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) { beginOfRecordPosition = initialPosition + recordIdx * itemSize; datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record - recordVersion = datalogMessage.getShort(); - if (recordVersion != 1) - return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it + datalogMessage.get(tempRecord); - sleepRecords[recordIdx] = new SleepRecord(datalogMessage.getInt(), - datalogMessage.getInt(), - datalogMessage.getInt(), - datalogMessage.getInt()); + sleepRecords[recordIdx] = new SleepRecord(tempRecord); } store(sleepRecords); @@ -71,7 +68,8 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { List overlayList = new ArrayList<>(); for (SleepRecord sleepRecord : sleepRecords) { - overlayList.add(new PebbleHealthActivityOverlay(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sleepRecord.type, deviceId, userId, null)); + //TODO: check the firmware version and don't use the sleep record if overlay is available? + overlayList.add(new PebbleHealthActivityOverlay(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sleepRecord.type, deviceId, userId, sleepRecord.rawData)); } overlayDao.insertOrReplaceInTx(overlayList); } catch (Exception ex) { @@ -80,17 +78,27 @@ class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth { } private class SleepRecord { + byte[] knownVersions = {1}; + short version; int type = 1; //sleep, hardcoded as we don't get other info int offsetUTC; //probably int bedTimeStart; int bedTimeEnd; int deepSleepSeconds; + byte[] rawData; - public SleepRecord(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) { - this.offsetUTC = offsetUTC; - this.bedTimeStart = bedTimeStart; - this.bedTimeEnd = bedTimeEnd; - this.deepSleepSeconds = deepSleepSeconds; + public SleepRecord(byte[] rawData) { + this.rawData = rawData; + ByteBuffer record = ByteBuffer.wrap(rawData); + record.order(ByteOrder.LITTLE_ENDIAN); + + + this.version = record.getShort(); + //TODO: check supported versions? + this.offsetUTC = record.getInt(); + this.bedTimeStart = record.getInt(); + this.bedTimeEnd = record.getInt(); + this.deepSleepSeconds = record.getInt(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index f6b661c3..ef85faba 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -59,10 +60,12 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { beginOfRecordPosition = datalogMessage.position(); StepsRecord[] stepsRecords = new StepsRecord[recordNum]; + byte[] tempRecord = new byte[recordLength]; for (int recordIdx = 0; recordIdx < recordNum; recordIdx++) { datalogMessage.position(beginOfRecordPosition + recordIdx * recordLength); //we may not consume all the bytes of a record - stepsRecords[recordIdx] = new StepsRecord(timestamp, datalogMessage.get() & 0xff, datalogMessage.get() & 0xff, datalogMessage.getShort() & 0xffff, datalogMessage.get() & 0xff); + datalogMessage.get(tempRecord); + stepsRecords[recordIdx] = new StepsRecord(timestamp, recordVersion, tempRecord); timestamp += 60; } @@ -84,7 +87,7 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { samples[j] = new PebbleHealthActivitySample( stepsRecord.timestamp, userId, deviceId, - null, // raw data here + stepsRecord.rawData, stepsRecord.intensity, stepsRecord.steps ); @@ -98,18 +101,28 @@ public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth { } private class StepsRecord { + byte[] knownVersions = {5, 6}; + short version; int timestamp; int steps; int orientation; int intensity; int light_intensity; + byte[] rawData; - public StepsRecord(int timestamp, int steps, int orientation, int intensity, int light_intensity) { + public StepsRecord(int timestamp, short version, byte[] rawData) { this.timestamp = timestamp; - this.steps = steps; - this.orientation = orientation; - this.intensity = intensity; - this.light_intensity = light_intensity; + this.rawData = rawData; + ByteBuffer record = ByteBuffer.wrap(rawData); + record.order(ByteOrder.LITTLE_ENDIAN); + + this.version = version; + //TODO: check supported versions? + + this.steps = record.get() & 0xff; + this.orientation = record.get() & 0xff; + this.intensity = record.getShort() & 0xffff; + this.light_intensity = record.get() & 0xff; } } From ec4469a87b0c8f66183c8988e10aa9e30834c22b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 24 Aug 2016 20:15:26 +0200 Subject: [PATCH 191/569] Testing with dummy onboarding activity crashes --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 5 ++ .../activities/ControlCenter.java | 13 ++++ .../activities/OnboardingActivity.java | 73 +++++++++++++++++++ .../main/res/layout/activity_onboarding.xml | 19 +++++ 5 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/OnboardingActivity.java create mode 100644 app/src/main/res/layout/activity_onboarding.xml diff --git a/app/build.gradle b/app/build.gradle index 65373c94..381f28e1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.11.2" - versionCode 58 + versionName "0.12.0" + versionCode 59 } buildTypes { release { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 97599340..a993e4f7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -226,6 +226,11 @@ android:parentActivityName=".activities.ControlCenter" android:screenOrientation="portrait" android:windowSoftInputMode="stateHidden" /> + () { + @Override + protected Object doInBackground(Object[] params) { + helper.importOldDb(oldHandler, device, targetHandler); + progress.dismiss(); + return null; + } + }.execute((Object[]) null); + } catch (Exception ex) { + GB.toast(OnboardingActivity.this, "Error importing old activity data into new database.", Toast.LENGTH_LONG, GB.ERROR, ex); + } + } + +} diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml new file mode 100644 index 00000000..a1a97d87 --- /dev/null +++ b/app/src/main/res/layout/activity_onboarding.xml @@ -0,0 +1,19 @@ + + + +

-

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

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