diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 4e0c91c3..d16904da 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -64,6 +64,7 @@ public class GBDaoGenerator { addPebbleMorpheuzActivitySample(schema, user, device); addHPlusHealthActivityKindOverlay(schema, user, device); addHPlusHealthActivitySample(schema, user, device); + addNo1F1ActivitySample(schema, user, device); addCalendarSyncState(schema, device); @@ -257,6 +258,15 @@ public class GBDaoGenerator { return activityOverlay; } + private static Entity addNo1F1ActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "No1F1ActivitySample"); + activitySample.implementsSerializable(); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + 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/no1f1/No1F1Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java index e839955e..651fdcb4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java @@ -15,6 +15,9 @@ public final class No1F1Constants { public static final byte CMD_USER_DATA = (byte) 0xa9; public static final byte CMD_ALARM = (byte) 0xab; public static final byte CMD_FACTORY_RESET = (byte) 0xad; + public static final byte CMD_REALTIME_STEPS = (byte) 0xb1; + public static final byte CMD_FETCH_STEPS = (byte) 0xb2; + public static final byte CMD_FETCH_SLEEP = (byte) 0xb3; public static final byte CMD_NOTIFICATION = (byte) 0xc1; public static final byte CMD_ICON = (byte) 0xc3; public static final byte CMD_DEVICE_SETTINGS = (byte) 0xd3; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java index bb51683a..4cd9dcb5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java @@ -13,12 +13,15 @@ import android.support.annotation.Nullable; import java.util.Collection; import java.util.Collections; +import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.No1F1ActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -65,22 +68,22 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator { @Nullable @Override public Class getPrimaryActivity() { - return null; + return ChartsActivity.class; } @Override public boolean supportsActivityDataFetching() { - return false; + return true; } @Override public boolean supportsActivityTracking() { - return false; + return true; } @Override public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { - return null; + return new No1F1SampleProvider(device, session); } @Override @@ -135,6 +138,8 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator { @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { - + Long deviceId = device.getId(); + QueryBuilder qb = session.getNo1F1ActivitySampleDao().queryBuilder(); + qb.where(No1F1ActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java new file mode 100644 index 00000000..0ff31708 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java @@ -0,0 +1,68 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.no1f1; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +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.No1F1ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.No1F1ActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public class No1F1SampleProvider extends AbstractSampleProvider { + + private GBDevice mDevice; + private DaoSession mSession; + + public No1F1SampleProvider(GBDevice device, DaoSession session) { + super(device, session); + + mSession = session; + mDevice = device; + } + + @Override + public int normalizeType(int rawType) { + return rawType; + } + + @Override + public int toRawActivityKind(int activityKind) { + return activityKind; + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity; + } + + @Override + public No1F1ActivitySample createActivitySample() { + return new No1F1ActivitySample(); + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getNo1F1ActivitySampleDao(); + } + + @Nullable + @Override + protected Property getRawKindSampleProperty() { + return No1F1ActivitySampleDao.Properties.RawKind; + } + + @NonNull + @Override + protected Property getTimestampSampleProperty() { + return No1F1ActivitySampleDao.Properties.Timestamp; + } + + @NonNull + @Override + protected Property getDeviceIdentifierSampleProperty() { + return No1F1ActivitySampleDao.Properties.DeviceId; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java index 4d4e8f29..b2a0bc0b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -12,12 +12,20 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.List; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Constants; +import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.No1F1ActivitySample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; @@ -29,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import static org.apache.commons.lang3.math.NumberUtils.min; @@ -38,6 +47,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); public BluetoothGattCharacteristic ctrlCharacteristic = null; public BluetoothGattCharacteristic measureCharacteristic = null; + private List samples = new ArrayList<>(); public No1F1Support() { super(LOG); @@ -99,6 +109,9 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { case No1F1Constants.CMD_USER_DATA: LOG.info("User data updated"); return true; + case No1F1Constants.CMD_FETCH_STEPS: + handleStepData(data); + return true; case No1F1Constants.CMD_NOTIFICATION: case No1F1Constants.CMD_ICON: case No1F1Constants.CMD_DEVICE_SETTINGS: @@ -201,15 +214,13 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { @Override public void onFetchActivityData() { - - } - - @Override - public void onReboot() { try { - TransactionBuilder builder = performInitialized("clearNotification"); + samples.clear(); + TransactionBuilder builder = performInitialized("fetchSteps"); + builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.busy_task_fetch_activity_data), getContext())); byte[] msg = new byte[]{ - (byte) 0xad + No1F1Constants.CMD_FETCH_STEPS, + (byte) 0xfa }; builder.write(ctrlCharacteristic, msg); performConnected(builder.getTransaction()); @@ -217,6 +228,10 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { } } + @Override + public void onReboot() { + } + @Override public void onHeartRateTest() { @@ -430,4 +445,48 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { } catch (IOException e) { } } + + private void handleStepData(byte[] data) { + if (data[1] == (byte) 0xfd) { + // TODO Check CRC + if (samples.size() > 0) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + No1F1SampleProvider provider = new No1F1SampleProvider(getDevice(), dbHandler.getDaoSession()); + for (int i = 0; i < samples.size(); i++) { + samples.get(i).setDeviceId(deviceId); + samples.get(i).setUserId(userId); + samples.get(i).setRawKind(ActivityKind.TYPE_ACTIVITY); + provider.addGBActivitySample(samples.get(i)); + } + samples.clear(); + LOG.info("Steps data saved"); + if (getDevice().isBusy()) { + getDevice().unsetBusyTask(); + getDevice().sendDeviceUpdateIntent(getContext()); + } + } catch (Exception ex) { + } + } + } else { + No1F1ActivitySample sample = new No1F1ActivitySample(); + + Calendar timestamp = GregorianCalendar.getInstance(); + timestamp.set(Calendar.YEAR, data[1] * 256 + (data[2] & 0xff)); + timestamp.set(Calendar.MONTH, (data[3] - 1) & 0xff); + timestamp.set(Calendar.DAY_OF_MONTH, data[4] & 0xff); + timestamp.set(Calendar.HOUR_OF_DAY, data[5] & 0xff); + timestamp.set(Calendar.MINUTE, 0); + timestamp.set(Calendar.SECOND, 0); + + sample.setTimestamp((int) (timestamp.getTimeInMillis() / 1000L)); + sample.setSteps(data[6] * 256 + (data[7] & 0xff)); + + samples.add(sample); + LOG.info("Received steps data for " + String.format("%1$TD %1$TT", timestamp) + ": " + + sample.getSteps() + " steps" + ); + } + } }