From c1d8eb58b9bef4fab9d2998b6dcff8a201f8e631 Mon Sep 17 00:00:00 2001 From: Thomas Kuehne Date: Fri, 1 May 2026 10:27:43 +0000 Subject: [PATCH 1/3] Garmin: check if GPX export is enabled before generating the export ActivityTrack --- .../gadgetbridge/export/AutoGpxExporter.java | 55 +++++++++++++++---- .../devices/garmin/fit/FitImporter.java | 10 ++-- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/AutoGpxExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/AutoGpxExporter.java index 0cb9e51cac..ac82146e93 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/AutoGpxExporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/AutoGpxExporter.java @@ -1,8 +1,25 @@ +/* Copyright (C) 2026 José Rebelo, Thomas Kuehne + + 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 . */ package nodomain.freeyourgadget.gadgetbridge.export; import android.content.Context; import android.net.Uri; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.documentfile.provider.DocumentFile; @@ -30,21 +47,41 @@ import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; public class AutoGpxExporter { private static final Logger LOG = LoggerFactory.getLogger(AutoGpxExporter.class); - public static void doExport(final Context context, - final GBDevice gbDevice, - @Nullable final BaseActivitySummary summary, - final ActivityTrack activityTrack) { + public static boolean isExportEnabled(@NonNull final GBDevice gbDevice) { + return getExportDirectory(gbDevice) != null; + } + + @Nullable + public static String getExportDirectory(@NonNull final GBDevice gbDevice) { final GBPrefs prefs = GBApplication.getPrefs(); final boolean enabled = prefs.getBoolean(GBPrefs.AUTO_EXPORT_GPX_ENABLED, false); if (!enabled) { LOG.debug("Auto gpx export is disabled"); - return; + return null; } final Set selectedDevices = prefs.getStringSet(GBPrefs.AUTO_EXPORT_GPX_SELECTED_DEVICES, Collections.emptySet()); final boolean allDevices = prefs.getBoolean(GBPrefs.AUTO_EXPORT_GPX_ALL_DEVICES, true); if (!allDevices && !selectedDevices.contains(gbDevice.getAddress())) { - LOG.debug("Skipping auto gpx export - not enabled for {}", gbDevice); + LOG.debug("Auto gpx export is not enabled for {}", gbDevice); + return null; + } + + final String directory = prefs.getString(GBPrefs.AUTO_EXPORT_GPX_DIRECTORY, ""); + if (directory.isBlank()) { + LOG.warn("No auto gpx export directory specified"); + return null; + } + + return directory; + } + + public static void doExport(final Context context, + final GBDevice gbDevice, + @Nullable final BaseActivitySummary summary, + final ActivityTrack activityTrack) { + final String directory = getExportDirectory(gbDevice); + if (directory == null) { return; } @@ -55,12 +92,6 @@ public class AutoGpxExporter { return; } - final String directory = prefs.getString(GBPrefs.AUTO_EXPORT_GPX_DIRECTORY, ""); - if (directory.isBlank()) { - LOG.warn("Not auto-exporting gpx, no directory specified"); - return; - } - final String trackType; if (summary != null) { trackType = context.getString(ActivityKind.fromCode(summary.getActivityKind()).getLabel()).toLowerCase(Locale.ROOT); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java index 882e4daefa..b792d68749 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java @@ -701,10 +701,12 @@ public class FitImporter { } // Export to gpx - final FitActivityTrackProvider activityTrackProvider = new FitActivityTrackProvider(); - final ActivityTrack activityTrack = activityTrackProvider.getActivityTrack(summary); - if (activityTrack != null) { - AutoGpxExporter.doExport(context, gbDevice, summary, activityTrack); + if (AutoGpxExporter.isExportEnabled(gbDevice)) { + final FitActivityTrackProvider activityTrackProvider = new FitActivityTrackProvider(); + final ActivityTrack activityTrack = activityTrackProvider.getActivityTrack(summary); + if (activityTrack != null) { + AutoGpxExporter.doExport(context, gbDevice, summary, activityTrack); + } } } From b8061337e2c8c6e1cf1f22b6bac7975fc500595c Mon Sep 17 00:00:00 2001 From: Thomas Kuehne Date: Fri, 1 May 2026 10:28:03 +0000 Subject: [PATCH 2/3] Garmin: avoid reading the FIT file twice for auto GPX export --- .../gadgetbridge/model/FitActivityTrackProvider.java | 4 ++++ .../service/devices/garmin/fit/FitImporter.java | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/FitActivityTrackProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/FitActivityTrackProvider.java index 4fbc54a1e6..19a0664d2e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/FitActivityTrackProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/FitActivityTrackProvider.java @@ -58,7 +58,11 @@ public class FitActivityTrackProvider implements ActivityTrackProvider { LOG.error("Failed to parse fit file", e); return null; } + return getActivityTrack(summary, fitFile); + } + @Nullable + public ActivityTrack getActivityTrack(@NonNull final BaseActivitySummary summary, @NonNull final FitFile fitFile) { final ActivityTrack activityTrack = new ActivityTrack(); activityTrack.setName(summary.getName()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java index b792d68749..4d712e679b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java @@ -566,7 +566,7 @@ public class FitImporter { switch (fileId.getType()) { case ACTIVITY: - persistWorkout(finalExportFile, session, isReprocessing); + persistWorkout(finalExportFile, session, isReprocessing, fitFile); break; case MONITOR: persistActivitySamples(session); @@ -644,7 +644,7 @@ public class FitImporter { } } - private void persistWorkout(final File file, final DaoSession session, boolean isReprocessing) { + private void persistWorkout(final File file, final DaoSession session, boolean isReprocessing, FitFile fitFile) { LOG.debug("Persisting workout for {}", fileId); Long sessionStart = workoutParser.getSessionStartTime(); @@ -703,7 +703,7 @@ public class FitImporter { // Export to gpx if (AutoGpxExporter.isExportEnabled(gbDevice)) { final FitActivityTrackProvider activityTrackProvider = new FitActivityTrackProvider(); - final ActivityTrack activityTrack = activityTrackProvider.getActivityTrack(summary); + final ActivityTrack activityTrack = activityTrackProvider.getActivityTrack(summary, fitFile); if (activityTrack != null) { AutoGpxExporter.doExport(context, gbDevice, summary, activityTrack); } From f1ecc71716ca1deb20e366d83ca05332496b9fd3 Mon Sep 17 00:00:00 2001 From: Thomas Kuehne Date: Fri, 1 May 2026 10:28:25 +0000 Subject: [PATCH 3/3] Garmin: re-use FitImporter to reduce memory pressure --- .../service/devices/garmin/fit/FitAsyncProcessor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitAsyncProcessor.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitAsyncProcessor.java index 2d3665f9f3..6582dfd045 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitAsyncProcessor.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitAsyncProcessor.java @@ -38,6 +38,7 @@ public class FitAsyncProcessor { new Thread(() -> { try { + FitImporter fitImporter = null; int i = 0; for (final File file : files) { i++; @@ -47,10 +48,13 @@ public class FitAsyncProcessor { FitAsyncProcessor.this.handler.post(() -> callback.onProgress(finalI)); try { - final FitImporter fitImporter = new FitImporter(context, gbDevice); + if (fitImporter == null) { + fitImporter = new FitImporter(context, gbDevice); + } fitImporter.importFile(file, isReprocessing); } catch (final Exception ex) { LOG.error("Exception while importing {}", file, ex); + fitImporter = null; continue; // do not remove from pending files }