mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-02-02 21:17:32 +01:00
Lefun: Implement activity data sampling
This commit is contained in:
parent
6c32a3a99d
commit
d161415046
@ -43,7 +43,7 @@ public class GBDaoGenerator {
|
|||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
Schema schema = new Schema(30, MAIN_PACKAGE + ".entities");
|
Schema schema = new Schema(31, MAIN_PACKAGE + ".entities");
|
||||||
|
|
||||||
Entity userAttributes = addUserAttributes(schema);
|
Entity userAttributes = addUserAttributes(schema);
|
||||||
Entity user = addUserInfo(schema, userAttributes);
|
Entity user = addUserInfo(schema, userAttributes);
|
||||||
@ -74,6 +74,9 @@ public class GBDaoGenerator {
|
|||||||
addWatchXPlusHealthActivitySample(schema, user, device);
|
addWatchXPlusHealthActivitySample(schema, user, device);
|
||||||
addWatchXPlusHealthActivityKindOverlay(schema, user, device);
|
addWatchXPlusHealthActivityKindOverlay(schema, user, device);
|
||||||
addTLW64ActivitySample(schema, user, device);
|
addTLW64ActivitySample(schema, user, device);
|
||||||
|
addLefunActivitySample(schema, user, device);
|
||||||
|
addLefunBiometricSample(schema,user,device);
|
||||||
|
addLefunSleepSample(schema, user, device);
|
||||||
|
|
||||||
addHybridHRActivitySample(schema, user, device);
|
addHybridHRActivitySample(schema, user, device);
|
||||||
addCalendarSyncState(schema, device);
|
addCalendarSyncState(schema, device);
|
||||||
@ -404,6 +407,48 @@ public class GBDaoGenerator {
|
|||||||
return activitySample;
|
return activitySample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Entity addLefunActivitySample(Schema schema, Entity user, Entity device) {
|
||||||
|
Entity activitySample = addEntity(schema, "LefunActivitySample");
|
||||||
|
activitySample.implementsSerializable();
|
||||||
|
addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
|
||||||
|
activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||||
|
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||||
|
activitySample.addIntProperty("distance").notNull();
|
||||||
|
activitySample.addIntProperty("calories").notNull();
|
||||||
|
addHeartRateProperties(activitySample);
|
||||||
|
return activitySample;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Entity addLefunBiometricSample(Schema schema, Entity user, Entity device) {
|
||||||
|
Entity biometricSample = addEntity(schema, "LefunBiometricSample");
|
||||||
|
biometricSample.implementsSerializable();
|
||||||
|
|
||||||
|
biometricSample.addIntProperty("timestamp").notNull().primaryKey();
|
||||||
|
Property deviceId = biometricSample.addLongProperty("deviceId").primaryKey().notNull().getProperty();
|
||||||
|
biometricSample.addToOne(device, deviceId);
|
||||||
|
Property userId = biometricSample.addLongProperty("userId").notNull().getProperty();
|
||||||
|
biometricSample.addToOne(user, userId);
|
||||||
|
|
||||||
|
biometricSample.addIntProperty("type").notNull();
|
||||||
|
biometricSample.addIntProperty("value1").notNull();
|
||||||
|
biometricSample.addIntProperty("value2");
|
||||||
|
return biometricSample;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Entity addLefunSleepSample(Schema schema, Entity user, Entity device) {
|
||||||
|
Entity sleepSample = addEntity(schema, "LefunSleepSample");
|
||||||
|
sleepSample.implementsSerializable();
|
||||||
|
|
||||||
|
sleepSample.addIntProperty("timestamp").notNull().primaryKey();
|
||||||
|
Property deviceId = sleepSample.addLongProperty("deviceId").primaryKey().notNull().getProperty();
|
||||||
|
sleepSample.addToOne(device, deviceId);
|
||||||
|
Property userId = sleepSample.addLongProperty("userId").notNull().getProperty();
|
||||||
|
sleepSample.addToOne(user, userId);
|
||||||
|
|
||||||
|
sleepSample.addIntProperty("type").notNull();
|
||||||
|
return sleepSample;
|
||||||
|
}
|
||||||
|
|
||||||
private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) {
|
private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) {
|
||||||
activitySample.setSuperclass(superClass);
|
activitySample.setSuperclass(superClass);
|
||||||
activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider");
|
activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider");
|
||||||
|
@ -73,4 +73,24 @@ public class LefunConstants {
|
|||||||
public static final int PPG_TYPE_HEART_RATE = 0;
|
public static final int PPG_TYPE_HEART_RATE = 0;
|
||||||
public static final int PPG_TYPE_BLOOD_PRESSURE = 1;
|
public static final int PPG_TYPE_BLOOD_PRESSURE = 1;
|
||||||
public static final int PPG_TYPE_BLOOD_OXYGEN = 2;
|
public static final int PPG_TYPE_BLOOD_OXYGEN = 2;
|
||||||
|
public static final int PPG_TYPE_COUNT = 3;
|
||||||
|
|
||||||
|
// Extended DB types
|
||||||
|
public static final int DB_SAMPLE_TYPE_PPG = 1;
|
||||||
|
public static final int DB_SAMPLE_TYPE_SLEEP_DAY = 2;
|
||||||
|
public static final int DB_SAMPLE_TYPE_SLEEP_PERIODS = 3;
|
||||||
|
|
||||||
|
// DB activity kinds
|
||||||
|
public static final int DB_ACTIVITY_KIND_UNKNOWN = 0;
|
||||||
|
public static final int DB_ACTIVITY_KIND_ACTIVITY = 1;
|
||||||
|
public static final int DB_ACTIVITY_KIND_HEART_RATE = 2;
|
||||||
|
public static final int DB_ACTIVITY_KIND_LIGHT_SLEEP = 3;
|
||||||
|
public static final int DB_ACTIVITY_KIND_DEEP_SLEEP = 4;
|
||||||
|
|
||||||
|
// Pseudo-intensity
|
||||||
|
public static final int INTENSITY_MIN = 0;
|
||||||
|
public static final int INTENSITY_DEEP_SLEEP = 1;
|
||||||
|
public static final int INTENSITY_LIGHT_SLEEP = 2;
|
||||||
|
public static final int INTENSITY_AWAKE = 3;
|
||||||
|
public static final int INTENSITY_MAX = 4;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ public class LefunDeviceCoordinator extends AbstractDeviceCoordinator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||||
return null;
|
return new LefunSampleProvider(device, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti
|
||||||
|
Copyright (C) 2020 Yukai Li
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.lefun;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
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.AbstractActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySampleDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||||
|
|
||||||
|
public class LefunSampleProvider extends AbstractSampleProvider<LefunActivitySample> {
|
||||||
|
public LefunSampleProvider(GBDevice device, DaoSession session) {
|
||||||
|
super(device, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractDao<LefunActivitySample, ?> getSampleDao() {
|
||||||
|
return getSession().getLefunActivitySampleDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected Property getRawKindSampleProperty() {
|
||||||
|
return LefunActivitySampleDao.Properties.RawKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getTimestampSampleProperty() {
|
||||||
|
return LefunActivitySampleDao.Properties.Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getDeviceIdentifierSampleProperty() {
|
||||||
|
return LefunActivitySampleDao.Properties.DeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int normalizeType(int rawType) {
|
||||||
|
switch (rawType) {
|
||||||
|
case LefunConstants.DB_ACTIVITY_KIND_ACTIVITY:
|
||||||
|
case LefunConstants.DB_ACTIVITY_KIND_HEART_RATE:
|
||||||
|
return ActivityKind.TYPE_ACTIVITY;
|
||||||
|
case LefunConstants.DB_ACTIVITY_KIND_LIGHT_SLEEP:
|
||||||
|
return ActivityKind.TYPE_LIGHT_SLEEP;
|
||||||
|
case LefunConstants.DB_ACTIVITY_KIND_DEEP_SLEEP:
|
||||||
|
return ActivityKind.TYPE_DEEP_SLEEP;
|
||||||
|
default:
|
||||||
|
return ActivityKind.TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int toRawActivityKind(int activityKind) {
|
||||||
|
switch (activityKind) {
|
||||||
|
case ActivityKind.TYPE_ACTIVITY:
|
||||||
|
return LefunConstants.DB_ACTIVITY_KIND_ACTIVITY;
|
||||||
|
case ActivityKind.TYPE_LIGHT_SLEEP:
|
||||||
|
return LefunConstants.DB_ACTIVITY_KIND_LIGHT_SLEEP;
|
||||||
|
case ActivityKind.TYPE_DEEP_SLEEP:
|
||||||
|
return LefunConstants.DB_ACTIVITY_KIND_DEEP_SLEEP;
|
||||||
|
default:
|
||||||
|
return LefunConstants.DB_ACTIVITY_KIND_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float normalizeIntensity(int rawIntensity) {
|
||||||
|
return rawIntensity / (float)LefunConstants.INTENSITY_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LefunActivitySample createActivitySample() {
|
||||||
|
return new LefunActivitySample();
|
||||||
|
}
|
||||||
|
}
|
@ -28,11 +28,27 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
import de.greenrobot.dao.query.Query;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.GetActivityDataCommand;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.GetPpgDataCommand;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.GetSleepDataCommand;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySampleDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.LefunBiometricSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.LefunBiometricSampleDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.LefunSleepSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||||
@ -41,14 +57,18 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.FindDeviceRequest;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.FindDeviceRequest;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetActivityDataRequest;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetBatteryLevelRequest;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetBatteryLevelRequest;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetFirmwareInfoRequest;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetFirmwareInfoRequest;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetPpgDataRequest;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetSleepDataRequest;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.Request;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.Request;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.SetTimeRequest;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.SetTimeRequest;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
@ -57,6 +77,7 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(LefunDeviceSupport.class);
|
private static final Logger LOG = LoggerFactory.getLogger(LefunDeviceSupport.class);
|
||||||
|
|
||||||
private final List<Request> inProgressRequests = Collections.synchronizedList(new ArrayList<Request>());
|
private final List<Request> inProgressRequests = Collections.synchronizedList(new ArrayList<Request>());
|
||||||
|
private final Queue<Request> queuedRequests = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
public LefunDeviceSupport() {
|
public LefunDeviceSupport() {
|
||||||
super(LOG);
|
super(LOG);
|
||||||
@ -113,7 +134,7 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
@Override
|
@Override
|
||||||
public void onSetTime() {
|
public void onSetTime() {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = createTransactionBuilder(SetTimeRequest.class.getSimpleName());
|
TransactionBuilder builder = performInitialized(SetTimeRequest.class.getSimpleName());
|
||||||
SetTimeRequest request = new SetTimeRequest(this, builder);
|
SetTimeRequest request = new SetTimeRequest(this, builder);
|
||||||
request.perform();
|
request.perform();
|
||||||
inProgressRequests.add(request);
|
inProgressRequests.add(request);
|
||||||
@ -186,7 +207,27 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFetchRecordedData(int dataTypes) {
|
public void onFetchRecordedData(int dataTypes) {
|
||||||
|
if ((dataTypes & RecordedDataTypes.TYPE_ACTIVITY) != 0) {
|
||||||
|
for (int i = 0; i < 7; ++i) {
|
||||||
|
GetActivityDataRequest req = new GetActivityDataRequest(this);
|
||||||
|
req.setDaysAgo(i);
|
||||||
|
queuedRequests.add(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < LefunConstants.PPG_TYPE_COUNT; ++i) {
|
||||||
|
GetPpgDataRequest req = new GetPpgDataRequest(this);
|
||||||
|
req.setPpgType(i);
|
||||||
|
queuedRequests.add(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; ++i) {
|
||||||
|
GetSleepDataRequest req = new GetSleepDataRequest(this);
|
||||||
|
req.setDaysAgo(i);
|
||||||
|
queuedRequests.add(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
runNextQueuedRequest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -208,7 +249,7 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
public void onFindDevice(boolean start) {
|
public void onFindDevice(boolean start) {
|
||||||
if (start) {
|
if (start) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = createTransactionBuilder(FindDeviceRequest.class.getSimpleName());
|
TransactionBuilder builder = performInitialized(FindDeviceRequest.class.getSimpleName());
|
||||||
FindDeviceRequest request = new FindDeviceRequest(this, builder);
|
FindDeviceRequest request = new FindDeviceRequest(this, builder);
|
||||||
request.perform();
|
request.perform();
|
||||||
inProgressRequests.add(request);
|
inProgressRequests.add(request);
|
||||||
@ -296,10 +337,12 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
if (handleAsynchronousResponse(data))
|
if (handleAsynchronousResponse(data))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
logMessageContent(data);
|
||||||
LOG.error(String.format("No handler for response 0x%02x", commandId));
|
LOG.error(String.format("No handler for response 0x%02x", commandId));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logMessageContent(data);
|
||||||
LOG.error("Invalid response received");
|
LOG.error("Invalid response received");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -316,4 +359,154 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
gbDevice.setState(GBDevice.State.INITIALIZED);
|
gbDevice.setState(GBDevice.State.INITIALIZED);
|
||||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int dateToTimestamp(byte year, byte month, byte day, byte hour, byte minute, byte second) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.set(
|
||||||
|
((int) year & 0xff) + 2000,
|
||||||
|
((int) month & 0xff) - 1,
|
||||||
|
(int) day,
|
||||||
|
(int) hour,
|
||||||
|
(int) minute,
|
||||||
|
(int) second
|
||||||
|
);
|
||||||
|
return (int) (calendar.getTimeInMillis() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LefunActivitySample getActivitySample(DaoSession session, int timestamp) {
|
||||||
|
LefunActivitySampleDao dao = session.getLefunActivitySampleDao();
|
||||||
|
Long userId = DBHelper.getUser(session).getId();
|
||||||
|
Long deviceId = DBHelper.getDevice(getDevice(), session).getId();
|
||||||
|
Query<LefunActivitySample> q = dao.queryBuilder()
|
||||||
|
.where(LefunActivitySampleDao.Properties.Timestamp.eq(timestamp))
|
||||||
|
.where(LefunActivitySampleDao.Properties.DeviceId.eq(deviceId))
|
||||||
|
.where(LefunActivitySampleDao.Properties.UserId.eq(userId))
|
||||||
|
.build();
|
||||||
|
return q.unique();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleActivityData(GetActivityDataCommand command) {
|
||||||
|
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||||
|
DaoSession session = handler.getDaoSession();
|
||||||
|
int timestamp = dateToTimestamp(command.getYear(), command.getMonth(), command.getDay(),
|
||||||
|
command.getHour(), command.getMinute(), (byte) 0);
|
||||||
|
// For the most part I'm ignoring the sample provider, because it doesn't really help
|
||||||
|
// when I need to combine sample data instead of replacing
|
||||||
|
LefunActivitySample sample = getActivitySample(session, timestamp);
|
||||||
|
if (sample == null) {
|
||||||
|
sample = new LefunActivitySample(timestamp,
|
||||||
|
DBHelper.getDevice(getDevice(), session).getId());
|
||||||
|
sample.setUserId(DBHelper.getUser(session).getId());
|
||||||
|
sample.setRawKind(LefunConstants.DB_ACTIVITY_KIND_ACTIVITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.setSteps(command.getSteps());
|
||||||
|
sample.setDistance(command.getDistance());
|
||||||
|
sample.setCalories(command.getCalories());
|
||||||
|
sample.setRawIntensity(LefunConstants.INTENSITY_AWAKE);
|
||||||
|
|
||||||
|
session.getLefunActivitySampleDao().insertOrReplace(sample);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Error handling activity data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePpgData(GetPpgDataCommand command) {
|
||||||
|
byte[] ppgData = command.getPpgData();
|
||||||
|
int ppgData0 = ppgData[0] & 0xff;
|
||||||
|
int ppgData1 = ppgData.length > 1 ? ppgData[1] & 0xff : 0;
|
||||||
|
|
||||||
|
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||||
|
DaoSession session = handler.getDaoSession();
|
||||||
|
int timestamp = dateToTimestamp(command.getYear(), command.getMonth(), command.getDay(),
|
||||||
|
command.getHour(), command.getMinute(), command.getSecond());
|
||||||
|
|
||||||
|
if (command.getPpgType() == LefunConstants.PPG_TYPE_HEART_RATE) {
|
||||||
|
LefunActivitySample sample = getActivitySample(session, timestamp);
|
||||||
|
if (sample == null) {
|
||||||
|
sample = new LefunActivitySample(timestamp,
|
||||||
|
DBHelper.getDevice(getDevice(), session).getId());
|
||||||
|
sample.setUserId(DBHelper.getUser(session).getId());
|
||||||
|
sample.setRawKind(LefunConstants.DB_ACTIVITY_KIND_HEART_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.setHeartRate(ppgData0);
|
||||||
|
|
||||||
|
session.getLefunActivitySampleDao().insertOrReplace(sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
LefunBiometricSample bioSample = new LefunBiometricSample(timestamp,
|
||||||
|
DBHelper.getDevice(getDevice(), session).getId());
|
||||||
|
bioSample.setUserId(DBHelper.getUser(session).getId());
|
||||||
|
bioSample.setType(command.getPpgType());
|
||||||
|
bioSample.setValue1(ppgData0);
|
||||||
|
bioSample.setValue2(ppgData1);
|
||||||
|
session.getLefunBiometricSampleDao().insertOrReplace(bioSample);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Error handling PPG data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleSleepData(GetSleepDataCommand command) {
|
||||||
|
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||||
|
DaoSession session = handler.getDaoSession();
|
||||||
|
int timestamp = dateToTimestamp(command.getYear(), command.getMonth(), command.getDay(),
|
||||||
|
command.getHour(), command.getMinute(), (byte) 0);
|
||||||
|
|
||||||
|
LefunActivitySample sample = getActivitySample(session, timestamp);
|
||||||
|
if (sample == null) {
|
||||||
|
sample = new LefunActivitySample(timestamp,
|
||||||
|
DBHelper.getDevice(getDevice(), session).getId());
|
||||||
|
sample.setUserId(DBHelper.getUser(session).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
int rawKind;
|
||||||
|
int intensity;
|
||||||
|
switch (command.getSleepType()) {
|
||||||
|
case GetSleepDataCommand.SLEEP_TYPE_AWAKE:
|
||||||
|
rawKind = LefunConstants.DB_ACTIVITY_KIND_ACTIVITY;
|
||||||
|
intensity = LefunConstants.INTENSITY_AWAKE;
|
||||||
|
break;
|
||||||
|
case GetSleepDataCommand.SLEEP_TYPE_LIGHT_SLEEP:
|
||||||
|
rawKind = LefunConstants.DB_ACTIVITY_KIND_LIGHT_SLEEP;
|
||||||
|
intensity = LefunConstants.INTENSITY_LIGHT_SLEEP;
|
||||||
|
break;
|
||||||
|
case GetSleepDataCommand.SLEEP_TYPE_DEEP_SLEEP:
|
||||||
|
rawKind = LefunConstants.DB_ACTIVITY_KIND_DEEP_SLEEP;
|
||||||
|
intensity = LefunConstants.INTENSITY_DEEP_SLEEP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rawKind = LefunConstants.DB_ACTIVITY_KIND_UNKNOWN;
|
||||||
|
intensity = LefunConstants.INTENSITY_AWAKE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.setRawKind(rawKind);
|
||||||
|
sample.setRawIntensity(intensity);
|
||||||
|
|
||||||
|
session.getLefunActivitySampleDao().insertOrReplace(sample);
|
||||||
|
|
||||||
|
LefunSleepSample sleepSample = new LefunSleepSample(timestamp,
|
||||||
|
DBHelper.getDevice(getDevice(), session).getId());
|
||||||
|
sleepSample.setUserId(DBHelper.getUser(session).getId());
|
||||||
|
sleepSample.setType(command.getSleepType());
|
||||||
|
session.getLefunSleepSampleDao().insertOrReplace(sleepSample);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Error handling sleep data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runNextQueuedRequest() {
|
||||||
|
Request request = queuedRequests.poll();
|
||||||
|
if (request != null) {
|
||||||
|
try {
|
||||||
|
request.perform();
|
||||||
|
if (!request.isSelfQueue())
|
||||||
|
performConnected(request.getTransactionBuilder().getTransaction());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GB.toast(getContext(), "Failed to run next queued request", Toast.LENGTH_SHORT,
|
||||||
|
GB.ERROR, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti
|
||||||
|
Copyright (C) 2020 Yukai Li
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.GetActivityDataCommand;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSupport;
|
||||||
|
|
||||||
|
public class GetActivityDataRequest extends MultiFetchRequest {
|
||||||
|
private int daysAgo;
|
||||||
|
|
||||||
|
public GetActivityDataRequest(LefunDeviceSupport support) {
|
||||||
|
super(support);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDaysAgo() {
|
||||||
|
return daysAgo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDaysAgo(int daysAgo) {
|
||||||
|
this.daysAgo = daysAgo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] createRequest() {
|
||||||
|
GetActivityDataCommand cmd = new GetActivityDataCommand();
|
||||||
|
cmd.setDaysAgo((byte) daysAgo);
|
||||||
|
return cmd.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleResponse(byte[] data) {
|
||||||
|
GetActivityDataCommand cmd = new GetActivityDataCommand();
|
||||||
|
cmd.deserialize(data);
|
||||||
|
|
||||||
|
if (daysAgo != (cmd.getDaysAgo() & 0xff)) {
|
||||||
|
throw new IllegalArgumentException("Mismatching days ago");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalRecords == -1) {
|
||||||
|
totalRecords = cmd.getTotalRecords() & 0xff;
|
||||||
|
} else if (totalRecords != (cmd.getTotalRecords() & 0xff)) {
|
||||||
|
throw new IllegalArgumentException("Total records mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalRecords != 0) {
|
||||||
|
int currentRecord = cmd.getCurrentRecord() & 0xff;
|
||||||
|
if (lastRecord + 1 != currentRecord) {
|
||||||
|
throw new IllegalArgumentException("Records received out of sequence");
|
||||||
|
}
|
||||||
|
lastRecord = currentRecord;
|
||||||
|
|
||||||
|
getSupport().handleActivityData(cmd);
|
||||||
|
} else {
|
||||||
|
lastRecord = totalRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastRecord == totalRecords)
|
||||||
|
operationFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCommandId() {
|
||||||
|
return LefunConstants.CMD_ACTIVITY_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getOperationName() {
|
||||||
|
return "Getting activity data";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti
|
||||||
|
Copyright (C) 2020 Yukai Li
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.GetPpgDataCommand;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSupport;
|
||||||
|
|
||||||
|
public class GetPpgDataRequest extends MultiFetchRequest {
|
||||||
|
private int ppgType;
|
||||||
|
|
||||||
|
public GetPpgDataRequest(LefunDeviceSupport support) {
|
||||||
|
super(support);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPpgType() {
|
||||||
|
return ppgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPpgType(int ppgType) {
|
||||||
|
this.ppgType = ppgType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] createRequest() {
|
||||||
|
GetPpgDataCommand cmd = new GetPpgDataCommand();
|
||||||
|
cmd.setPpgType(ppgType);
|
||||||
|
return cmd.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleResponse(byte[] data) {
|
||||||
|
GetPpgDataCommand cmd = new GetPpgDataCommand();
|
||||||
|
cmd.deserialize(data);
|
||||||
|
|
||||||
|
if (cmd.getPpgType() != ppgType) {
|
||||||
|
throw new IllegalArgumentException("Mismatching PPG type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalRecords == -1) {
|
||||||
|
totalRecords = cmd.getTotalRecords() & 0xffff;
|
||||||
|
} else if (totalRecords != (cmd.getTotalRecords() & 0xffff)) {
|
||||||
|
throw new IllegalArgumentException("Total records mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalRecords != 0) {
|
||||||
|
int currentRecord = cmd.getCurrentRecord() & 0xffff;
|
||||||
|
if (lastRecord + 1 != currentRecord) {
|
||||||
|
throw new IllegalArgumentException("Records received out of sequence");
|
||||||
|
}
|
||||||
|
lastRecord = currentRecord;
|
||||||
|
|
||||||
|
getSupport().handlePpgData(cmd);
|
||||||
|
} else {
|
||||||
|
lastRecord = totalRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastRecord == totalRecords)
|
||||||
|
operationFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCommandId() {
|
||||||
|
return LefunConstants.CMD_PPG_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getOperationName() {
|
||||||
|
return "Getting PPG data";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti
|
||||||
|
Copyright (C) 2020 Yukai Li
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.GetSleepDataCommand;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSupport;
|
||||||
|
|
||||||
|
public class GetSleepDataRequest extends MultiFetchRequest {
|
||||||
|
private int daysAgo;
|
||||||
|
|
||||||
|
public GetSleepDataRequest(LefunDeviceSupport support) {
|
||||||
|
super(support);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDaysAgo() {
|
||||||
|
return daysAgo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDaysAgo(int daysAgo) {
|
||||||
|
this.daysAgo = daysAgo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] createRequest() {
|
||||||
|
GetSleepDataCommand cmd = new GetSleepDataCommand();
|
||||||
|
cmd.setDaysAgo((byte) daysAgo);
|
||||||
|
return cmd.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleResponse(byte[] data) {
|
||||||
|
GetSleepDataCommand cmd = new GetSleepDataCommand();
|
||||||
|
cmd.deserialize(data);
|
||||||
|
|
||||||
|
if (daysAgo != (cmd.getDaysAgo() & 0xff)) {
|
||||||
|
throw new IllegalArgumentException("Mismatching days ago");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalRecords == -1) {
|
||||||
|
totalRecords = cmd.getTotalRecords() & 0xff;
|
||||||
|
} else if (totalRecords != (cmd.getTotalRecords() & 0xff)) {
|
||||||
|
throw new IllegalArgumentException("Total records mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalRecords != 0) {
|
||||||
|
int currentRecord = cmd.getCurrentRecord() & 0xff;
|
||||||
|
if (lastRecord + 1 != currentRecord) {
|
||||||
|
throw new IllegalArgumentException("Records received out of sequence");
|
||||||
|
}
|
||||||
|
lastRecord = currentRecord;
|
||||||
|
|
||||||
|
getSupport().handleSleepData(cmd);
|
||||||
|
} else {
|
||||||
|
lastRecord = totalRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastRecord == totalRecords)
|
||||||
|
operationFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCommandId() {
|
||||||
|
return LefunConstants.CMD_SLEEP_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getOperationName() {
|
||||||
|
return "Getting sleep data";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti
|
||||||
|
Copyright (C) 2020 Yukai Li
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSupport;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
|
public abstract class MultiFetchRequest extends Request {
|
||||||
|
protected MultiFetchRequest(LefunDeviceSupport support) {
|
||||||
|
super(support, null);
|
||||||
|
removeAfterHandling = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int lastRecord = 0;
|
||||||
|
protected int totalRecords = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prePerform() throws IOException {
|
||||||
|
super.prePerform();
|
||||||
|
builder = performInitialized(getClass().getSimpleName());
|
||||||
|
if (getDevice().isBusy()) {
|
||||||
|
throw new IllegalStateException("Device is busy");
|
||||||
|
}
|
||||||
|
builder.add(new SetDeviceBusyAction(getDevice(), getOperationName(), getContext()));
|
||||||
|
builder.wait(1000); // Wait a bit (after previous operation), or device sometimes won't respond
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void operationFinished() {
|
||||||
|
if (lastRecord == totalRecords)
|
||||||
|
removeAfterHandling = true;
|
||||||
|
try {
|
||||||
|
super.operationFinished();
|
||||||
|
TransactionBuilder builder = performInitialized("Finishing operation");
|
||||||
|
builder.setGattCallback(null);
|
||||||
|
builder.queue(getQueue());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GB.toast(getContext(), "Failed to reset callback", Toast.LENGTH_SHORT,
|
||||||
|
GB.ERROR, e);
|
||||||
|
}
|
||||||
|
unsetBusy();
|
||||||
|
operationStatus = OperationStatus.FINISHED;
|
||||||
|
getSupport().runNextQueuedRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
|
if (characteristic.getUuid().equals(LefunConstants.UUID_CHARACTERISTIC_LEFUN_NOTIFY)) {
|
||||||
|
byte[] data = characteristic.getValue();
|
||||||
|
// Parse response
|
||||||
|
if (data.length >= LefunConstants.CMD_HEADER_LENGTH && data[0] == LefunConstants.CMD_RESPONSE_ID) {
|
||||||
|
try {
|
||||||
|
handleResponse(data);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log("Failed to handle response");
|
||||||
|
operationFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSupport().logMessageContent(data);
|
||||||
|
log("Invalid response received");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onCharacteristicChanged(gatt, characteristic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSelfQueue() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getOperationName();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user