diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereConstants.java new file mode 100644 index 00000000..27a6d95a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereConstants.java @@ -0,0 +1,173 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.here; + +/* +* @author Nicolò Balzarotti <anothersms@gmail.com> +*/ + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public final class HereConstants { + + public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("14702856-620a-3973-7c78-9cfff0876abd"); + public static final UUID UUID_CHARACTERISTIC_MEASURE = UUID.fromString("14702853-620a-3973-7c78-9cfff0876abd"); + public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); + + public static final byte ARG_WRIST_LEFT = 0; //Guess... + public static final byte ARG_WRIST_RIGHT = 1; //Guess... + + public static final byte ARG_LANGUAGE_CN = 1; + public static final byte ARG_LANGUAGE_EN = 2; + + public static final byte ARG_TIMEMODE_24H = 1; + public static final byte ARG_TIMEMODE_12H = 0; + + public static final byte ARG_UNIT_METRIC = 0; + public static final byte ARG_UNIT_IMPERIAL = 1; + + public static final byte ARG_GENDER_MALE = 0; + public static final byte ARG_GENDER_FEMALE = 1; + + public static final byte ARG_HEARTRATE_MEASURE_ON = 11; + public static final byte ARG_HEARTRATE_MEASURE_OFF = 22; + + public static final byte ARG_HEARTRATE_ALLDAY_ON = 0x0A; + public static final byte ARG_HEARTRATE_ALLDAY_OFF = (byte) 0xff; + + public static final byte INCOMING_CALL_STATE_DISABLED_THRESHOLD = 0x7B; + public static final byte INCOMING_CALL_STATE_ENABLED = (byte) 0xAA; + + public static final byte ARG_ALARM_DISABLE = (byte) -1; + + public static final byte[] CMD_SET_PREF_START = new byte[]{0x4f, 0x5a}; + public static final byte[] CMD_SET_PREF_START1 = new byte[]{0x4d}; + //public static final byte CMD_SET_ALARM = 0x4c; Unknown + public static final byte CMD_SET_ALARM = 0x0c; + public static final byte CMD_SET_LANGUAGE = 0x22; + public static final byte CMD_SET_TIMEMODE = 0x47; + public static final byte CMD_SET_UNITS = 0x48; + public static final byte CMD_SET_GENDER = 0x2d; + public static final byte CMD_SET_DATE = 0x08; + public static final byte CMD_SET_TIME = 0x09; + public static final byte CMD_SET_WEEK = 0x2a; + public static final byte CMD_SET_PREF_SIT = 0x1e; + public static final byte CMD_SET_WEIGHT = 0x05; + public static final byte CMD_HEIGHT = 0x04; + public static final byte CMD_SET_AGE = 0x2c; + public static final byte CMD_SET_GOAL = 0x26; + public static final byte CMD_SET_SCREENTIME = 0x0b; + public static final byte CMD_SET_BLOOD = 0x4e; //?? + + public static final byte CMD_SET_FINDME = 0x0a; + public static final byte ARG_FINDME_ON = 0x01; + public static final byte ARG_FINDME_OFF = 0x02; + + public static final byte CMD_GET_VERSION = 0x17; + public static final byte CMD_SET_END = 0x4f; + public static final byte CMD_SET_INCOMING_CALL_NUMBER = 0x23; + public static final byte CMD_SET_ALLDAY_HRM = 0x35; + public static final byte CMD_ACTION_INCOMING_CALL = 0x41; + public static final byte CMD_SET_CONF_END = 0x4f; + public static final byte CMD_SET_PREFS = 0x50; + public static final byte CMD_SET_SIT_INTERVAL = 0x51; + public static final byte CMD_SET_HEARTRATE_STATE = 0x32; + + //Actions to device + public static final byte CMD_GET_ACTIVE_DAY = 0x27; + public static final byte CMD_GET_DAY_DATA = 0x15; + public static final byte CMD_GET_SLEEP = 0x19; + public static final byte CMD_GET_CURR_DATA = 0x16; + public static final byte CMD_GET_DEVICE_ID = 0x24; + + public static final byte CMD_ACTION_INCOMING_SOCIAL = 0x31; + //public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; //Unknown + public static final byte CMD_ACTION_DISPLAY_TEXT = 0x43; + + public static final byte CMD_ACTION_DISPLAY_TEXT_NAME = 0x3F; + public static final byte CMD_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312? + public static final byte[] CMD_ACTION_HELLO = new byte[]{0x01, 0x00}; + public static final byte CMD_SHUTDOWN = 0x5B; + public static final byte ARG_SHUTDOWN_EN = 0x5A; + + public static final byte CMD_FACTORY_RESET = -74; + public static final byte ARG_FACTORY_RESET_EN = 0x5A; + + public static final byte CMD_SET_INCOMING_MESSAGE = 0x07; + public static final byte CMD_SET_INCOMING_CALL = 0x06; + public static final byte ARG_INCOMING_CALL = (byte) -86; + public static final byte ARG_INCOMING_MESSAGE = (byte) -86; + + //Incoming Messages + public static final byte DATA_STATS = 0x33; + public static final byte DATA_STEPS = 0x36; + public static final byte DATA_DAY_SUMMARY = 0x38; + public static final byte DATA_DAY_SUMMARY_ALT = 0x39; + public static final byte DATA_SLEEP = 0x1A; + public static final byte DATA_VERSION = 0x18; + + public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; + public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; + public static final String PREF_HPLUS_UNIT = "hplus_unit"; + public static final String PREF_HPLUS_TIMEFORMAT = "hplus_timeformat"; + public static final String PREF_HPLUS_WRIST = "hplus_wrist"; + public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time"; + public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time"; + + public static final Map transliterateMap = new HashMap(){ + { + //These are missing + put('ó', new Byte((byte) 111)); + put('Ó', new Byte((byte) 79)); + put('í', new Byte((byte) 105)); + put('Í', new Byte((byte) 73)); + put('ú', new Byte((byte) 117)); + put('Ú', new Byte((byte) 85)); + + //These mostly belong to the extended ASCII table + put('Ç', new Byte((byte) 128)); + put('ü', new Byte((byte) 129)); + put('é', new Byte((byte) 130)); + put('â', new Byte((byte) 131)); + put('ä', new Byte((byte) 132)); + put('à', new Byte((byte) 133)); + put('ã', new Byte((byte) 134)); + put('ç', new Byte((byte) 135)); + put('ê', new Byte((byte) 136)); + put('ë', new Byte((byte) 137)); + put('è', new Byte((byte) 138)); + put('Ï', new Byte((byte) 139)); + put('Î', new Byte((byte) 140)); + put('Ì', new Byte((byte) 141)); + put('Ã', new Byte((byte) 142)); + put('Ä', new Byte((byte) 143)); + put('É', new Byte((byte) 144)); + put('æ', new Byte((byte) 145)); + put('Æ', new Byte((byte) 146)); + put('ô', new Byte((byte) 147)); + put('ö', new Byte((byte) 148)); + put('ò', new Byte((byte) 149)); + put('û', new Byte((byte) 150)); + put('ù', new Byte((byte) 151)); + put('ÿ', new Byte((byte) 152)); + put('Ö', new Byte((byte) 153)); + put('Ü', new Byte((byte) 154)); + put('¢', new Byte((byte) 155)); + put('£', new Byte((byte) 156)); + put('¥', new Byte((byte) 157)); + put('ƒ', new Byte((byte) 159)); + put('á', new Byte((byte) 160)); + put('ñ', new Byte((byte) 164)); + put('Ñ', new Byte((byte) 165)); + put('ª', new Byte((byte) 166)); + put('º', new Byte((byte) 167)); + put('¿', new Byte((byte) 168)); + put('¬', new Byte((byte) 170)); + put('½', new Byte((byte) 171)); + put('¼', new Byte((byte) 172)); + put('¡', new Byte((byte) 173)); + put('«', new Byte((byte) 174)); + put('»', new Byte((byte) 175)); + } + }; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereCoordinator.java new file mode 100644 index 00000000..5932b4bd --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereCoordinator.java @@ -0,0 +1,261 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.here; + +/* +* @author Nicolò Balzarotti <anothersms@gmail.com> +*/ + +import android.annotation.TargetApi; +import android.app.Activity; +import android.bluetooth.le.ScanFilter; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; +import android.support.annotation.NonNull; + +import de.greenrobot.dao.query.QueryBuilder; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +//import nodomain.freeyourgadget.gadgetbridge.entities.HereHealthActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; +import java.util.Locale; +import java.util.regex.Pattern; // Regex match name +import java.util.regex.Matcher; // Regex match name + +import static nodomain.freeyourgadget.gadgetbridge.GBApplication.getContext; + +public class HereCoordinator extends AbstractDeviceCoordinator { + protected static final Logger LOG = LoggerFactory.getLogger(HereCoordinator.class); + protected static Prefs prefs = GBApplication.getPrefs(); + + @NonNull + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid hpService = new ParcelUuid(HereConstants.UUID_SERVICE_HP); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(hpService).build(); + return Collections.singletonList(filter); + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + Pattern pat = Pattern.compile("H([LR])[A-F0-9]+|HERE-([LR])-[A-F0-9]+"); + Matcher mat = pat.matcher(name); + // Identify if device is Left or Right + if (mat.matches()) { + if (mat.group(1) == "L") { + return DeviceType.HEREL; + } else if (mat.group(1) == "R") { + return DeviceType.HERER; + } + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.HERER; //FIXME: SAVE device type + } + + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public Class getPrimaryActivity() { + return ChartsActivity.class; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return true; + } + + @Override + public boolean supportsActivityTracking() { + return true; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsAlarmConfiguration() { + return true; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return true; + } + + @Override + public int getTapString() { + return R.string.tap_connected_device_for_activity; + } + + @Override + public String getManufacturer() { + return "Zeblaze"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + Long deviceId = device.getId(); + QueryBuilder qb = session.getHereHealthActivitySampleDao().queryBuilder(); + qb.where(HereHealthActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); + } + + public static byte getLanguage(String address) { + String language = prefs.getString("language", "default"); + Locale locale; + + if (language.equals("default")) { + locale = Locale.getDefault(); + } else { + locale = new Locale(language); + } + + if (locale.getLanguage().equals(new Locale("cn").getLanguage())){ + return HereConstants.ARG_LANGUAGE_CN; + }else{ + return HereConstants.ARG_LANGUAGE_EN; + } + } + + public static byte getTimeMode(String address) { + String tmode = prefs.getString(HereConstants.PREF_HPLUS_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); + + if(tmode.equals(getContext().getString(R.string.p_timeformat_24h))) { + return HereConstants.ARG_TIMEMODE_24H; + }else{ + return HereConstants.ARG_TIMEMODE_12H; + } + + } + + public static byte getUnit(String address) { + String units = prefs.getString(HereConstants.PREF_HPLUS_UNIT, getContext().getString(R.string.p_unit_metric)); + + if(units.equals(getContext().getString(R.string.p_unit_metric))){ + return HereConstants.ARG_UNIT_METRIC; + }else{ + return HereConstants.ARG_UNIT_IMPERIAL; + } + } + + public static byte getUserWeight(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) (activityUser.getWeightKg() & 0xFF); + } + + public static byte getUserHeight(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) (activityUser.getHeightCm() & 0xFF); + } + + public static byte getUserAge(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) (activityUser.getAge() & 0xFF); + } + + public static byte getUserGender(String address) { + ActivityUser activityUser = new ActivityUser(); + + if (activityUser.getGender() == ActivityUser.GENDER_MALE) + return HereConstants.ARG_GENDER_MALE; + + return HereConstants.ARG_GENDER_FEMALE; + } + + public static int getGoal(String address) { + ActivityUser activityUser = new ActivityUser(); + + return activityUser.getStepsGoal(); + } + + public static byte getScreenTime(String address) { + return (byte) (prefs.getInt(HereConstants.PREF_HPLUS_SCREENTIME, 5) & 0xFF); + } + + public static byte getAllDayHR(String address) { + Boolean value = (prefs.getBoolean(HereConstants.PREF_HPLUS_ALLDAYHR, true)); + + if(value){ + return HereConstants.ARG_HEARTRATE_ALLDAY_ON; + }else{ + return HereConstants.ARG_HEARTRATE_ALLDAY_OFF; + } + } + + public static byte getSocial(String address) { + //TODO: Figure what this is. Returning the default value + + return (byte) 255; + } + + public static byte getUserWrist(String address) { + String value = prefs.getString(HereConstants.PREF_HPLUS_WRIST, getContext().getString(R.string.left)); + + if(value.equals(getContext().getString(R.string.left))){ + return HereConstants.ARG_WRIST_LEFT; + }else{ + return HereConstants.ARG_WRIST_RIGHT; + } + } + + public static int getSITStartTime(String address) { + return prefs.getInt(HereConstants.PREF_HPLUS_SIT_START_TIME, 0); + } + + public static int getSITEndTime(String address) { + return prefs.getInt(HereConstants.PREF_HPLUS_SIT_END_TIME, 0); + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereHealthSampleProvider.java new file mode 100644 index 00000000..e615ea65 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/HereHealthSampleProvider.java @@ -0,0 +1,223 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.here; + +/* +* @author Nicolò Balzarotti <anothersms@gmail.com> +*/ + +import android.support.annotation.NonNull; +import android.util.Log; + +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.GregorianCalendar; +import java.util.List; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import de.greenrobot.dao.query.QueryBuilder; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlay; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlayDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusDataRecord; + +public class HereHealthSampleProvider extends AbstractSampleProvider { + + private GBDevice mDevice; + private DaoSession mSession; + + public HereHealthSampleProvider(GBDevice device, DaoSession session) { + super(device, session); + + mSession = session; + mDevice = device; + } + + public int getID() { + + return SampleProvider.PROVIDER_HPLUS; + } + + public int normalizeType(int rawType) { + switch (rawType){ + case HPlusDataRecord.TYPE_DAY_SLOT: + case HPlusDataRecord.TYPE_DAY_SUMMARY: + case HPlusDataRecord.TYPE_REALTIME: + case HPlusDataRecord.TYPE_SLEEP: + case HPlusDataRecord.TYPE_UNKNOWN: + return ActivityKind.TYPE_UNKNOWN; + default: + return rawType; + } + } + + public int toRawActivityKind(int activityKind) { + switch (activityKind){ + case ActivityKind.TYPE_DEEP_SLEEP: + return ActivityKind.TYPE_DEEP_SLEEP; + case ActivityKind.TYPE_LIGHT_SLEEP: + return ActivityKind.TYPE_LIGHT_SLEEP; + default: + return HPlusDataRecord.TYPE_DAY_SLOT; + } + + } + + @NonNull + @Override + protected Property getTimestampSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.Timestamp; + } + + @Override + public HereHealthActivitySample createActivitySample() { + return new HPlusHealthActivitySample(); + } + + @Override + protected Property getRawKindSampleProperty() { + return null; // HPlusHealthActivitySampleDao.Properties.RawKind; + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity / (float) 100.0; + } + + @NonNull + @Override + protected Property getDeviceIdentifierSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.DeviceId; + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getHPlusHealthActivitySampleDao(); + } + + + public List getActivityamples(int timestamp_from, int timestamp_to) { + return getAllActivitySamples(timestamp_from, timestamp_to); + } + + public List getSleepSamples(int timestamp_from, int timestamp_to) { + return getAllActivitySamples(timestamp_from, timestamp_to); + } + + @NonNull + @Override + public List getAllActivitySamples(int timestamp_from, int timestamp_to) { + List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); + + Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); + if (dbDevice == null) { + return Collections.emptyList(); + } + + QueryBuilder qb = getSession().getHPlusHealthActivityOverlayDao().queryBuilder(); + + qb.where(HPlusHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()), + HPlusHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from - 3600 * 24), + HPlusHealthActivityOverlayDao.Properties.TimestampTo.le(timestamp_to), + HPlusHealthActivityOverlayDao.Properties.TimestampTo.ge(timestamp_from)); + + List overlayRecords = qb.build().list(); + + //Todays sample steps will come from the Day Slots messages + //Historical steps will be provided by Day Summaries messages + //This will allow both week and current day results to be consistent + Calendar today = GregorianCalendar.getInstance(); + today.set(Calendar.HOUR_OF_DAY, 0); + today.set(Calendar.MINUTE, 0); + today.set(Calendar.SECOND, 0); + today.set(Calendar.MILLISECOND, 0); + + int stepsTodayMax = 0; + int stepsTodayCount = 0; + HPlusHealthActivitySample lastSample = null; + + for(HPlusHealthActivitySample sample: samples){ + if(sample.getTimestamp() >= today.getTimeInMillis() / 1000){ + + /**Strategy is: + * Calculate max steps from realtime messages + * Calculate sum of steps from day 10 minute slot summaries + */ + + if(sample.getRawKind() == HPlusDataRecord.TYPE_REALTIME) { + stepsTodayMax = Math.max(stepsTodayMax, sample.getSteps()); + }else if(sample.getRawKind() == HPlusDataRecord.TYPE_DAY_SLOT) { + stepsTodayCount += sample.getSteps(); + } + + sample.setSteps(ActivitySample.NOT_MEASURED); + lastSample = sample; + }else{ + if (sample.getRawKind() != HPlusDataRecord.TYPE_DAY_SUMMARY) { + sample.setSteps(ActivitySample.NOT_MEASURED); + } + } + } + + if(lastSample != null) + lastSample.setSteps(Math.max(stepsTodayCount, stepsTodayMax)); + + for (HPlusHealthActivityOverlay overlay : overlayRecords) { + + //Create fake events to improve activity counters if there are no events around the overlay + //timestamp boundaries + //Insert one before, one at the beginning, one at the end, and one 1s after. + insertVirtualItem(samples, Math.max(overlay.getTimestampFrom() - 1, timestamp_from), overlay.getDeviceId(), overlay.getUserId()); + insertVirtualItem(samples, Math.max(overlay.getTimestampFrom(), timestamp_from), overlay.getDeviceId(), overlay.getUserId()); + insertVirtualItem(samples, Math.min(overlay.getTimestampTo() - 1, timestamp_to - 1), overlay.getDeviceId(), overlay.getUserId()); + insertVirtualItem(samples, Math.min(overlay.getTimestampTo(), timestamp_to), overlay.getDeviceId(), overlay.getUserId()); + + for (HPlusHealthActivitySample sample : samples) { + + if (sample.getTimestamp() >= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) { + sample.setRawKind(overlay.getRawKind()); + } + } + } + + detachFromSession(); + + Collections.sort(samples, new Comparator() { + public int compare(HPlusHealthActivitySample one, HPlusHealthActivitySample other) { + return one.getTimestamp() - other.getTimestamp(); + } + }); + + return samples; + } + + private List insertVirtualItem(List samples, int timestamp, long deviceId, long userId) { + HPlusHealthActivitySample sample = new HPlusHealthActivitySample( + timestamp, // ts + deviceId, + userId, // User id + null, // Raw Data + ActivityKind.TYPE_UNKNOWN, + 1, // Intensity + ActivitySample.NOT_MEASURED, // Steps + ActivitySample.NOT_MEASURED, // HR + ActivitySample.NOT_MEASURED, // Distance + ActivitySample.NOT_MEASURED // Calories + ); + + sample.setProvider(this); + samples.add(sample); + + return samples; + } +} + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/MakibesF68Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/MakibesF68Coordinator.java new file mode 100644 index 00000000..450e00bb --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/here/MakibesF68Coordinator.java @@ -0,0 +1,39 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.here; + +/* +* @author Nicolò Balzarotti <anothersms@gmail.com> +*/ + +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.devices.here.HereCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +/** + * Pseudo Coordinator for the Makibes F68, a sub type of the HPLUS devices + */ +public class MakibesF68Coordinator extends HereCoordinator { + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + if(name != null && name.startsWith("SPORT")){ + return DeviceType.MAKIBESF68; + } + + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.MAKIBESF68; + } + + @Override + public String getManufacturer() { + return "Makibes"; + } + +} 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 5777b5d9..b337dce7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -15,6 +15,8 @@ public enum DeviceType { LIVEVIEW(30), HPLUS(40), MAKIBESF68(41), + HEREL(50), // Single device made of L + R + HERER(51), // But can be controlled independently TEST(1000); private final int key;