Track is now a record.

This commit is contained in:
Dennis Guse
2025-11-27 19:35:05 +01:00
parent 6a262426a5
commit 990c91f978
51 changed files with 772 additions and 523 deletions
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.io.file;
import androidx.annotation.NonNull;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
@@ -12,8 +11,10 @@ import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.util.FileUtils;
public class TrackFilenameGenerator {
@@ -41,11 +42,11 @@ public class TrackFilenameGenerator {
public String format(@NonNull Track track, @NonNull TrackFileFormat trackFileFormat) {
Map<String, String> values = new HashMap<>();
values.put(UUID_KEY, track.getUuid().toString().substring(0, 8));
values.put(TRACKNAME_KEY, track.getName());
values.put(ACTIVITY_TYPE_KEY, track.getActivityTypeLocalized());
values.put(STARTTIME_TIME_KEY, track.getStartTime().toLocalTime().toString());
values.put(STARTTIME_DATE_KEY, track.getStartTime().toLocalDate().toString());
values.put(UUID_KEY, track.uuid().toString().substring(0, 8));
values.put(TRACKNAME_KEY, track.name());
values.put(ACTIVITY_TYPE_KEY, track.activityTypeLocalized());
values.put(STARTTIME_TIME_KEY, track.startTime().toLocalTime().toString());
values.put(STARTTIME_DATE_KEY, track.stopTime().toLocalDate().toString());
return format(format(template, values), trackFileFormat);
}
@@ -90,9 +91,16 @@ public class TrackFilenameGenerator {
}
public String getExample() {
Track track = new Track(ZoneOffset.UTC, Statistics.DEFAULT);
track.setName("Berlin");
track.setUuid(UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"));
Track track = new Track(
null,
UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"),
"Berlin",
"",
"",
ActivityType.CYCLING,
ZoneOffset.UTC,
Statistics.DEFAULT
);
return format(track, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES);
}
@@ -111,7 +111,7 @@ public class CSVTrackExporter implements TrackExporter {
boolean headerWritten = false;
for (Track track : tracks) {
columns.get(0).extractor = t -> quote(StringUtils.formatDateTimeIso8601(t.getTime(), track.getZoneOffset()));
columns.get(0).extractor = t -> quote(StringUtils.formatDateTimeIso8601(t.getTime(), track.zoneOffset()));
if (!headerWritten) {
writeHeader(columns);
@@ -131,7 +131,7 @@ public class CSVTrackExporter implements TrackExporter {
}
private void writeTrackPoints(List<Column> columns, Track track) throws InterruptedException {
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.getId(), null)) {
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) {
while (trackPointIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException();
@@ -40,6 +40,7 @@ import de.dennisguse.opentracks.AbstractActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.databinding.ExportActivityBinding;
import de.dennisguse.opentracks.io.file.ErrorListDialog;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
@@ -222,9 +223,9 @@ public class ExportActivity extends AbstractActivity {
exportTasks = new ArrayList<>();
if (allInOneFile) {
String filename = "OpenTracks-Backup";
exportTasks.add(new ExportTask(filename, trackFileFormat, tracks.stream().map(Track::getId).toList()));
exportTasks.add(new ExportTask(filename, trackFileFormat, tracks.stream().map(Track::id).toList()));
} else {
exportTasks.addAll(tracks.stream().map(it -> new ExportTask(null, trackFileFormat, List.of(it.getId()))).toList());
exportTasks.addAll(tracks.stream().map(it -> new ExportTask(null, trackFileFormat, List.of(it.id()))).toList());
}
trackExportTotalCount = exportTasks.size();
}
@@ -362,7 +363,7 @@ public class ExportActivity extends AbstractActivity {
if (exportTask.isMultiExport()) {
name = exportTask.getFilename();
} else {
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).getName();
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).name();
}
Log.e(TAG, "Error exporting " + name + ": " + errorMessage);
trackErrors.add(name);
@@ -402,7 +403,7 @@ public class ExportActivity extends AbstractActivity {
if (exportTask.isMultiExport()) {
name = exportTask.getFilename();
} else {
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).getName();
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).name();
}
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, name));
setConflictVisibility(View.VISIBLE);
@@ -25,6 +25,7 @@ import java.util.List;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
import de.dennisguse.opentracks.settings.PreferencesUtils;
@@ -17,6 +17,7 @@
package de.dennisguse.opentracks.io.file.exporter;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -128,7 +129,7 @@ public class GPXTrackExporter implements TrackExporter {
LinkedList<TrackPoint> sensorPoints = new LinkedList<>();
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.getId(), null)) {
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) {
while (trackPointIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException();
@@ -152,7 +153,7 @@ public class GPXTrackExporter implements TrackExporter {
writeOpenSegment();
wroteSegment = true;
if (trackPoint.hasLocation()) {
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
trackDistance = trackDistance.plus(writeTrackPoint(track.zoneOffset(), trackPoint, sensorPoints, trackDistance));
sensorPoints.clear();
} else {
sensorPoints.add(trackPoint);
@@ -165,7 +166,7 @@ public class GPXTrackExporter implements TrackExporter {
wroteSegment = true;
}
if (trackPoint.hasLocation()) {
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
trackDistance = trackDistance.plus(writeTrackPoint(track.zoneOffset(), trackPoint, sensorPoints, trackDistance));
sensorPoints.clear();
} else {
sensorPoints.add(trackPoint);
@@ -238,12 +239,12 @@ public class GPXTrackExporter implements TrackExporter {
}
private void writeMarkers(Track track) throws InterruptedException {
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.getId())) {
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.id())) {
while (markerIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException();
writeMarker(track.getZoneOffset(), markerIterator.next());
writeMarker(track.zoneOffset(), markerIterator.next());
}
}
}
@@ -262,19 +263,19 @@ public class GPXTrackExporter implements TrackExporter {
private void writeBeginTrack(Track track) {
printWriter.println("<trk>");
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(track.getActivityType().getId()) + "</type>");
printWriter.println("<name>" + StringUtils.formatCData(track.name()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(track.description()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(track.activityType().getId()) + "</type>");
printWriter.println("<extensions>");
printWriter.println("<topografix:color>c0c0c0</topografix:color>");
printWriter.println("<opentracks:trackid>" + track.getUuid() + "</opentracks:trackid>");
printWriter.println("<opentracks:trackid>" + track.uuid() + "</opentracks:trackid>");
if (track.getActivityTypeLocalized() != null || !track.getActivityTypeLocalized().isBlank()) {
printWriter.println("<opentracks:typeTranslated>" + StringUtils.formatCData(track.getActivityTypeLocalized()) + "</opentracks:typeTranslated>");
if (!TextUtils.isEmpty(track.activityTypeLocalized())) {
printWriter.println("<opentracks:typeTranslated>" + StringUtils.formatCData(track.activityTypeLocalized()) + "</opentracks:typeTranslated>");
}
Statistics trackStatistics = track.getStatistics();
Statistics trackStatistics = track.statistics();
printWriter.println("<gpxtrkx:TrackStatsExtension>");
printWriter.println("<gpxtrkx:Distance>" + trackStatistics.totalDistance().toM() + "</gpxtrkx:Distance>");
printWriter.println("<gpxtrkx:TimerTime>" + trackStatistics.totalDuration().getSeconds() + "</gpxtrkx:TimerTime>");
@@ -136,7 +136,7 @@ public class KMLTrackExporter implements TrackExporter {
private void writeMarkers(Track track) throws InterruptedException {
boolean hasMarkers = false;
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.getId())) {
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.id())) {
while (markerIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException();
@@ -145,7 +145,7 @@ public class KMLTrackExporter implements TrackExporter {
hasMarkers = true;
}
writeMarker(markerIterator.next(), track.getZoneOffset());
writeMarker(markerIterator.next(), track.zoneOffset());
}
}
if (hasMarkers) {
@@ -157,7 +157,7 @@ public class KMLTrackExporter implements TrackExporter {
boolean wroteTrack = false;
boolean wroteSegment = false;
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.getId(), null)) {
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) {
while (trackPointIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException();
@@ -171,12 +171,12 @@ public class KMLTrackExporter implements TrackExporter {
case SEGMENT_START_MANUAL, SEGMENT_START_AUTOMATIC -> {
if (wroteSegment) writeCloseSegment();
writeOpenSegment();
writeTrackPoint(track.getZoneOffset(), trackPoint);
writeTrackPoint(track.zoneOffset(), trackPoint);
wroteSegment = true;
}
case SEGMENT_END_MANUAL -> {
if (!wroteSegment) writeOpenSegment();
writeTrackPoint(track.getZoneOffset(), trackPoint);
writeTrackPoint(track.zoneOffset(), trackPoint);
writeCloseSegment();
wroteSegment = false;
}
@@ -187,7 +187,7 @@ public class KMLTrackExporter implements TrackExporter {
wroteSegment = true;
}
writeTrackPoint(track.getZoneOffset(), trackPoint);
writeTrackPoint(track.zoneOffset(), trackPoint);
}
default ->
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
@@ -239,7 +239,7 @@ public class KMLTrackExporter implements TrackExporter {
printWriter.println("<visibility>1</visibility>");
Track track = tracks.get(0);
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
printWriter.println("<name>" + StringUtils.formatCData(track.name()) + "</name>");
printWriter.println("<atom:generator>" + StringUtils.formatCData(context.getString(R.string.app_name)) + "</atom:generator>");
writeTrackStyle();
@@ -261,7 +261,7 @@ public class KMLTrackExporter implements TrackExporter {
private void writeBeginMarkers(Track track) {
printWriter.println("<Folder>");
printWriter.println("<name>" + StringUtils.formatCData(context.getString(R.string.track_markers, track.getName())) + "</name>");
printWriter.println("<name>" + StringUtils.formatCData(context.getString(R.string.track_markers, track.name())) + "</name>");
printWriter.println("<open>1</open>");
}
@@ -291,13 +291,13 @@ public class KMLTrackExporter implements TrackExporter {
private void writeBeginTrack(Track track) {
printWriter.println("<Placemark>");
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
printWriter.println("<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
printWriter.println("<opentracks:trackid>" + track.getUuid() + "</opentracks:trackid>");
printWriter.println("<name>" + StringUtils.formatCData(track.name()) + "</name>");
printWriter.println("<description>" + StringUtils.formatCData(track.description()) + "</description>");
printWriter.println("<opentracks:trackid>" + track.uuid() + "</opentracks:trackid>");
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
writeActivityType(track.getActivityType());
writeTypeLocalized(track.getActivityTypeLocalized());
writeActivityType(track.activityType());
writeTypeLocalized(track.activityTypeLocalized());
printWriter.println("<MultiTrack>");
printWriter.println("<altitudeMode>absolute</altitudeMode>");
printWriter.println("<interpolate>1</interpolate>");
@@ -17,7 +17,6 @@
package de.dennisguse.opentracks.io.file.exporter;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
@@ -35,6 +34,7 @@ import java.util.zip.ZipOutputStream;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils;
@@ -90,14 +90,14 @@ public class KMZTrackExporter implements TrackExporter {
private void addImages(Context context, List<Track> tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
for (Track track : tracks) {
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.getId())) {
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.id())) {
while (markerIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException();
Marker marker = markerIterator.next();
if (marker.hasPhoto()) {
Uri uriPhoto = marker.getPhotoUrl();
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.getId(), uriPhoto) != null;
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.id(), uriPhoto) != null;
if (existsPhoto) {
addImage(context, zipOutputStream, uriPhoto, marker);
}
@@ -22,6 +22,7 @@ import java.io.OutputStream;
import java.util.List;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
/**
* Track exporting for exporting track to an {@link OutputStream}.
@@ -17,7 +17,6 @@
package de.dennisguse.opentracks.io.file.importer;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
@@ -31,7 +30,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -25,6 +25,7 @@ import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
@@ -146,12 +147,16 @@ public class TrackImporter {
adjustTrackPoints();
TrackStatisticsUpdater updater = new TrackStatisticsUpdater(trackPoints);
Track track = new Track(trackData.zoneOffset, updater.getTrackStatistics());
track.setName(trackData.name);
track.setUuid(trackData.uuid);
track.setDescription(trackData.description);
track.setActivityType(trackData.activityType);
track.setActivityTypeLocalized(trackData.activityTypeLocalized);
Track track = new Track(
null,
trackData.uuid,
trackData.name,
trackData.description,
trackData.activityTypeLocalized,
trackData.activityType,
trackData.zoneOffset,
updater.getTrackStatistics()
);
Track.Id trackId = contentProviderUtils.insertTrack(track);