mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
+5
-5
@@ -337,7 +337,7 @@ public class Workout {
|
||||
}
|
||||
|
||||
public static class WorkoutData {
|
||||
public static final int id = 0x0a;
|
||||
public static final byte id = 0x0a;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
@@ -712,7 +712,7 @@ public class Workout {
|
||||
}
|
||||
|
||||
public static class WorkoutPace {
|
||||
public static final int id = 0x0c;
|
||||
public static final byte id = 0x0c;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
@@ -800,7 +800,7 @@ public class Workout {
|
||||
}
|
||||
|
||||
public static class WorkoutSwimSegments {
|
||||
public static final int id = 0x0e;
|
||||
public static final byte id = 0x0e;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
@@ -901,7 +901,7 @@ public class Workout {
|
||||
}
|
||||
|
||||
public static class WorkoutSpO2 {
|
||||
public static final int id = 0x14;
|
||||
public static final byte id = 0x14;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
@@ -1013,7 +1013,7 @@ public class Workout {
|
||||
}
|
||||
|
||||
public static class WorkoutSections {
|
||||
public static final int id = 0x16;
|
||||
public static final byte id = 0x16;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
|
||||
+16
-412
@@ -92,8 +92,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.ui.HuaweiStressCalibrationFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSampleDao;
|
||||
@@ -102,16 +100,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStageSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStatsSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiStressSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSectionsSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSectionsSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSpO2Sample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSpO2SampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummaryAdditionalValuesSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummaryAdditionalValuesSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSwimSegmentsSample;
|
||||
@@ -170,7 +158,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetO
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSmartAlarmList;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWatchfaceParams;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWorkoutCapability;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWorkoutTotalsRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendCameraRemoteSetupEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendCountryCodeRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendDeviceReportThreshold;
|
||||
@@ -204,7 +191,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetF
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetHiChainRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSleepDataCountRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetStepDataCountRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWorkoutCountRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotificationRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetMusicRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.AlarmsRequest;
|
||||
@@ -310,6 +296,8 @@ public class HuaweiSupportProvider {
|
||||
|
||||
protected HuaweiDataSyncManager huaweiDataSyncManager = new HuaweiDataSyncManager(this);
|
||||
|
||||
protected HuaweiWorkoutSyncManager huaweiWorkoutSyncManager = new HuaweiWorkoutSyncManager(this);
|
||||
|
||||
private HuaweiDataSyncFeatureManager huaweiDataSyncFeatureManager = null;
|
||||
|
||||
private HuaweiDataSyncNotificationPictures huaweiDataSyncNotificationPictures = null;
|
||||
@@ -1653,80 +1641,25 @@ public class HuaweiSupportProvider {
|
||||
if (!syncState.startWorkoutSync())
|
||||
return;
|
||||
|
||||
int start = 0;
|
||||
int end = (int) (System.currentTimeMillis() / 1000);
|
||||
|
||||
SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress());
|
||||
long prefLastSyncTime = sharedPreferences.getLong("lastSportsActivityTimeMillis", 0);
|
||||
if (prefLastSyncTime != 0) {
|
||||
start = (int) (prefLastSyncTime / 1000);
|
||||
|
||||
// Reset for next calls
|
||||
sharedPreferences.edit().putLong("lastSportsActivityTimeMillis", 0).apply();
|
||||
} else {
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId();
|
||||
|
||||
QueryBuilder<HuaweiWorkoutSummarySample> qb1 = db.getDaoSession().getHuaweiWorkoutSummarySampleDao().queryBuilder().where(
|
||||
HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.UserId.eq(userId)
|
||||
).orderDesc(
|
||||
HuaweiWorkoutSummarySampleDao.Properties.StartTimestamp
|
||||
).limit(1);
|
||||
|
||||
List<HuaweiWorkoutSummarySample> samples1 = qb1.list();
|
||||
if (!samples1.isEmpty())
|
||||
start = samples1.get(0).getEndTimestamp();
|
||||
|
||||
QueryBuilder<BaseActivitySummary> qb2 = db.getDaoSession().getBaseActivitySummaryDao().queryBuilder().where(
|
||||
BaseActivitySummaryDao.Properties.DeviceId.eq(deviceId),
|
||||
BaseActivitySummaryDao.Properties.UserId.eq(userId)
|
||||
).orderDesc(
|
||||
BaseActivitySummaryDao.Properties.StartTime
|
||||
).limit(1);
|
||||
|
||||
List<BaseActivitySummary> samples2 = qb2.list();
|
||||
if (!samples2.isEmpty())
|
||||
start = Math.min(start, (int) (samples2.get(0).getEndTime().getTime() / 1000L));
|
||||
|
||||
start = start + 1;
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Exception for getting start time, using 10/06/2022 - 00:00:00.");
|
||||
huaweiWorkoutSyncManager.performSync(new HuaweiWorkoutSyncManager.WorkoutSyncCallback() {
|
||||
@Override
|
||||
public void syncComplete() {
|
||||
LOG.info("Workout sync complete!");
|
||||
syncState.stopWorkoutSync();
|
||||
}
|
||||
|
||||
if (start == 0 || start == 1)
|
||||
start = 1654819200;
|
||||
}
|
||||
|
||||
final GetWorkoutCountRequest getWorkoutCountRequest;
|
||||
if (isBLE()) {
|
||||
nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder leBuilder = createLeTransactionBuilder("FetchWorkoutData");
|
||||
leBuilder.setBusyTask(R.string.busy_task_fetch_activity_data);
|
||||
getWorkoutCountRequest = new GetWorkoutCountRequest(this, leBuilder, start, end);
|
||||
} else {
|
||||
nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder brBuilder = createBrTransactionBuilder("FetchWorkoutData");
|
||||
brBuilder.setBusyTask(R.string.busy_task_fetch_activity_data);
|
||||
getWorkoutCountRequest = new GetWorkoutCountRequest(this, brBuilder, start, end);
|
||||
}
|
||||
|
||||
getWorkoutCountRequest.setFinalizeReq(new RequestCallback() {
|
||||
@Override
|
||||
public void handleException(Request.ResponseParseException e) {
|
||||
// This is propagated through the workout requests, hence the slightly generic error message
|
||||
GB.toast(context, "Exception synchronizing workout", Toast.LENGTH_SHORT, GB.ERROR);
|
||||
LOG.error("Exception synchronizing workout", e);
|
||||
endOfWorkoutSync();
|
||||
public void handleTimeout() {
|
||||
LOG.error("Workout sync timeout!");
|
||||
syncState.stopWorkoutSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleException() {
|
||||
LOG.error("Workout sync exception!");
|
||||
syncState.stopWorkoutSync();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
getWorkoutCountRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
GB.toast(context, "Exception synchronizing workout", Toast.LENGTH_SHORT, GB.ERROR);
|
||||
LOG.error("Error sending workout count - showing user that the workout sync failed", e);
|
||||
endOfWorkoutSync();
|
||||
}
|
||||
}
|
||||
|
||||
public void onReset(int flags) {
|
||||
@@ -2011,312 +1944,6 @@ public class HuaweiSupportProvider {
|
||||
// TODO: potentially do more with this, maybe through realtime data?
|
||||
}
|
||||
|
||||
public Long addWorkoutTotalsData(Workout.WorkoutTotals.Response packet) {
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId();
|
||||
|
||||
// Avoid duplicates
|
||||
QueryBuilder<HuaweiWorkoutSummarySample> qb = db.getDaoSession().getHuaweiWorkoutSummarySampleDao().queryBuilder().where(
|
||||
HuaweiWorkoutSummarySampleDao.Properties.UserId.eq(userId),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.WorkoutNumber.eq(packet.number),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.StartTimestamp.eq(packet.startTime),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.EndTimestamp.eq(packet.endTime)
|
||||
);
|
||||
List<HuaweiWorkoutSummarySample> results = qb.build().list();
|
||||
Long workoutId = null;
|
||||
if (!results.isEmpty())
|
||||
workoutId = results.get(0).getWorkoutId();
|
||||
|
||||
byte[] raw;
|
||||
if (packet.rawData == null)
|
||||
raw = null;
|
||||
else
|
||||
raw = StringUtils.bytesToHex(packet.rawData).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
byte[] recoveryHeartRates;
|
||||
if (packet.recoveryHeartRates == null)
|
||||
recoveryHeartRates = null;
|
||||
else
|
||||
recoveryHeartRates = StringUtils.bytesToHex(packet.recoveryHeartRates).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
HuaweiWorkoutSummarySample summarySample = new HuaweiWorkoutSummarySample(
|
||||
workoutId,
|
||||
deviceId,
|
||||
userId,
|
||||
packet.number,
|
||||
packet.status,
|
||||
packet.startTime,
|
||||
packet.endTime,
|
||||
packet.calories,
|
||||
packet.distance,
|
||||
packet.stepCount,
|
||||
packet.totalTime,
|
||||
packet.duration,
|
||||
packet.type,
|
||||
packet.strokes,
|
||||
packet.avgStrokeRate,
|
||||
packet.poolLength,
|
||||
packet.laps,
|
||||
packet.avgSwolf,
|
||||
raw,
|
||||
null,
|
||||
packet.maxAltitude,
|
||||
packet.minAltitude,
|
||||
packet.elevationGain,
|
||||
packet.elevationLoss,
|
||||
packet.workoutLoad,
|
||||
packet.workoutAerobicEffect,
|
||||
packet.workoutAnaerobicEffect,
|
||||
packet.recoveryTime,
|
||||
packet.minHeartRatePeak,
|
||||
packet.maxHeartRatePeak,
|
||||
recoveryHeartRates,
|
||||
packet.swimType,
|
||||
packet.maxMET,
|
||||
packet.hrZoneType,
|
||||
packet.runPaceZone1Min,
|
||||
packet.runPaceZone2Min,
|
||||
packet.runPaceZone3Min,
|
||||
packet.runPaceZone4Min,
|
||||
packet.runPaceZone5Min,
|
||||
packet.runPaceZone5Max,
|
||||
packet.runPaceZone1Time,
|
||||
packet.runPaceZone2Time,
|
||||
packet.runPaceZone3Time,
|
||||
packet.runPaceZone4Time,
|
||||
packet.runPaceZone5Time,
|
||||
packet.algType,
|
||||
packet.trainingPoints,
|
||||
packet.longestStreak,
|
||||
packet.tripped,
|
||||
getDeviceState().isSupportsWorkoutNewSteps(),
|
||||
null
|
||||
);
|
||||
|
||||
db.getDaoSession().getHuaweiWorkoutSummarySampleDao().insertOrReplace(summarySample);
|
||||
|
||||
// We should completely replace values. Delete all and insert again.
|
||||
final DeleteQuery<HuaweiWorkoutSummaryAdditionalValuesSample> tableDeleteQuery = db.getDaoSession().getHuaweiWorkoutSummaryAdditionalValuesSampleDao().queryBuilder()
|
||||
.where(HuaweiWorkoutSummaryAdditionalValuesSampleDao.Properties.WorkoutId.eq(summarySample.getWorkoutId()))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
|
||||
for (Map.Entry<String, String> entry : packet.additionalValues.entrySet()) {
|
||||
HuaweiWorkoutSummaryAdditionalValuesSample summarySampleAdditionalValue = new HuaweiWorkoutSummaryAdditionalValuesSample(summarySample.getWorkoutId(), entry.getKey(), entry.getValue());
|
||||
db.getDaoSession().getHuaweiWorkoutSummaryAdditionalValuesSampleDao().insertOrReplace(summarySampleAdditionalValue);
|
||||
}
|
||||
|
||||
return summarySample.getWorkoutId();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout totals data to database", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutSampleData(Long workoutId, List<Workout.WorkoutData.Response.Data> dataList) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutDataSampleDao dao = db.getDaoSession().getHuaweiWorkoutDataSampleDao();
|
||||
|
||||
for (Workout.WorkoutData.Response.Data data : dataList) {
|
||||
byte[] unknown;
|
||||
if (data.unknownData == null)
|
||||
unknown = null;
|
||||
else
|
||||
unknown = StringUtils.bytesToHex(data.unknownData).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
HuaweiWorkoutDataSample dataSample = new HuaweiWorkoutDataSample(
|
||||
workoutId,
|
||||
data.timestamp,
|
||||
data.heartRate,
|
||||
data.speed,
|
||||
data.stepRate,
|
||||
data.cadence,
|
||||
data.stepLength,
|
||||
data.groundContactTime,
|
||||
data.impact,
|
||||
data.swingAngle,
|
||||
data.foreFootLanding,
|
||||
data.midFootLanding,
|
||||
data.backFootLanding,
|
||||
data.eversionAngle,
|
||||
data.swolf,
|
||||
data.strokeRate,
|
||||
unknown,
|
||||
data.calories,
|
||||
data.cyclingPower,
|
||||
data.frequency,
|
||||
data.altitude,
|
||||
data.hangTime,
|
||||
data.impactHangRate,
|
||||
data.rideCadence,
|
||||
data.ap,
|
||||
data.vo,
|
||||
data.gtb,
|
||||
data.vr,
|
||||
data.ceiling,
|
||||
data.temp,
|
||||
data.spo2,
|
||||
data.cns
|
||||
);
|
||||
dao.insertOrReplace(dataSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutPaceData(Long workoutId, List<Workout.WorkoutPace.Response.Block> paceList, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutPaceSampleDao dao = db.getDaoSession().getHuaweiWorkoutPaceSampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutPaceSample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutPaceSampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
int paceIndex = (int) dao.queryBuilder().where(HuaweiWorkoutPaceSampleDao.Properties.WorkoutId.eq(workoutId)).count();
|
||||
for (Workout.WorkoutPace.Response.Block block : paceList) {
|
||||
|
||||
Integer correction = block.hasCorrection ? (int) block.correction : null;
|
||||
HuaweiWorkoutPaceSample paceSample = new HuaweiWorkoutPaceSample(
|
||||
workoutId,
|
||||
paceIndex++,
|
||||
block.distance,
|
||||
block.type,
|
||||
block.pace,
|
||||
block.pointIndex,
|
||||
correction
|
||||
);
|
||||
dao.insertOrReplace(paceSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout pace data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addWorkoutSwimSegmentsData(Long workoutId, List<Workout.WorkoutSwimSegments.Response.Block> paceList, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutSwimSegmentsSampleDao dao = db.getDaoSession().getHuaweiWorkoutSwimSegmentsSampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutSwimSegmentsSample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
int paceIndex = (int) dao.queryBuilder().where(HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(workoutId)).count();
|
||||
for (Workout.WorkoutSwimSegments.Response.Block block : paceList) {
|
||||
HuaweiWorkoutSwimSegmentsSample swimSectionSample = new HuaweiWorkoutSwimSegmentsSample(
|
||||
workoutId,
|
||||
paceIndex++,
|
||||
block.distance,
|
||||
block.type,
|
||||
block.pace,
|
||||
block.pointIndex,
|
||||
block.segment,
|
||||
block.swimType,
|
||||
block.strokes,
|
||||
block.avgSwolf,
|
||||
block.time
|
||||
);
|
||||
dao.insertOrReplace(swimSectionSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout swim section data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutSpO2Data(Long workoutId, List<Workout.WorkoutSpO2.Response.Block> spO2List, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutSpO2SampleDao dao = db.getDaoSession().getHuaweiWorkoutSpO2SampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutSpO2Sample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutSpO2SampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
for (Workout.WorkoutSpO2.Response.Block block : spO2List) {
|
||||
HuaweiWorkoutSpO2Sample spO2Sample = new HuaweiWorkoutSpO2Sample(
|
||||
workoutId,
|
||||
block.interval,
|
||||
block.value
|
||||
);
|
||||
dao.insertOrReplace(spO2Sample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout SpO2 data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutSectionsData(Long workoutId, List<Workout.WorkoutSections.Response.Block> spO2List, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
// NOTE: All fields of this data is optional. At this point I don't all workouts that this data used.
|
||||
// I decided to add two additional fields dataIdx and rowIdx as primary keys that should identify each row
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutSectionsSampleDao dao = db.getDaoSession().getHuaweiWorkoutSectionsSampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutSectionsSample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutSectionsSampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (Workout.WorkoutSections.Response.Block block : spO2List) {
|
||||
HuaweiWorkoutSectionsSample huaweiWorkoutSectionsSample = new HuaweiWorkoutSectionsSample(
|
||||
workoutId,
|
||||
number,
|
||||
i++,
|
||||
block.num,
|
||||
block.time,
|
||||
block.distance,
|
||||
block.pace,
|
||||
block.heartRate,
|
||||
block.cadence,
|
||||
block.stepLength,
|
||||
block.totalRise,
|
||||
block.totalDescend,
|
||||
block.groundContactTime,
|
||||
block.groundImpact,
|
||||
block.swingAngle,
|
||||
block.eversion,
|
||||
block.avgCadence,
|
||||
block.divingUnderwaterTime,
|
||||
block.divingMaxDepth,
|
||||
block.divingUnderwaterTime,
|
||||
block.divingBreakTime
|
||||
);
|
||||
dao.insertOrReplace(huaweiWorkoutSectionsSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout sections data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWearLocation() {
|
||||
try {
|
||||
SetWearLocationRequest setWearLocationReq = new SetWearLocationRequest(this);
|
||||
@@ -3245,29 +2872,6 @@ public class HuaweiSupportProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void nextWorkoutSync(List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder, RequestCallback finalizeReq) {
|
||||
if (!remainder.isEmpty()) {
|
||||
GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest(
|
||||
this,
|
||||
remainder.remove(0),
|
||||
remainder
|
||||
);
|
||||
nextRequest.setFinalizeReq(finalizeReq);
|
||||
// Cannot do this with nextRequest because it's in a callback
|
||||
try {
|
||||
nextRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
finalizeReq.handleException(new Request.ResponseParseException("Cannot send next request", e));
|
||||
}
|
||||
} else {
|
||||
this.endOfWorkoutSync();
|
||||
}
|
||||
}
|
||||
|
||||
public void endOfWorkoutSync() {
|
||||
this.syncState.stopWorkoutSync();
|
||||
}
|
||||
|
||||
public void downloadWorkoutPdrFiles(short workoutId, Long databaseId) {
|
||||
if(!getDeviceState().isSupportsGpsNewSync())
|
||||
return;
|
||||
|
||||
+2
-1
@@ -6,12 +6,12 @@ import org.slf4j.LoggerFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
class HuaweiSyncState {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HuaweiSyncState.class);
|
||||
|
||||
private final HuaweiSupportProvider supportProvider;
|
||||
private final List<Integer> syncQueue = new ArrayList<>(2);
|
||||
|
||||
@@ -134,6 +134,7 @@ class HuaweiSyncState {
|
||||
return false;
|
||||
}
|
||||
this.workoutSync = true;
|
||||
this.supportProvider.getDevice().setBusyTask(R.string.busy_task_fetch_activity_data, supportProvider.getContext());
|
||||
}
|
||||
LOG.debug("Set workout sync state to true");
|
||||
return true;
|
||||
|
||||
+979
@@ -0,0 +1,979 @@
|
||||
/* Copyright (C) 2026 Martin.JM
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import de.greenrobot.dao.query.DeleteQuery;
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSectionsSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSectionsSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSpO2Sample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSpO2SampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummaryAdditionalValuesSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummaryAdditionalValuesSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSwimSegmentsSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSwimSegmentsSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.RequestBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
public class HuaweiWorkoutSyncManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HuaweiWorkoutSyncManager.class);
|
||||
|
||||
/*
|
||||
* TODO: We add the workout totals first, if at any point after that a sync fails, the workout
|
||||
* will not be synchronised again automatically. Might want to fix that.
|
||||
*/
|
||||
|
||||
/*
|
||||
* General dataflow in this class:
|
||||
* - Sync function sends a request to the device
|
||||
* - Response gets into handle function, which calls the next sync function
|
||||
*/
|
||||
|
||||
private record WorkoutSyncRequest(int start, int end) { }
|
||||
|
||||
public interface WorkoutSyncCallback {
|
||||
void syncComplete();
|
||||
void handleTimeout();
|
||||
void handleException();
|
||||
}
|
||||
|
||||
private final HuaweiSupportProvider supportProvider;
|
||||
private final ArrayList<WorkoutSyncRequest> syncRequests;
|
||||
private WorkoutSyncRequest currentSyncRequest;
|
||||
/// Used for the callback, but also to see if there's still a sync running
|
||||
private WorkoutSyncCallback callback = null;
|
||||
|
||||
private List<Workout.WorkoutCount.Response.WorkoutNumbers> workoutNumbersList;
|
||||
private Workout.WorkoutCount.Response.WorkoutNumbers currentWorkoutNumbers;
|
||||
|
||||
private Long currentDatabaseId;
|
||||
private int currentNumber; // Used for data, pace, etc...
|
||||
|
||||
public HuaweiWorkoutSyncManager(HuaweiSupportProvider supportProvider) {
|
||||
this.supportProvider = supportProvider;
|
||||
this.syncRequests = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void performSync(WorkoutSyncCallback callback) {
|
||||
int start = getStartTimeFromDb();
|
||||
int end = (int) (System.currentTimeMillis() / 1000);
|
||||
WorkoutSyncRequest syncRequest = new WorkoutSyncRequest(start, end);
|
||||
|
||||
synchronized (supportProvider) {
|
||||
if (this.callback != null) {
|
||||
callback.handleException();
|
||||
return;
|
||||
}
|
||||
this.callback = callback;
|
||||
syncRequests.add(syncRequest);
|
||||
}
|
||||
syncNextFromQueue();
|
||||
}
|
||||
|
||||
private void resetState() {
|
||||
synchronized (this.supportProvider) {
|
||||
this.callback = null;
|
||||
|
||||
if (!this.syncRequests.isEmpty())
|
||||
LOG.error("SyncRequests is not empty yet state is reset");
|
||||
this.syncRequests.clear();
|
||||
this.workoutNumbersList = null;
|
||||
this.currentDatabaseId = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(@Nullable Exception e) {
|
||||
GB.toast("Exception synchronizing workout", Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||
this.callback.handleException();
|
||||
resetState();
|
||||
}
|
||||
|
||||
private void handleTimeout() {
|
||||
GB.toast("Timeout when synchronizing workout", Toast.LENGTH_SHORT, GB.WARN);
|
||||
this.callback.handleTimeout();
|
||||
resetState();
|
||||
}
|
||||
|
||||
private void syncNextFromQueue() {
|
||||
if (this.syncRequests.isEmpty()) {
|
||||
this.callback.syncComplete();
|
||||
resetState();
|
||||
return;
|
||||
}
|
||||
this.currentSyncRequest = this.syncRequests.remove(0);
|
||||
|
||||
Request getWorkoutCountRequest = new RequestBuilder<Workout.WorkoutCount.Response>(
|
||||
this.supportProvider,
|
||||
Workout.id,
|
||||
Workout.WorkoutCount.id,
|
||||
new Workout.WorkoutCount.Request(
|
||||
supportProvider.getParamsProvider(),
|
||||
this.currentSyncRequest.start,
|
||||
this.currentSyncRequest.end
|
||||
)
|
||||
)
|
||||
.setTimeout(5000) // Set a 5 second timeout
|
||||
.onCallback(this::handleWorkoutCount)
|
||||
.onException(e -> {
|
||||
LOG.error("Exception receiving workout count", e);
|
||||
handleException(e);
|
||||
})
|
||||
.onTimeout(packet -> {
|
||||
LOG.error("Timeout on workout count");
|
||||
handleTimeout();
|
||||
})
|
||||
.build();
|
||||
try {
|
||||
getWorkoutCountRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Exception sending workout count", e);
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWorkoutCount(Workout.WorkoutCount.Response packet) {
|
||||
if (packet.error != null) {
|
||||
LOG.warn("Error when retrieving/parsing workout count: {}", packet.error);
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
if (packet.count == 0 || packet.workoutNumbers == null) {
|
||||
this.callback.syncComplete();
|
||||
resetState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.count != packet.workoutNumbers.size()) {
|
||||
// If it errors, try to get less data - half the timeframe
|
||||
int start = this.currentSyncRequest.start;
|
||||
int end = this.currentSyncRequest.end;
|
||||
int half = start + (end - start) / 2;
|
||||
|
||||
this.syncRequests.add(new WorkoutSyncRequest(start, half));
|
||||
this.syncRequests.add(new WorkoutSyncRequest(half + 1, end));
|
||||
|
||||
syncNextFromQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
this.workoutNumbersList = packet.workoutNumbers;
|
||||
// Has to be sorted for the timestamp-based sync stat that we use
|
||||
this.workoutNumbersList.sort(Comparator.comparingInt(o -> o.workoutNumber));
|
||||
|
||||
syncNextWorkout();
|
||||
}
|
||||
|
||||
private void syncNextWorkout() {
|
||||
if (this.workoutNumbersList == null || this.workoutNumbersList.isEmpty()) {
|
||||
syncNextFromQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentWorkoutNumbers = this.workoutNumbersList.remove(0);
|
||||
|
||||
Request getWorkoutTotalsRequest = new RequestBuilder<Workout.WorkoutTotals.Response>(
|
||||
this.supportProvider,
|
||||
Workout.id,
|
||||
Workout.WorkoutTotals.id,
|
||||
new Workout.WorkoutTotals.Request(
|
||||
this.supportProvider.getParamsProvider(),
|
||||
this.currentWorkoutNumbers.workoutNumber
|
||||
)
|
||||
)
|
||||
.setTimeout(5000)
|
||||
.onCallback(this::handleWorkoutTotals)
|
||||
.onException(e -> {
|
||||
LOG.error("Exception receiving workout totals", e);
|
||||
handleException(e);
|
||||
})
|
||||
.onTimeout(packet -> {
|
||||
LOG.error("Timeout on workout totals");
|
||||
handleTimeout();
|
||||
})
|
||||
.build();
|
||||
try {
|
||||
getWorkoutTotalsRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Exception sending workout totals", e);
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWorkoutTotals(Workout.WorkoutTotals.Response packet) {
|
||||
if (packet.error != null) {
|
||||
LOG.error("Error {} occurred during workout totals sync", packet.error);
|
||||
handleException(null);
|
||||
// TODO: Original went to the next workout here. We might still need to do that.
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.number != this.currentWorkoutNumbers.workoutNumber) {
|
||||
LOG.error("Incorrect workout number for totals response!");
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Workout {} totals:", this.currentWorkoutNumbers.workoutNumber);
|
||||
LOG.info("Number : {}", packet.number);
|
||||
LOG.info("Status : {}", packet.status);
|
||||
LOG.info("Start : {}", packet.startTime);
|
||||
LOG.info("End : {}", packet.endTime);
|
||||
LOG.info("Calories: {}", packet.calories);
|
||||
LOG.info("Distance: {}", packet.distance);
|
||||
LOG.info("Steps : {}", packet.stepCount);
|
||||
LOG.info("Time : {}", packet.totalTime);
|
||||
LOG.info("Duration: {}", packet.duration);
|
||||
LOG.info("Type : {}", packet.type);
|
||||
|
||||
this.currentDatabaseId = addWorkoutTotalsDataToDb(packet);
|
||||
if (this.currentDatabaseId == null) {
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentNumber = 0;
|
||||
syncData();
|
||||
}
|
||||
|
||||
private void syncData() {
|
||||
if (this.currentNumber >= this.currentWorkoutNumbers.dataCount) {
|
||||
this.currentNumber = 0;
|
||||
syncPace();
|
||||
return;
|
||||
}
|
||||
|
||||
Request getWorkoutDataRequest = new RequestBuilder<Workout.WorkoutData.Response>(
|
||||
this.supportProvider,
|
||||
Workout.id,
|
||||
Workout.WorkoutData.id,
|
||||
new Workout.WorkoutData.Request(
|
||||
this.supportProvider.getParamsProvider(),
|
||||
this.currentWorkoutNumbers.workoutNumber,
|
||||
(short) this.currentNumber,
|
||||
this.supportProvider.getDeviceState().isSupportsWorkoutNewSteps()
|
||||
)
|
||||
)
|
||||
.setTimeout(5000)
|
||||
.onCallback(this::handleData)
|
||||
.onException(e -> {
|
||||
LOG.error("Exception receiving workout data", e);
|
||||
handleException(e);
|
||||
})
|
||||
.onTimeout(packet -> {
|
||||
LOG.error("Timeout on workout data");
|
||||
handleTimeout();
|
||||
})
|
||||
.build();
|
||||
try {
|
||||
getWorkoutDataRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Exception sending workout data request", e);
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleData(Workout.WorkoutData.Response packet) {
|
||||
// TODO: Original didn't even have this check...
|
||||
if (packet.error != null) {
|
||||
LOG.error("Error {} occurred during workout data sync", packet.error);
|
||||
handleException(null);
|
||||
// TODO: Original went to the next workout here. We might still need to do that.
|
||||
return;
|
||||
}
|
||||
if (packet.workoutNumber != this.currentWorkoutNumbers.workoutNumber) {
|
||||
LOG.error("Incorrect workout number for data response!");
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
if (packet.dataNumber != this.currentNumber) {
|
||||
LOG.error("Incorrect data number!");
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Workout {} data {}:", this.currentWorkoutNumbers.workoutNumber, this.currentNumber);
|
||||
LOG.info("Workout : {}", packet.workoutNumber);
|
||||
LOG.info("Data num: {}", packet.dataNumber);
|
||||
LOG.info("Header : {}", Arrays.toString(packet.rawHeader));
|
||||
LOG.info("Header : {}", packet.header);
|
||||
LOG.info("Data : {}", Arrays.toString(packet.rawData));
|
||||
LOG.info("Data : {}", Arrays.toString(packet.dataList.toArray()));
|
||||
LOG.info("Bitmap : {}", packet.innerBitmap);
|
||||
|
||||
addWorkoutSampleDataToDb(this.currentDatabaseId, packet.dataList);
|
||||
this.currentNumber += 1;
|
||||
|
||||
syncData();
|
||||
}
|
||||
|
||||
private void syncPace() {
|
||||
if (this.currentNumber >= this.currentWorkoutNumbers.paceCount) {
|
||||
this.currentNumber = 0;
|
||||
syncSwimSegments();
|
||||
return;
|
||||
}
|
||||
|
||||
Request getWorkoutPaceRequest = new RequestBuilder<Workout.WorkoutPace.Response>(
|
||||
this.supportProvider,
|
||||
Workout.id,
|
||||
Workout.WorkoutPace.id,
|
||||
new Workout.WorkoutPace.Request(
|
||||
this.supportProvider.getParamsProvider(),
|
||||
this.currentWorkoutNumbers.workoutNumber,
|
||||
(short) this.currentNumber
|
||||
)
|
||||
)
|
||||
.setTimeout(5000)
|
||||
.onCallback(this::handlePace)
|
||||
.onException(e -> {
|
||||
LOG.error("Exception receiving workout pace", e);
|
||||
handleException(e);
|
||||
})
|
||||
.onTimeout(packet -> {
|
||||
LOG.error("Tiemout on workout pace");
|
||||
handleTimeout();
|
||||
})
|
||||
.build();
|
||||
try {
|
||||
getWorkoutPaceRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Exception sending workout data request", e);
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePace(Workout.WorkoutPace.Response packet) {
|
||||
if (packet.error != null) {
|
||||
LOG.error("Error {} occurred during workout pace sync", packet.error);
|
||||
handleException(null);
|
||||
// TODO: Original went to the next workout here. We might still need to do that.
|
||||
return;
|
||||
}
|
||||
if (packet.workoutNumber != this.currentWorkoutNumbers.workoutNumber) {
|
||||
LOG.error("Incorrect workout number for pace response!");
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
if (packet.paceNumber != currentNumber) {
|
||||
LOG.error("Incorrect pace number!");
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Workout {} pace {}:", this.currentWorkoutNumbers.workoutNumber, this.currentNumber);
|
||||
LOG.info("Workout : {}", packet.workoutNumber);
|
||||
LOG.info("Pace : {}", packet.paceNumber);
|
||||
LOG.info("Block num: {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
addWorkoutPaceDataToDb(this.currentDatabaseId, packet.blocks, packet.paceNumber);
|
||||
currentNumber += 1;
|
||||
|
||||
syncPace();
|
||||
}
|
||||
|
||||
private void syncSwimSegments() {
|
||||
if (this.currentNumber >= this.currentWorkoutNumbers.segmentsCount) {
|
||||
this.currentNumber = 0;
|
||||
syncSpO2();
|
||||
return;
|
||||
}
|
||||
|
||||
Request getWorkoutSwimSegmentsRequest = new RequestBuilder<Workout.WorkoutSwimSegments.Response>(
|
||||
this.supportProvider,
|
||||
Workout.id,
|
||||
Workout.WorkoutSwimSegments.id,
|
||||
new Workout.WorkoutSwimSegments.Request(
|
||||
this.supportProvider.getParamsProvider(),
|
||||
this.currentWorkoutNumbers.workoutNumber,
|
||||
(short) this.currentNumber
|
||||
)
|
||||
)
|
||||
.setTimeout(5000)
|
||||
.onCallback(this::handleSwimSegment)
|
||||
.onException(e -> {
|
||||
LOG.error("Exception receiving workout swim segments", e);
|
||||
handleException(e);
|
||||
})
|
||||
.onTimeout(packet -> {
|
||||
LOG.error("Timeout on workout swim segments");
|
||||
handleTimeout();
|
||||
})
|
||||
.build();
|
||||
try {
|
||||
getWorkoutSwimSegmentsRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Exception sending workout swim segment request", e);
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSwimSegment(Workout.WorkoutSwimSegments.Response packet) {
|
||||
if (packet.error != null) {
|
||||
LOG.error("Error {} occurred during workout swim segments sync", packet.error);
|
||||
handleException(null);
|
||||
// TODO: Original went to the next workout here. We might still need to do that.
|
||||
return;
|
||||
}
|
||||
if (packet.workoutNumber != this.currentWorkoutNumbers.workoutNumber) {
|
||||
LOG.error("Incorrect workout number for swim segment response!");
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
if (packet.segmentNumber != this.currentNumber) {
|
||||
LOG.error("Incorrect swim segment number!");
|
||||
handleException(null);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Workout {} segment {}:", this.currentWorkoutNumbers.workoutNumber, this.currentNumber);
|
||||
LOG.info("Workout : {}", packet.workoutNumber);
|
||||
LOG.info("Segments : {}", packet.segmentNumber);
|
||||
LOG.info("Block num: {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
addWorkoutSwimSegmentsDataToDb(this.currentDatabaseId, packet.blocks, packet.segmentNumber);
|
||||
this.currentNumber += 1;
|
||||
|
||||
syncSwimSegments();
|
||||
}
|
||||
|
||||
private void syncSpO2() {
|
||||
if (this.currentNumber >= this.currentWorkoutNumbers.spO2Count) {
|
||||
this.currentNumber = 0;
|
||||
syncSections();
|
||||
return;
|
||||
}
|
||||
|
||||
Request getWorkoutSpO2Request = new RequestBuilder<Workout.WorkoutSpO2.Response>(
|
||||
this.supportProvider,
|
||||
Workout.id,
|
||||
Workout.WorkoutSpO2.id,
|
||||
new Workout.WorkoutSpO2.Request(
|
||||
this.supportProvider.getParamsProvider(),
|
||||
this.currentWorkoutNumbers.workoutNumber,
|
||||
(short) this.currentNumber
|
||||
)
|
||||
)
|
||||
.setTimeout(5000)
|
||||
.onCallback(this::handleSpO2)
|
||||
.onException(e -> {
|
||||
LOG.error("Exception receiving SpO2 data");
|
||||
handleException(e);
|
||||
})
|
||||
.onTimeout(packet -> {
|
||||
LOG.error("Timeout on SpO2 data");
|
||||
handleTimeout();
|
||||
})
|
||||
.build();
|
||||
try {
|
||||
getWorkoutSpO2Request.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Exception sending workout SpO2 request", e);
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSpO2(Workout.WorkoutSpO2.Response packet) {
|
||||
if (packet.error != null) {
|
||||
LOG.error("Error {} occurred during workout SpO2 sync", packet.error);
|
||||
handleException(null);
|
||||
// TODO: Original went to the next workout here. We might still need to do that.
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Workout {} current {}:", this.currentWorkoutNumbers.workoutNumber, this.currentNumber);
|
||||
LOG.info("spO2Number1: {}", packet.spO2Number1);
|
||||
LOG.info("spO2Number2: {}", packet.spO2Number2);
|
||||
LOG.info("Block num : {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
addWorkoutSpO2DataToDb(this.currentDatabaseId, packet.blocks, (short) this.currentNumber);
|
||||
this.currentNumber += 1;
|
||||
|
||||
syncSpO2();
|
||||
}
|
||||
|
||||
private void syncSections() {
|
||||
if (this.currentNumber >= this.currentWorkoutNumbers.sectionsCount) {
|
||||
this.currentNumber = 0;
|
||||
finishSingleWorkoutSync();
|
||||
return;
|
||||
}
|
||||
|
||||
Request getWorkoutSectionsRequest = new RequestBuilder<Workout.WorkoutSections.Response>(
|
||||
this.supportProvider,
|
||||
Workout.id,
|
||||
Workout.WorkoutSections.id,
|
||||
new Workout.WorkoutSections.Request(
|
||||
this.supportProvider.getParamsProvider(),
|
||||
this.currentWorkoutNumbers.workoutNumber,
|
||||
(short) this.currentNumber
|
||||
)
|
||||
)
|
||||
.setTimeout(5000)
|
||||
.onCallback(this::handleSections)
|
||||
.onException(e -> {
|
||||
LOG.error("Exception receiving workout sections", e);
|
||||
handleException(e);
|
||||
})
|
||||
.onTimeout(packet -> {
|
||||
LOG.error("Timeout on workout sections");
|
||||
handleTimeout();
|
||||
})
|
||||
.build();
|
||||
try {
|
||||
getWorkoutSectionsRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Exception sending workout sections request", e);
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSections(Workout.WorkoutSections.Response packet) {
|
||||
if (packet.error != null) {
|
||||
if (packet.error == 0x0001E079) {
|
||||
LOG.warn("Error 0001E079 occurred during workout swim segments sync. This seems to be normal, so it's ignored.");
|
||||
this.currentNumber += 1;
|
||||
syncSections();
|
||||
return;
|
||||
}
|
||||
LOG.error("Error {} occurred during workout sections sync", packet.error);
|
||||
handleException(null);
|
||||
// TODO: Original went to the next workout here. We might still need to do that.
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Workout {} section {}:", this.currentWorkoutNumbers.workoutNumber, this.currentNumber);
|
||||
LOG.info("workoutId : {}", packet.workoutId);
|
||||
LOG.info("number : {}", packet.number);
|
||||
LOG.info("Block num : {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
addWorkoutSectionsDataToDb(this.currentDatabaseId, packet.blocks, (short) this.currentNumber);
|
||||
currentNumber += 1;
|
||||
|
||||
syncSections();
|
||||
}
|
||||
|
||||
private void finishSingleWorkoutSync() {
|
||||
new HuaweiWorkoutGbParser(
|
||||
this.supportProvider.getDevice(),
|
||||
this.supportProvider.getContext()
|
||||
).parseWorkout(this.currentDatabaseId);
|
||||
|
||||
supportProvider.downloadWorkoutGpsFiles(
|
||||
this.currentWorkoutNumbers.workoutNumber,
|
||||
this.currentDatabaseId,
|
||||
this::syncNextWorkout
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------
|
||||
DATABASE FUNCTIONS
|
||||
--------------------------------------------------
|
||||
*/
|
||||
private int getStartTimeFromDb() {
|
||||
int start = 0;
|
||||
GBDevice gbDevice = supportProvider.getDevice();
|
||||
SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress());
|
||||
long prefLastSyncTime = sharedPreferences.getLong("lastSportsActivityTimeMillis", 0);
|
||||
if (prefLastSyncTime != 0) {
|
||||
start = (int) (prefLastSyncTime / 1000);
|
||||
|
||||
// Reset for next calls
|
||||
sharedPreferences.edit().putLong("lastSportsActivityTimeMillis", 0).apply();
|
||||
} else {
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId();
|
||||
|
||||
QueryBuilder<HuaweiWorkoutSummarySample> qb1 = db.getDaoSession().getHuaweiWorkoutSummarySampleDao().queryBuilder().where(
|
||||
HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.UserId.eq(userId)
|
||||
).orderDesc(
|
||||
HuaweiWorkoutSummarySampleDao.Properties.StartTimestamp
|
||||
).limit(1);
|
||||
|
||||
List<HuaweiWorkoutSummarySample> samples1 = qb1.list();
|
||||
if (!samples1.isEmpty())
|
||||
start = samples1.get(0).getEndTimestamp();
|
||||
|
||||
QueryBuilder<BaseActivitySummary> qb2 = db.getDaoSession().getBaseActivitySummaryDao().queryBuilder().where(
|
||||
BaseActivitySummaryDao.Properties.DeviceId.eq(deviceId),
|
||||
BaseActivitySummaryDao.Properties.UserId.eq(userId)
|
||||
).orderDesc(
|
||||
BaseActivitySummaryDao.Properties.StartTime
|
||||
).limit(1);
|
||||
|
||||
List<BaseActivitySummary> samples2 = qb2.list();
|
||||
if (!samples2.isEmpty())
|
||||
start = Math.min(start, (int) (samples2.get(0).getEndTime().getTime() / 1000L));
|
||||
|
||||
start = start + 1;
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Exception for getting start time, using 10/06/2022 - 00:00:00.");
|
||||
start = 1654819200;
|
||||
}
|
||||
|
||||
if (start == 0 || start == 1)
|
||||
start = 1654819200;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
public Long addWorkoutTotalsDataToDb(Workout.WorkoutTotals.Response packet) {
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(
|
||||
this.supportProvider.getDevice(),
|
||||
db.getDaoSession()
|
||||
).getId();
|
||||
|
||||
// Avoid duplicates
|
||||
QueryBuilder<HuaweiWorkoutSummarySample> qb = db.getDaoSession().getHuaweiWorkoutSummarySampleDao().queryBuilder().where(
|
||||
HuaweiWorkoutSummarySampleDao.Properties.UserId.eq(userId),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.WorkoutNumber.eq(packet.number),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.StartTimestamp.eq(packet.startTime),
|
||||
HuaweiWorkoutSummarySampleDao.Properties.EndTimestamp.eq(packet.endTime)
|
||||
);
|
||||
List<HuaweiWorkoutSummarySample> results = qb.build().list();
|
||||
Long workoutId = null;
|
||||
if (!results.isEmpty())
|
||||
workoutId = results.get(0).getWorkoutId();
|
||||
|
||||
byte[] raw;
|
||||
if (packet.rawData == null)
|
||||
raw = null;
|
||||
else
|
||||
raw = StringUtils.bytesToHex(packet.rawData).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
byte[] recoveryHeartRates;
|
||||
if (packet.recoveryHeartRates == null)
|
||||
recoveryHeartRates = null;
|
||||
else
|
||||
recoveryHeartRates = StringUtils.bytesToHex(packet.recoveryHeartRates).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
HuaweiWorkoutSummarySample summarySample = new HuaweiWorkoutSummarySample(
|
||||
workoutId,
|
||||
deviceId,
|
||||
userId,
|
||||
packet.number,
|
||||
packet.status,
|
||||
packet.startTime,
|
||||
packet.endTime,
|
||||
packet.calories,
|
||||
packet.distance,
|
||||
packet.stepCount,
|
||||
packet.totalTime,
|
||||
packet.duration,
|
||||
packet.type,
|
||||
packet.strokes,
|
||||
packet.avgStrokeRate,
|
||||
packet.poolLength,
|
||||
packet.laps,
|
||||
packet.avgSwolf,
|
||||
raw,
|
||||
null,
|
||||
packet.maxAltitude,
|
||||
packet.minAltitude,
|
||||
packet.elevationGain,
|
||||
packet.elevationLoss,
|
||||
packet.workoutLoad,
|
||||
packet.workoutAerobicEffect,
|
||||
packet.workoutAnaerobicEffect,
|
||||
packet.recoveryTime,
|
||||
packet.minHeartRatePeak,
|
||||
packet.maxHeartRatePeak,
|
||||
recoveryHeartRates,
|
||||
packet.swimType,
|
||||
packet.maxMET,
|
||||
packet.hrZoneType,
|
||||
packet.runPaceZone1Min,
|
||||
packet.runPaceZone2Min,
|
||||
packet.runPaceZone3Min,
|
||||
packet.runPaceZone4Min,
|
||||
packet.runPaceZone5Min,
|
||||
packet.runPaceZone5Max,
|
||||
packet.runPaceZone1Time,
|
||||
packet.runPaceZone2Time,
|
||||
packet.runPaceZone3Time,
|
||||
packet.runPaceZone4Time,
|
||||
packet.runPaceZone5Time,
|
||||
packet.algType,
|
||||
packet.trainingPoints,
|
||||
packet.longestStreak,
|
||||
packet.tripped,
|
||||
this.supportProvider.getDeviceState().isSupportsWorkoutNewSteps(),
|
||||
null
|
||||
);
|
||||
|
||||
db.getDaoSession().getHuaweiWorkoutSummarySampleDao().insertOrReplace(summarySample);
|
||||
|
||||
// We should completely replace values. Delete all and insert again.
|
||||
final DeleteQuery<HuaweiWorkoutSummaryAdditionalValuesSample> tableDeleteQuery = db.getDaoSession().getHuaweiWorkoutSummaryAdditionalValuesSampleDao().queryBuilder()
|
||||
.where(HuaweiWorkoutSummaryAdditionalValuesSampleDao.Properties.WorkoutId.eq(summarySample.getWorkoutId()))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
|
||||
for (Map.Entry<String, String> entry : packet.additionalValues.entrySet()) {
|
||||
HuaweiWorkoutSummaryAdditionalValuesSample summarySampleAdditionalValue = new HuaweiWorkoutSummaryAdditionalValuesSample(summarySample.getWorkoutId(), entry.getKey(), entry.getValue());
|
||||
db.getDaoSession().getHuaweiWorkoutSummaryAdditionalValuesSampleDao().insertOrReplace(summarySampleAdditionalValue);
|
||||
}
|
||||
|
||||
return summarySample.getWorkoutId();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout totals data to database", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutSampleDataToDb(Long workoutId, List<Workout.WorkoutData.Response.Data> dataList) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutDataSampleDao dao = db.getDaoSession().getHuaweiWorkoutDataSampleDao();
|
||||
|
||||
for (Workout.WorkoutData.Response.Data data : dataList) {
|
||||
byte[] unknown;
|
||||
if (data.unknownData == null)
|
||||
unknown = null;
|
||||
else
|
||||
unknown = StringUtils.bytesToHex(data.unknownData).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
HuaweiWorkoutDataSample dataSample = new HuaweiWorkoutDataSample(
|
||||
workoutId,
|
||||
data.timestamp,
|
||||
data.heartRate,
|
||||
data.speed,
|
||||
data.stepRate,
|
||||
data.cadence,
|
||||
data.stepLength,
|
||||
data.groundContactTime,
|
||||
data.impact,
|
||||
data.swingAngle,
|
||||
data.foreFootLanding,
|
||||
data.midFootLanding,
|
||||
data.backFootLanding,
|
||||
data.eversionAngle,
|
||||
data.swolf,
|
||||
data.strokeRate,
|
||||
unknown,
|
||||
data.calories,
|
||||
data.cyclingPower,
|
||||
data.frequency,
|
||||
data.altitude,
|
||||
data.hangTime,
|
||||
data.impactHangRate,
|
||||
data.rideCadence,
|
||||
data.ap,
|
||||
data.vo,
|
||||
data.gtb,
|
||||
data.vr,
|
||||
data.ceiling,
|
||||
data.temp,
|
||||
data.spo2,
|
||||
data.cns
|
||||
);
|
||||
dao.insertOrReplace(dataSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutPaceDataToDb(Long workoutId, List<Workout.WorkoutPace.Response.Block> paceList, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutPaceSampleDao dao = db.getDaoSession().getHuaweiWorkoutPaceSampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutPaceSample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutPaceSampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
int paceIndex = (int) dao.queryBuilder().where(HuaweiWorkoutPaceSampleDao.Properties.WorkoutId.eq(workoutId)).count();
|
||||
for (Workout.WorkoutPace.Response.Block block : paceList) {
|
||||
|
||||
Integer correction = block.hasCorrection ? (int) block.correction : null;
|
||||
HuaweiWorkoutPaceSample paceSample = new HuaweiWorkoutPaceSample(
|
||||
workoutId,
|
||||
paceIndex++,
|
||||
block.distance,
|
||||
block.type,
|
||||
block.pace,
|
||||
block.pointIndex,
|
||||
correction
|
||||
);
|
||||
dao.insertOrReplace(paceSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout pace data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addWorkoutSwimSegmentsDataToDb(Long workoutId, List<Workout.WorkoutSwimSegments.Response.Block> paceList, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutSwimSegmentsSampleDao dao = db.getDaoSession().getHuaweiWorkoutSwimSegmentsSampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutSwimSegmentsSample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
int paceIndex = (int) dao.queryBuilder().where(HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(workoutId)).count();
|
||||
for (Workout.WorkoutSwimSegments.Response.Block block : paceList) {
|
||||
HuaweiWorkoutSwimSegmentsSample swimSectionSample = new HuaweiWorkoutSwimSegmentsSample(
|
||||
workoutId,
|
||||
paceIndex++,
|
||||
block.distance,
|
||||
block.type,
|
||||
block.pace,
|
||||
block.pointIndex,
|
||||
block.segment,
|
||||
block.swimType,
|
||||
block.strokes,
|
||||
block.avgSwolf,
|
||||
block.time
|
||||
);
|
||||
dao.insertOrReplace(swimSectionSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout swim section data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutSpO2DataToDb(Long workoutId, List<Workout.WorkoutSpO2.Response.Block> spO2List, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutSpO2SampleDao dao = db.getDaoSession().getHuaweiWorkoutSpO2SampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutSpO2Sample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutSpO2SampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
for (Workout.WorkoutSpO2.Response.Block block : spO2List) {
|
||||
HuaweiWorkoutSpO2Sample spO2Sample = new HuaweiWorkoutSpO2Sample(
|
||||
workoutId,
|
||||
block.interval,
|
||||
block.value
|
||||
);
|
||||
dao.insertOrReplace(spO2Sample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout SpO2 data to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWorkoutSectionsDataToDb(Long workoutId, List<Workout.WorkoutSections.Response.Block> sectionsList, short number) {
|
||||
if (workoutId == null)
|
||||
return;
|
||||
|
||||
// NOTE: All fields of this data is optional. At this point I don't all workouts that this data used.
|
||||
// I decided to add two additional fields dataIdx and rowIdx as primary keys that should identify each row
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
HuaweiWorkoutSectionsSampleDao dao = db.getDaoSession().getHuaweiWorkoutSectionsSampleDao();
|
||||
|
||||
if (number == 0) {
|
||||
final DeleteQuery<HuaweiWorkoutSectionsSample> tableDeleteQuery = dao.queryBuilder()
|
||||
.where(HuaweiWorkoutSectionsSampleDao.Properties.WorkoutId.eq(workoutId))
|
||||
.buildDelete();
|
||||
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (Workout.WorkoutSections.Response.Block block : sectionsList) {
|
||||
HuaweiWorkoutSectionsSample huaweiWorkoutSectionsSample = new HuaweiWorkoutSectionsSample(
|
||||
workoutId,
|
||||
number,
|
||||
i++,
|
||||
block.num,
|
||||
block.time,
|
||||
block.distance,
|
||||
block.pace,
|
||||
block.heartRate,
|
||||
block.cadence,
|
||||
block.stepLength,
|
||||
block.totalRise,
|
||||
block.totalDescend,
|
||||
block.groundContactTime,
|
||||
block.groundImpact,
|
||||
block.swingAngle,
|
||||
block.eversion,
|
||||
block.avgCadence,
|
||||
block.divingUnderwaterTime,
|
||||
block.divingMaxDepth,
|
||||
block.divingUnderwaterTime,
|
||||
block.divingBreakTime
|
||||
);
|
||||
dao.insertOrReplace(huaweiWorkoutSectionsSample);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to add workout sections data to database", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
/* Copyright (C) 2024 Damien Gaignon, Martin.JM
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class GetWorkoutCountRequest extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutCountRequest.class);
|
||||
|
||||
private final int start;
|
||||
private final int end;
|
||||
|
||||
public GetWorkoutCountRequest(
|
||||
HuaweiSupportProvider support,
|
||||
nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder builder,
|
||||
int start,
|
||||
int end
|
||||
) {
|
||||
super(support, builder);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutCount.id;
|
||||
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public GetWorkoutCountRequest(
|
||||
HuaweiSupportProvider support,
|
||||
nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder builder,
|
||||
int start,
|
||||
int end
|
||||
) {
|
||||
super(support, builder);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutCount.id;
|
||||
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new Workout.WorkoutCount.Request(paramsProvider, this.start, this.end).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseParseException {
|
||||
if (!(receivedPacket instanceof Workout.WorkoutCount.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutCount.Response.class);
|
||||
|
||||
Workout.WorkoutCount.Response packet = (Workout.WorkoutCount.Response) receivedPacket;
|
||||
|
||||
if (packet.count == 0 || packet.workoutNumbers == null || packet.error != null) {
|
||||
if(packet.error != null) {
|
||||
LOG.warn("Error occurred during workout sync: {}", packet.error);
|
||||
GB.toast("Error occurred during workout sync", Toast.LENGTH_LONG, GB.WARN);
|
||||
}
|
||||
this.supportProvider.endOfWorkoutSync();
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.count > packet.workoutNumbers.size()) {
|
||||
LOG.warn("Packet count is greater than workoutNumbers size: {} > {}", packet.count, packet.workoutNumbers.size());
|
||||
GB.toast("Workout count mismatch, after this sync is complete, try synchronising again", Toast.LENGTH_LONG, GB.WARN);
|
||||
|
||||
packet.count = (short) packet.workoutNumbers.size();
|
||||
} else if (packet.count < packet.workoutNumbers.size()) {
|
||||
LOG.warn("Packet count is smaller than workoutNumbers size: {} < {}", packet.count, packet.workoutNumbers.size());
|
||||
GB.toast("Workout count mismatch, after this sync is complete, try synchronising again", Toast.LENGTH_LONG, GB.WARN);
|
||||
|
||||
packet.workoutNumbers.subList(packet.count, packet.workoutNumbers.size()).clear();
|
||||
}
|
||||
|
||||
// Has to be sorted for the timestamp-based sync start that we use in the HuaweiSupportProvider
|
||||
packet.workoutNumbers.sort(Comparator.comparingInt(o -> o.workoutNumber));
|
||||
|
||||
if (packet.count > 0) {
|
||||
GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest(
|
||||
this.supportProvider,
|
||||
packet.workoutNumbers.remove(0),
|
||||
packet.workoutNumbers
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
/* Copyright (C) 2024 Damien Gaignon, Martin.JM
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWorkoutGbParser;
|
||||
|
||||
public class GetWorkoutDataRequest extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutDataRequest.class);
|
||||
|
||||
Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers;
|
||||
List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder;
|
||||
short number;
|
||||
Long databaseId;
|
||||
|
||||
/**
|
||||
* Request to get workout totals
|
||||
*
|
||||
* @param support The support
|
||||
* @param workoutNumbers The numbers of the current workout
|
||||
* @param remainder The numbers of the remainder if the workouts to get
|
||||
* @param number The number of this data request
|
||||
*/
|
||||
public GetWorkoutDataRequest(HuaweiSupportProvider support, Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers, List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder, short number, Long databaseId) {
|
||||
super(support);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutData.id;
|
||||
|
||||
this.workoutNumbers = workoutNumbers;
|
||||
this.remainder = remainder;
|
||||
this.number = number;
|
||||
|
||||
this.databaseId = databaseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new Workout.WorkoutData.Request(paramsProvider, workoutNumbers.workoutNumber, this.number, supportProvider.getDeviceState().isSupportsWorkoutNewSteps()).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseParseException {
|
||||
if (!(receivedPacket instanceof Workout.WorkoutData.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutData.Response.class);
|
||||
|
||||
Workout.WorkoutData.Response packet = (Workout.WorkoutData.Response) receivedPacket;
|
||||
|
||||
if (packet.workoutNumber != this.workoutNumbers.workoutNumber)
|
||||
throw new WorkoutParseException("Incorrect workout number!");
|
||||
|
||||
if (packet.dataNumber != this.number)
|
||||
throw new WorkoutParseException("Incorrect data number!");
|
||||
|
||||
LOG.info("Workout {} data {}:", this.workoutNumbers.workoutNumber, this.number);
|
||||
LOG.info("Workout : {}", packet.workoutNumber);
|
||||
LOG.info("Data num: {}", packet.dataNumber);
|
||||
LOG.info("Header : {}", Arrays.toString(packet.rawHeader));
|
||||
LOG.info("Header : {}", packet.header);
|
||||
LOG.info("Data : {}", Arrays.toString(packet.rawData));
|
||||
LOG.info("Data : {}", Arrays.toString(packet.dataList.toArray()));
|
||||
LOG.info("Bitmap : {}", packet.innerBitmap);
|
||||
|
||||
this.supportProvider.addWorkoutSampleData(
|
||||
this.databaseId,
|
||||
packet.dataList
|
||||
);
|
||||
|
||||
if (this.workoutNumbers.dataCount > this.number + 1) {
|
||||
GetWorkoutDataRequest nextRequest = new GetWorkoutDataRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) (this.number + 1),
|
||||
databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.paceCount > 0) {
|
||||
GetWorkoutPaceRequest nextRequest = new GetWorkoutPaceRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.segmentsCount > 0) {
|
||||
GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.spO2Count > 0) {
|
||||
GetWorkoutSpO2Request nextRequest = new GetWorkoutSpO2Request(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.sectionsCount > 0) {
|
||||
GetWorkoutSectionsRequest nextRequest = new GetWorkoutSectionsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else {
|
||||
new HuaweiWorkoutGbParser(getDevice(), getContext()).parseWorkout(this.databaseId);
|
||||
supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!remainder.isEmpty()) {
|
||||
GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest(
|
||||
GetWorkoutDataRequest.this.supportProvider,
|
||||
remainder.remove(0),
|
||||
remainder
|
||||
);
|
||||
nextRequest.setFinalizeReq(GetWorkoutDataRequest.this.finalizeReq);
|
||||
// Cannot do this with nextRequest because it's in a callback
|
||||
try {
|
||||
nextRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
finalizeReq.handleException(new ResponseParseException("Cannot send next request", e));
|
||||
}
|
||||
} else {
|
||||
supportProvider.endOfWorkoutSync();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
/* Copyright (C) 2024 Damien Gaignon, Martin.JM
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWorkoutGbParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class GetWorkoutPaceRequest extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutPaceRequest.class);
|
||||
|
||||
Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers;
|
||||
List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder;
|
||||
short number;
|
||||
Long databaseId;
|
||||
|
||||
public GetWorkoutPaceRequest(HuaweiSupportProvider support, Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers, List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder, short number, Long databaseId) {
|
||||
super(support);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutPace.id;
|
||||
|
||||
this.workoutNumbers = workoutNumbers;
|
||||
this.remainder = remainder;
|
||||
this.number = number;
|
||||
|
||||
this.databaseId = databaseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new Workout.WorkoutPace.Request(paramsProvider, this.workoutNumbers.workoutNumber, this.number).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseParseException {
|
||||
if (!(receivedPacket instanceof Workout.WorkoutPace.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutPace.Response.class);
|
||||
|
||||
Workout.WorkoutPace.Response packet = (Workout.WorkoutPace.Response) receivedPacket;
|
||||
|
||||
if (packet.error != null) {
|
||||
LOG.warn("Error {} occurred during workout pace sync. ignoring", packet.error);
|
||||
GB.toast("Error occurred during workout sync", Toast.LENGTH_LONG, GB.WARN);
|
||||
supportProvider.nextWorkoutSync(remainder, GetWorkoutPaceRequest.this.finalizeReq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.workoutNumber != this.workoutNumbers.workoutNumber)
|
||||
throw new WorkoutParseException("Incorrect workout number!");
|
||||
|
||||
if (packet.paceNumber != this.number)
|
||||
throw new WorkoutParseException("Incorrect pace number!");
|
||||
|
||||
LOG.info("Workout {} pace {}:", this.workoutNumbers.workoutNumber, this.number);
|
||||
LOG.info("Workout : {}", packet.workoutNumber);
|
||||
LOG.info("Pace : {}", packet.paceNumber);
|
||||
LOG.info("Block num: {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
supportProvider.addWorkoutPaceData(this.databaseId, packet.blocks, packet.paceNumber);
|
||||
|
||||
if (this.workoutNumbers.paceCount > this.number + 1) {
|
||||
GetWorkoutPaceRequest nextRequest = new GetWorkoutPaceRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) (this.number + 1),
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.segmentsCount > 0) {
|
||||
GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.spO2Count > 0) {
|
||||
GetWorkoutSpO2Request nextRequest = new GetWorkoutSpO2Request(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.sectionsCount > 0) {
|
||||
GetWorkoutSectionsRequest nextRequest = new GetWorkoutSectionsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else {
|
||||
new HuaweiWorkoutGbParser(getDevice(), getContext()).parseWorkout(this.databaseId);
|
||||
supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, () -> supportProvider.nextWorkoutSync(remainder, GetWorkoutPaceRequest.this.finalizeReq));
|
||||
}
|
||||
}
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
/* Copyright (C) 2024-2025 Damien Gaignon, Martin.JM, Me7c7
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWorkoutGbParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class GetWorkoutSectionsRequest extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutSectionsRequest.class);
|
||||
|
||||
Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers;
|
||||
List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder;
|
||||
short number;
|
||||
Long databaseId;
|
||||
|
||||
public GetWorkoutSectionsRequest(HuaweiSupportProvider support, Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers, List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder, short number, Long databaseId) {
|
||||
super(support);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutSections.id;
|
||||
|
||||
this.workoutNumbers = workoutNumbers;
|
||||
this.remainder = remainder;
|
||||
this.number = number;
|
||||
|
||||
this.databaseId = databaseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new Workout.WorkoutSections.Request(paramsProvider, this.workoutNumbers.workoutNumber, this.number).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseParseException {
|
||||
if (!(receivedPacket instanceof Workout.WorkoutSections.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutSections.Response.class);
|
||||
|
||||
Workout.WorkoutSections.Response packet = (Workout.WorkoutSections.Response) receivedPacket;
|
||||
|
||||
if (packet.error == null) {
|
||||
LOG.info("Workout {} section {}:", this.workoutNumbers.workoutNumber, this.number);
|
||||
LOG.info("workoutId : {}", packet.workoutId);
|
||||
LOG.info("number : {}", packet.number);
|
||||
LOG.info("Block num : {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
supportProvider.addWorkoutSectionsData(this.databaseId, packet.blocks, this.number);
|
||||
} else if (packet.error == 0x0001E079) {
|
||||
// We don't know why it often returns this error, but it seems to be normal, so we ignore it
|
||||
LOG.warn("Error 0001E079 occurred during workout swim segments sync. This seems to be normal, so it's ignored.");
|
||||
} else {
|
||||
LOG.warn("Error {} occurred during workout swim segments sync. Skipping workout", packet.error);
|
||||
GB.toast("Error occurred during workout sync", Toast.LENGTH_LONG, GB.WARN);
|
||||
supportProvider.nextWorkoutSync(remainder, GetWorkoutSectionsRequest.this.finalizeReq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.workoutNumbers.sectionsCount > this.number + 1) {
|
||||
GetWorkoutSectionsRequest nextRequest = new GetWorkoutSectionsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) (this.number + 1),
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else {
|
||||
new HuaweiWorkoutGbParser(getDevice(), getContext()).parseWorkout(this.databaseId);
|
||||
supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, () -> supportProvider.nextWorkoutSync(remainder, GetWorkoutSectionsRequest.this.finalizeReq));
|
||||
}
|
||||
}
|
||||
}
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
/* Copyright (C) 2024-2025 Damien Gaignon, Martin.JM, Me7c7
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWorkoutGbParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class GetWorkoutSpO2Request extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutSpO2Request.class);
|
||||
|
||||
Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers;
|
||||
List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder;
|
||||
short number;
|
||||
Long databaseId;
|
||||
|
||||
public GetWorkoutSpO2Request(HuaweiSupportProvider support, Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers, List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder, short number, Long databaseId) {
|
||||
super(support);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutSpO2.id;
|
||||
|
||||
this.workoutNumbers = workoutNumbers;
|
||||
this.remainder = remainder;
|
||||
this.number = number;
|
||||
|
||||
this.databaseId = databaseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new Workout.WorkoutSpO2.Request(paramsProvider, this.workoutNumbers.workoutNumber, this.number).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseParseException {
|
||||
if (!(receivedPacket instanceof Workout.WorkoutSpO2.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutSections.Response.class);
|
||||
|
||||
Workout.WorkoutSpO2.Response packet = (Workout.WorkoutSpO2.Response) receivedPacket;
|
||||
|
||||
if (packet.error != null) {
|
||||
LOG.warn("Error {} occurred during workout swim segments sync. ignoring", packet.error);
|
||||
GB.toast("Error occurred during workout sync", Toast.LENGTH_LONG, GB.WARN);
|
||||
supportProvider.nextWorkoutSync(remainder, GetWorkoutSpO2Request.this.finalizeReq);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Workout {} current {}:", this.workoutNumbers.workoutNumber, this.number);
|
||||
LOG.info("spO2Number1: {}", packet.spO2Number1);
|
||||
LOG.info("spO2Number2: {}", packet.spO2Number2);
|
||||
LOG.info("Block num : {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
supportProvider.addWorkoutSpO2Data(this.databaseId, packet.blocks, this.number);
|
||||
|
||||
if (this.workoutNumbers.spO2Count > this.number + 1) {
|
||||
GetWorkoutSpO2Request nextRequest = new GetWorkoutSpO2Request(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) (this.number + 1),
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.sectionsCount > 0) {
|
||||
GetWorkoutSectionsRequest nextRequest = new GetWorkoutSectionsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else {
|
||||
new HuaweiWorkoutGbParser(getDevice(), getContext()).parseWorkout(this.databaseId);
|
||||
supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, () -> supportProvider.nextWorkoutSync(remainder, GetWorkoutSpO2Request.this.finalizeReq));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-127
@@ -1,127 +0,0 @@
|
||||
/* Copyright (C) 2024-2025 Damien Gaignon, Martin.JM, Me7c7
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWorkoutGbParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class GetWorkoutSwimSegmentsRequest extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutSwimSegmentsRequest.class);
|
||||
|
||||
Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers;
|
||||
List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder;
|
||||
short number;
|
||||
Long databaseId;
|
||||
|
||||
public GetWorkoutSwimSegmentsRequest(HuaweiSupportProvider support, Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers, List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder, short number, Long databaseId) {
|
||||
super(support);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutSwimSegments.id;
|
||||
|
||||
this.workoutNumbers = workoutNumbers;
|
||||
this.remainder = remainder;
|
||||
this.number = number;
|
||||
|
||||
this.databaseId = databaseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new Workout.WorkoutSwimSegments.Request(paramsProvider, this.workoutNumbers.workoutNumber, this.number).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseParseException {
|
||||
if (!(receivedPacket instanceof Workout.WorkoutSwimSegments.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutSwimSegments.Response.class);
|
||||
|
||||
Workout.WorkoutSwimSegments.Response packet = (Workout.WorkoutSwimSegments.Response) receivedPacket;
|
||||
|
||||
if (packet.error != null) {
|
||||
LOG.warn("Error {} occurred during workout swim segments sync. ignoring", packet.error);
|
||||
GB.toast("Error occurred during workout sync", Toast.LENGTH_LONG, GB.WARN);
|
||||
supportProvider.nextWorkoutSync(remainder, GetWorkoutSwimSegmentsRequest.this.finalizeReq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.workoutNumber != this.workoutNumbers.workoutNumber)
|
||||
throw new WorkoutParseException("Incorrect workout number!");
|
||||
|
||||
if (packet.segmentNumber != this.number)
|
||||
throw new WorkoutParseException("Incorrect pace number!");
|
||||
|
||||
LOG.info("Workout {} segment {}:", this.workoutNumbers.workoutNumber, this.number);
|
||||
LOG.info("Workout : {}", packet.workoutNumber);
|
||||
LOG.info("Segments : {}", packet.segmentNumber);
|
||||
LOG.info("Block num: {}", packet.blocks.size());
|
||||
LOG.info("Blocks : {}", Arrays.toString(packet.blocks.toArray()));
|
||||
|
||||
supportProvider.addWorkoutSwimSegmentsData(this.databaseId, packet.blocks, packet.segmentNumber);
|
||||
|
||||
if (this.workoutNumbers.segmentsCount > this.number + 1) {
|
||||
GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) (this.number + 1),
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.spO2Count > 0) {
|
||||
GetWorkoutSpO2Request nextRequest = new GetWorkoutSpO2Request(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.sectionsCount > 0) {
|
||||
GetWorkoutSectionsRequest nextRequest = new GetWorkoutSectionsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
this.databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else {
|
||||
new HuaweiWorkoutGbParser(getDevice(), getContext()).parseWorkout(this.databaseId);
|
||||
supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, () -> supportProvider.nextWorkoutSync(remainder, GetWorkoutSwimSegmentsRequest.this.finalizeReq));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
/* Copyright (C) 2024 Damien Gaignon, Martin.JM
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWorkoutGbParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class GetWorkoutTotalsRequest extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutTotalsRequest.class);
|
||||
|
||||
Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers;
|
||||
List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder;
|
||||
|
||||
/**
|
||||
* Request to get workout totals
|
||||
*
|
||||
* @param support The support
|
||||
* @param workoutNumbers The numbers of the current workout
|
||||
* @param remainder The numbers of the remainder of the workouts to get
|
||||
*/
|
||||
public GetWorkoutTotalsRequest(HuaweiSupportProvider support, Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers, List<Workout.WorkoutCount.Response.WorkoutNumbers> remainder) {
|
||||
super(support);
|
||||
|
||||
this.serviceId = Workout.id;
|
||||
this.commandId = Workout.WorkoutTotals.id;
|
||||
|
||||
this.workoutNumbers = workoutNumbers;
|
||||
this.remainder = remainder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new Workout.WorkoutTotals.Request(paramsProvider, workoutNumbers.workoutNumber).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseParseException {
|
||||
if (!(receivedPacket instanceof Workout.WorkoutTotals.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutTotals.Response.class);
|
||||
|
||||
Workout.WorkoutTotals.Response packet = (Workout.WorkoutTotals.Response) receivedPacket;
|
||||
|
||||
if (packet.error != null) {
|
||||
LOG.warn("Error {} occurred during workout pace sync. ignoring", packet.error);
|
||||
GB.toast("Error occurred during workout sync", Toast.LENGTH_LONG, GB.WARN);
|
||||
supportProvider.nextWorkoutSync(remainder, GetWorkoutTotalsRequest.this.finalizeReq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.number != this.workoutNumbers.workoutNumber)
|
||||
throw new WorkoutParseException("Incorrect workout number!");
|
||||
|
||||
|
||||
LOG.info("Workout {} totals:", this.workoutNumbers.workoutNumber);
|
||||
LOG.info("Number : {}", packet.number);
|
||||
LOG.info("Status : {}", packet.status);
|
||||
LOG.info("Start : {}", packet.startTime);
|
||||
LOG.info("End : {}", packet.endTime);
|
||||
LOG.info("Calories: {}", packet.calories);
|
||||
LOG.info("Distance: {}", packet.distance);
|
||||
LOG.info("Steps : {}", packet.stepCount);
|
||||
LOG.info("Time : {}", packet.totalTime);
|
||||
LOG.info("Duration: {}", packet.duration);
|
||||
LOG.info("Type : {}", packet.type);
|
||||
|
||||
Long databaseId = this.supportProvider.addWorkoutTotalsData(packet);
|
||||
|
||||
// Create the next request
|
||||
if (this.workoutNumbers.dataCount > 0) {
|
||||
GetWorkoutDataRequest nextRequest = new GetWorkoutDataRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.paceCount > 0) {
|
||||
GetWorkoutPaceRequest nextRequest = new GetWorkoutPaceRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.segmentsCount > 0) {
|
||||
GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.spO2Count > 0) {
|
||||
GetWorkoutSpO2Request nextRequest = new GetWorkoutSpO2Request(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else if (this.workoutNumbers.sectionsCount > 0) {
|
||||
GetWorkoutSectionsRequest nextRequest = new GetWorkoutSectionsRequest(
|
||||
this.supportProvider,
|
||||
this.workoutNumbers,
|
||||
this.remainder,
|
||||
(short) 0,
|
||||
databaseId
|
||||
);
|
||||
nextRequest.setFinalizeReq(this.finalizeReq);
|
||||
this.nextRequest(nextRequest);
|
||||
} else {
|
||||
new HuaweiWorkoutGbParser(getDevice(), getContext()).parseWorkout(databaseId);
|
||||
supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, databaseId, () -> supportProvider.nextWorkoutSync(remainder, GetWorkoutTotalsRequest.this.finalizeReq));
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -34,6 +34,8 @@ public class RequestBuilder<T extends HuaweiPacket> {
|
||||
|
||||
private boolean addToResponse = true;
|
||||
|
||||
private Integer timeout = null;
|
||||
|
||||
private OnCallback<T> onCallback = null;
|
||||
private OnTimeout<T> onTimeout = null;
|
||||
private OnException onException = null;
|
||||
@@ -55,6 +57,11 @@ public class RequestBuilder<T extends HuaweiPacket> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequestBuilder<T> setTimeout(Integer timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequestBuilder<T> onCallback(OnCallback<T> onCallback) {
|
||||
this.onCallback = onCallback;
|
||||
return this;
|
||||
@@ -76,6 +83,7 @@ public class RequestBuilder<T extends HuaweiPacket> {
|
||||
r.commandId = commandId;
|
||||
r.sendingPacket = requestPacket;
|
||||
r.addToResponse = this.addToResponse;
|
||||
r.setupTimeoutUntilNext(this.timeout);
|
||||
r.setFinalizeReq(new Request.RequestCallback() {
|
||||
@Override
|
||||
public void call(Request request) {
|
||||
|
||||
Reference in New Issue
Block a user