diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index cc41d4a8f..c52e016c0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -79,7 +79,6 @@ public final class HPlusConstants { public static final byte ARG_FINDME_ON = 0x01; public static final byte ARG_FINDME_OFF = 0x02; - public static final byte CMD_GET_VERSION = 0x17; public static final byte CMD_SET_END = 0x4f; public static final byte CMD_SET_INCOMING_CALL_NUMBER = 0x23; public static final byte CMD_SET_ALLDAY_HRM = 0x35; @@ -89,7 +88,8 @@ public final class HPlusConstants { public static final byte CMD_SET_SIT_INTERVAL = 0x51; public static final byte CMD_SET_HEARTRATE_STATE = 0x32; - //Actions to device + //GET messages + public static final byte CMD_GET_VERSION = 0x17; public static final byte CMD_GET_ACTIVE_DAY = 0x27; public static final byte CMD_GET_DAY_DATA = 0x15; public static final byte CMD_GET_SLEEP = 0x19; @@ -122,7 +122,7 @@ public final class HPlusConstants { public static final byte DATA_SLEEP = 0x1A; public static final byte DATA_VERSION = 0x18; public static final byte DATA_VERSION1 = 0x2E; - + public static final byte DATA_DAY_UNKNOWN = 0x52; //To be defined public static final byte DATA_UNKNOWN = 0x4d; public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java index adda8dc43..a348f0ea8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -21,6 +21,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.hplus; */ import android.support.annotation.NonNull; +import android.util.Log; import java.util.Calendar; import java.util.Collections; @@ -57,7 +58,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) { - if(overlay.getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP || overlay.getRawKind() == ActivityKind.TYPE_DEEP_SLEEP) { - if(sample.getRawKind() == HPlusDataRecord.TYPE_DAY_SLOT && sample.getSteps() > 0){ + if (overlay.getRawKind() == ActivityKind.TYPE_NOT_WORN || overlay.getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP || overlay.getRawKind() == ActivityKind.TYPE_DEEP_SLEEP) { + if (sample.getRawKind() == HPlusDataRecord.TYPE_DAY_SLOT && sample.getSteps() > 0){ nonSleepTimeEnd = sample.getTimestamp() + 10 * 60; // 10 minutes continue; }else if(sample.getRawKind() == HPlusDataRecord.TYPE_REALTIME && sample.getTimestamp() <= nonSleepTimeEnd){ continue; } - sample.setRawKind(overlay.getRawKind()); + if (overlay.getRawKind() == ActivityKind.TYPE_NOT_WORN) + sample.setHeartRate(0); + + if (sample.getRawKind() != ActivityKind.TYPE_NOT_WORN) + sample.setRawKind(overlay.getRawKind()); + sample.setRawIntensity(10); } } @@ -199,8 +205,8 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider= today.getTimeInMillis() / 1000){ + for (HPlusHealthActivitySample sample: samples) { + if (sample.getTimestamp() >= today.getTimeInMillis() / 1000) { /**Strategy is: * Calculate max steps from realtime messages @@ -215,7 +221,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider 0) { + if (now.compareTo(mGetDaySummaryTime) > 0) { requestDaySummaryData(); } - if(now.compareTo(mHelloTime) > 0){ + if (now.compareTo(mHelloTime) > 0) { sendHello(); } @@ -179,7 +179,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { mDaySlotRecords.clear(); try { - if(!mHPlusSupport.isConnected()) + if (!mHPlusSupport.isConnected()) mHPlusSupport.connect(); TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); @@ -189,7 +189,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA}); mHPlusSupport.performConnected(builder.getTransaction()); - }catch(Exception e){ + } catch(Exception e) { LOG.warn("HPlus: Synchronization exception: " + e); } @@ -198,13 +198,13 @@ class HPlusHandlerThread extends GBDeviceIoThread { } } - public void sendHello(){ + public void sendHello() { try { TransactionBuilder builder = new TransactionBuilder("hello"); builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO); mHPlusSupport.performConnected(builder.getTransaction()); - }catch(Exception e){ + } catch(Exception e) { } mHelloTime = GregorianCalendar.getInstance(); @@ -227,31 +227,33 @@ class HPlusHandlerThread extends GBDeviceIoThread { try{ record = new HPlusDataRecordDaySlot(data, age); - } catch(IllegalArgumentException e){ + } catch(IllegalArgumentException e) { LOG.info((e.getMessage())); return false; } + LOG.info("SLOT: " + record); + Calendar now = GregorianCalendar.getInstance(); int nowSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + (now.get(Calendar.MINUTE) / 10); - if(record.slot == nowSlot){ - if(mCurrentDaySlot != null && mCurrentDaySlot != record){ + if (record.slot == nowSlot){ + if (mCurrentDaySlot != null && mCurrentDaySlot != record) { mCurrentDaySlot.accumulate(record); mDaySlotRecords.add(mCurrentDaySlot); mCurrentDaySlot = null; - }else{ + } else { //Store it to a temp variable as this is an intermediate value mCurrentDaySlot = record; - if(!mSlotsInitialSync) + if (!mSlotsInitialSync) return true; } } - if(mSlotsInitialSync) { + if (mSlotsInitialSync) { //If the slot is in the future, actually it is from the previous day //Subtract a day of seconds - if(record.slot > nowSlot){ + if (record.slot > nowSlot) { record.timestamp -= 3600 * 24; } @@ -260,7 +262,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { } //Ignore the current slot as it is incomplete - if(record.slot != nowSlot) + if (record.slot != nowSlot) mDaySlotRecords.add(record); //Still fetching ring buffer. Request the next slots @@ -272,14 +274,14 @@ class HPlusHandlerThread extends GBDeviceIoThread { } //Keep buffering - if(record.slot != 143) + if (record.slot != 143) return true; } else { mGetDaySlotsTime = GregorianCalendar.getInstance(); mGetDaySlotsTime.add(Calendar.DAY_OF_MONTH, 1); } - if(mDaySlotRecords.size() > 0) { + if (mDaySlotRecords.size() > 0) { //Sort the samples Collections.sort(mDaySlotRecords, new Comparator() { public int compare(HPlusDataRecordDaySlot one, HPlusDataRecordDaySlot other) { @@ -287,6 +289,8 @@ class HPlusHandlerThread extends GBDeviceIoThread { } }); + List notWornSlots = new ArrayList<>(); + try (DBHandler dbHandler = GBApplication.acquireDB()) { HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); List samples = new ArrayList<>(); @@ -294,23 +298,62 @@ class HPlusHandlerThread extends GBDeviceIoThread { for (HPlusDataRecordDaySlot storedRecord : mDaySlotRecords) { //Invalid records (no data) will be ignored - if(!storedRecord.isValid()) + if (!storedRecord.isValid()) continue; HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp); sample.setRawHPlusHealthData(storedRecord.getRawData()); sample.setSteps(storedRecord.steps); + sample.setRawIntensity(storedRecord.intensity); sample.setHeartRate(storedRecord.heartRate); sample.setRawKind(storedRecord.type); sample.setProvider(provider); samples.add(sample); + + if (HPlusCoordinator.getAllDayHR(gbDevice.getAddress()) == HPlusConstants.ARG_HEARTRATE_ALLDAY_ON && storedRecord.heartRate == ActivitySample.NOT_MEASURED && storedRecord.steps <= 0) { + notWornSlots.add(sample.getTimestamp()); + notWornSlots.add(sample.getTimestamp() + 10 * 60); + + } } provider.getSampleDao().insertOrReplaceInTx(samples); mDaySlotRecords.clear(); + //Create an overlay with unused slots + if (notWornSlots.size() > 0) { + DaoSession session = dbHandler.getDaoSession(); + Long userId = DBHelper.getUser(session).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), session).getId(); + + HPlusHealthActivityOverlayDao overlayDao = session.getHPlusHealthActivityOverlayDao(); + List overlayList = new ArrayList<>(); + + + int firstSlotTimestamp = notWornSlots.get(0); + int lastSlotTimestamp = notWornSlots.get(0); + + int i = 1; + for (Integer timestamp : notWornSlots) { + + //If it is the last of the samples or of the interruption period + if (timestamp - lastSlotTimestamp > 10 * 60) { + overlayList.add(new HPlusHealthActivityOverlay(firstSlotTimestamp, lastSlotTimestamp, ActivityKind.TYPE_NOT_WORN, deviceId, userId, null)); + firstSlotTimestamp = timestamp; + } + + lastSlotTimestamp = timestamp; + + } + + if (firstSlotTimestamp != lastSlotTimestamp) + overlayList.add(new HPlusHealthActivityOverlay(firstSlotTimestamp, lastSlotTimestamp, ActivityKind.TYPE_NOT_WORN, deviceId, userId, null)); + + overlayDao.insertOrReplaceInTx(overlayList); + } + } catch (GBException ex) { LOG.info((ex.getMessage())); } catch (Exception ex) { @@ -330,7 +373,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { * @param data the message from the device * @return boolean indicating success or fail */ - public boolean processIncomingSleepData(byte[] data){ + public boolean processIncomingSleepData(byte[] data) { HPlusDataRecordSleep record; try{ @@ -354,7 +397,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { List overlayList = new ArrayList<>(); List intervals = record.getIntervals(); - for(HPlusDataRecord.RecordInterval interval : intervals){ + for(HPlusDataRecord.RecordInterval interval : intervals) { overlayList.add(new HPlusHealthActivityOverlay(interval.timestampFrom, interval.timestampTo, interval.activityKind, deviceId, userId, null)); } @@ -387,16 +430,16 @@ class HPlusHandlerThread extends GBDeviceIoThread { public boolean processRealtimeStats(byte[] data, int age) { HPlusDataRecordRealtime record; - try{ + try { record = new HPlusDataRecordRealtime(data, age); } catch(IllegalArgumentException e){ LOG.info((e.getMessage())); return false; } - + LOG.info("RealTime: " + record); //Skip duplicated messages as the device seems to send the same record multiple times //This can be used to detect the user is moving (not sleeping) - if(prevRealTimeRecord != null && record.same(prevRealTimeRecord)) + if (prevRealTimeRecord != null && record.same(prevRealTimeRecord)) return true; prevRealTimeRecord = record; @@ -406,10 +449,10 @@ class HPlusHandlerThread extends GBDeviceIoThread { //Skip when measuring heart rate //Calories and Distance are updated and these values will be lost. //Because a message with a valid Heart Rate will be provided, this loss very limited - if(record.heartRate == ActivityKind.TYPE_NOT_MEASURED) { + if (record.heartRate == ActivityKind.TYPE_NOT_MEASURED) { getDevice().setFirmwareVersion2("---"); getDevice().sendDeviceUpdateIntent(getContext()); - }else { + } else { getDevice().setFirmwareVersion2("" + record.heartRate); getDevice().sendDeviceUpdateIntent(getContext()); } @@ -457,9 +500,9 @@ class HPlusHandlerThread extends GBDeviceIoThread { public boolean processDaySummary(byte[] data) { HPlusDataRecordDaySummary record; - try{ + try { record = new HPlusDataRecordDaySummary(data); - } catch(IllegalArgumentException e){ + } catch(IllegalArgumentException e) { LOG.info((e.getMessage())); return false; } @@ -499,7 +542,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { public boolean processVersion(byte[] data) { int major, minor; - if(data.length >= 11){ + if (data.length >= 11) { major = data[10] & 0xFF; minor = data[9] & 0xFF; @@ -507,8 +550,8 @@ class HPlusHandlerThread extends GBDeviceIoThread { int hwMinor = data[1] & 0xFF; getDevice().setFirmwareVersion2(hwMajor + "." + hwMinor); - mHPlusSupport.setUnicodeSupport((data[3] != 0)); - }else { + mHPlusSupport.setUnicodeSupport(data[3] != 0); + } else { major = data[2] & 0xFF; minor = data[1] & 0xFF; } @@ -527,7 +570,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); mHPlusSupport.performConnected(builder.getTransaction()); - }catch(Exception e){ + } catch(Exception e) { } @@ -549,23 +592,23 @@ class HPlusHandlerThread extends GBDeviceIoThread { //Sync to current time mGetDaySlotsTime = now; - if(mSlotsInitialSync) { - if(mLastSlotReceived == 143) { + if (mSlotsInitialSync) { + if (mLastSlotReceived == 143) { mSlotsInitialSync = false; mGetDaySlotsTime.set(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); //Sync complete. Delay timer forever mLastSlotReceived = -1; mLastSlotRequested = mLastSlotReceived + 1; return; - }else { + } else { mGetDaySlotsTime.add(Calendar.SECOND, CURRENT_DAY_SYNC_RETRY_PERIOD); } - }else{ + } else { //Sync complete. Delay timer forever mGetDaySlotsTime.set(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD); return; } - if(mLastSlotReceived == 143) + if (mLastSlotReceived == 143) mLastSlotReceived = -1; byte hour = (byte) ((mLastSlotReceived + 1)/ 6); @@ -582,19 +625,19 @@ class HPlusHandlerThread extends GBDeviceIoThread { TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); builder.write(mHPlusSupport.ctrlCharacteristic, msg); mHPlusSupport.performConnected(builder.getTransaction()); - }catch(Exception e){ + } catch(Exception e) { } } /** * Request a batch of data with the summary of the previous days */ - public void requestDaySummaryData(){ + public void requestDaySummaryData() { try { TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary"); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); mHPlusSupport.performConnected(builder.getTransaction()); - }catch(Exception e){ + } catch(Exception e) { } mGetDaySummaryTime = GregorianCalendar.getInstance(); @@ -607,7 +650,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { * @param timestamp The sample timestamp * @return The sample just created */ - private HPlusHealthActivitySample createSample(DBHandler dbHandler, int timestamp){ + private HPlusHealthActivitySample createSample(DBHandler dbHandler, int timestamp) { Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); HPlusHealthActivitySample sample = new HPlusHealthActivitySample( diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 06ba110dc..e84c8b10f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -819,7 +819,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { cs = HPlusConstants.transliterateMap.get(c); } else { try { - if(HPlusCoordinator.getUnicodeSupport(this.gbDevice.getAddress())) if(unicode) cs = c.toString().getBytes("Unicode"); else