igpsport: Copy FitAsyncProcessor from Garmin

This commit is contained in:
Vitaliy Tomin
2026-05-13 23:33:24 +02:00
committed by José Rebelo
parent 4f56b6569b
commit bab5311d7f
3 changed files with 117 additions and 10 deletions
@@ -720,6 +720,7 @@ public class IGPSportDeviceSupport extends AbstractBTLESingleDeviceSupport {
public void setContext(final GBDevice gbDevice, final BluetoothAdapter btAdapter, final Context context) {
super.setContext(gbDevice, btAdapter, context);
this.mediaManager = new MediaManager(context);
this.downloadManager.setContext(context);
}
@Override
@@ -19,6 +19,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport;
import static nodomain.freeyourgadget.gadgetbridge.GBApplication.getContext;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminTimeUtils.garminTimestampToJavaMillis;
import android.content.Context;
import com.google.protobuf.InvalidProtocolBufferException;
import org.slf4j.Logger;
@@ -43,10 +45,11 @@ import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Common;
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.CyclingData;
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.FileDownload;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.exception.FitParseException;
import nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport.fit.FitAsyncProcessor;
import nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport.fit.FitImporter;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.notifications.GBProgressNotification;
public class IGPSportDownloadManager {
@@ -59,15 +62,23 @@ public class IGPSportDownloadManager {
private Boolean downloadInProgress = false;
private Boolean firstChunk = true;
private FitImporter fitImporter;
private GBProgressNotification transferNotification;
private List<File> filesToProcess = new ArrayList<>();
int pbSize=0;
public IGPSportDownloadManager(IGPSportDeviceSupport support) {
this.support = support;
recievingDataBuffer = new ByteArrayOutputStream();
}
public void setContext(Context context) {
this.transferNotification = new GBProgressNotification(context, GB.NOTIFICATION_CHANNEL_ID_TRANSFER);
}
public void setFilesAvaliable(byte[] pbData) throws InvalidProtocolBufferException {
List<CyclingData.cycling_data_file_flag_message> message = CyclingData.cycling_data_msg.parseFrom(pbData).getCyclingDataFileFlagMsgList();
@@ -76,6 +87,7 @@ public class IGPSportDownloadManager {
}
LOG.info("Found " + message.size() + " files");
filesToProcess.clear();
syncNextFile();
}
@@ -83,11 +95,26 @@ public class IGPSportDownloadManager {
public void syncNextFile() {
if (avaliableActivityFiles.isEmpty()) {
LOG.info("No files to sync");
if (support.getDevice().isBusy() ) {
support.getDevice().unsetBusyTask();
GB.signalActivityDataFinish(support.getDevice());
support.getDevice().sendDeviceUpdateIntent(getContext());
}
transferNotification.start(R.string.busy_task_processing_files, 0, filesToProcess.size());
final FitAsyncProcessor fitAsyncProcessor = new FitAsyncProcessor(getContext(), support.getDevice());
fitAsyncProcessor.process(filesToProcess, new FitAsyncProcessor.Callback() {
@Override
public void onProgress(final int i) {
transferNotification.setTotalProgress(i);
}
@Override
public void onFinish() {
support.getDevice().unsetBusyTask();
GB.signalActivityDataFinish(support.getDevice());
transferNotification.finish();
support.getDevice().sendDeviceUpdateIntent(getContext());
}
});
return;
} else {
if (!support.getDevice().isBusy() ) {
@@ -117,7 +144,7 @@ public class IGPSportDownloadManager {
recievingDataBuffer.reset();
downloadInProgress = true;
firstChunk = true;
fitImporter = new FitImporter(support.getContext(), support.getDevice());
}
public void addData(byte[] data) {
@@ -151,11 +178,9 @@ public class IGPSportDownloadManager {
FileDownload.file_download pbInfo = FileDownload.file_download.parseFrom(pbData);
FileUtils.copyStreamToFile(new ByteArrayInputStream(recievingDataBuffer.toByteArray(), 20+4+pbSize, pbInfo.getFileSize()), outputFile);
outputFile.setLastModified(garminTimestampToJavaMillis(downloadingFile.getGarminTimeStamp()));
fitImporter.importFile(outputFile);
filesToProcess.add(outputFile);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (FitParseException e) {
throw new RuntimeException(e);
}
syncNextFile();
@@ -0,0 +1,81 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport.fit;
import android.content.Context;
import android.os.Handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.PendingFileProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport.fit.FitImporter;
public class FitAsyncProcessor {
private static final Logger LOG = LoggerFactory.getLogger(FitAsyncProcessor.class);
private static final AtomicLong THREAD_COUNTER = new AtomicLong(0L);
private final Context context;
private final GBDevice gbDevice;
private final Handler handler;
public FitAsyncProcessor(final Context context, final GBDevice gbDevice) {
this.context = context;
this.gbDevice = gbDevice;
this.handler = new Handler(context.getMainLooper());
}
/**
* Process a list of files asynchronously. Callback is executed on the UI thread.
*/
public void process(final List<File> files, final Callback callback) {
LOG.debug("Starting processor for {} files", files.size());
new Thread(() -> {
try {
int i = 0;
for (final File file : files) {
i++;
LOG.debug("Parsing {}", file);
final int finalI = i;
FitAsyncProcessor.this.handler.post(() -> callback.onProgress(finalI));
try {
FitImporter fitImporter = new FitImporter(context, gbDevice);
fitImporter.importFile(file);
} catch (final Exception ex) {
LOG.error("Exception while importing {}", file, ex);
continue; // do not remove from pending files
}
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final PendingFileProvider pendingFileProvider = new PendingFileProvider(gbDevice, session);
pendingFileProvider.removePendingFile(file.getPath());
} catch (final Exception e) {
LOG.error("Exception while removing pending file {}", file, e);
}
}
} catch (final Exception e) {
LOG.error("Failed to parse from storage", e);
}
FitAsyncProcessor.this.handler.post(callback::onFinish);
}, "FitAsyncProcessor_" + THREAD_COUNTER.getAndIncrement()).start();
}
public interface Callback {
void onProgress(final int perc);
void onFinish();
}
}