Xiaomi: Add ActivityTrackProvider

This commit is contained in:
José Rebelo
2026-03-21 21:55:04 +00:00
parent 9ddb4895df
commit a7b432c4b0
6 changed files with 92 additions and 6 deletions
@@ -63,6 +63,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrackProvider;
import nodomain.freeyourgadget.gadgetbridge.model.BodyEnergySample;
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.PaiSample;
@@ -74,6 +75,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiUuids;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiPreferences;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityTrackProvider;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl.WorkoutSummaryParser;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@@ -200,6 +202,12 @@ public abstract class XiaomiCoordinator extends AbstractBLEDeviceCoordinator {
return new WorkoutSummaryParser();
}
@Nullable
@Override
public ActivityTrackProvider getActivityTrackProvider(@NonNull final GBDevice device, @NonNull final Context context) {
return new XiaomiActivityTrackProvider(device, context);
}
@Override
public boolean supportsFlashing(@NonNull GBDevice device) {
return true;
@@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
@@ -220,6 +221,9 @@ public class XiaomiActivityFileFetcher {
protected void dumpBytesToExternalStorage(final XiaomiActivityFileId fileId, final byte[] bytes) {
try {
final File outputFile = getRawFile(mHealthService.getSupport().getDevice(), fileId);
if (outputFile == null) {
throw new IOException("Failed to get output file");
}
final File parentFile = outputFile.getParentFile();
if (parentFile != null) {
//noinspection ResultOfMethodCallIgnored
@@ -99,7 +99,7 @@ public class XiaomiActivityFileId implements Comparable<XiaomiActivityFileId> {
}
public static XiaomiActivityFileId from(final byte[] bytes) {
assert bytes.length == 7;
assert bytes.length >= 7;
return from(ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN));
}
@@ -0,0 +1,62 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrackProvider;
import nodomain.freeyourgadget.gadgetbridge.model.GpxActivityTrackProvider;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl.WorkoutGpsParser;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
@SuppressWarnings("ClassCanBeRecord")
public class XiaomiActivityTrackProvider implements ActivityTrackProvider {
private static final Logger LOG = LoggerFactory.getLogger(XiaomiActivityTrackProvider.class);
private final GBDevice device;
private final Context context;
public XiaomiActivityTrackProvider(final GBDevice device, final Context context) {
this.device = device;
this.context = context;
}
@Nullable
@Override
public ActivityTrack getActivityTrack(@NonNull final BaseActivitySummary summary) {
final File inputFile = FileUtils.tryFixPath(summary.getRawDetailsPath());
if (inputFile == null) {
LOG.warn("Raw file for details not found: {}", summary.getRawDetailsPath());
return new GpxActivityTrackProvider().getActivityTrack(summary);
}
LOG.debug("Loading raw details from {}", inputFile);
final byte[] detailsBytes;
try {
detailsBytes = FileUtils.readAll(inputFile);
} catch (final IOException e) {
LOG.error("Failed to read {}", inputFile, e);
return new GpxActivityTrackProvider().getActivityTrack(summary);
}
try {
final WorkoutGpsParser workoutGpsParser = new WorkoutGpsParser();
final XiaomiActivityFileId fileId = XiaomiActivityFileId.from(detailsBytes);
return workoutGpsParser.getActivityTrack(fileId, detailsBytes);
} catch (final Exception e) {
LOG.error("Failed to parse bytes from {}", inputFile, e);
return new GpxActivityTrackProvider().getActivityTrack(summary);
}
}
}
@@ -19,6 +19,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.imp
import android.content.Context;
import android.widget.Toast;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,8 +53,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class WorkoutGpsParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(WorkoutGpsParser.class);
@Override
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
@Nullable
public ActivityTrack getActivityTrack(final XiaomiActivityFileId fileId, final byte[] bytes) {
final int version = fileId.getVersion();
final int headerSize;
final int sampleSize;
@@ -67,7 +69,7 @@ public class WorkoutGpsParser extends XiaomiActivityParser {
break;
default:
LOG.warn("Unable to parse workout gps version {}", fileId.getVersion());
return false;
return null;
}
final ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
@@ -118,7 +120,16 @@ public class WorkoutGpsParser extends XiaomiActivityParser {
}
}
try (DBHandler dbHandler = GBApplication.acquireDB()) {
return activityTrack;
}
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
final ActivityTrack activityTrack = getActivityTrack(fileId, bytes);
if (activityTrack == null) {
return false;
}
try (DBHandler dbHandler = GBApplication.acquireDB()) {
final DaoSession session = dbHandler.getDaoSession();
final Device device = DBHelper.getDevice(gbDevice, session);
final User user = DBHelper.getUser(session);
@@ -152,6 +152,7 @@ public class XiaomiSimpleActivityParser {
}
}
summaryData.setHasGps(summary.getRawDetailsPath() != null);
summary.setSummaryData(summaryData.toString());
}