diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java index 3446e5873..00dd2393b 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java @@ -383,7 +383,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track return null; } - File file = FileUtils.getPhotoFileIfExists(context, importTrackId, Uri.parse(externalPhotoUrl)); + String importFileName = KmzTrackImporter.importNameForFilename(externalPhotoUrl); + File file = FileUtils.getPhotoFileIfExists(context, importTrackId, Uri.parse(importFileName)); if (file != null) { Uri photoUri = FileUtils.getUriForFile(context, file); return "" + photoUri; diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportAsyncTask.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportAsyncTask.java index fa8c55c95..96a701d9b 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportAsyncTask.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportAsyncTask.java @@ -99,7 +99,7 @@ class ImportAsyncTask extends AsyncTask { for (DocumentFile candidate : file.listFiles()) { if (!candidate.isDirectory()) { String extension = FileUtils.getExtension(candidate.getName()); - if (trackFileFormat.getExtension().equals(extension)) { + if (extension != null && trackFileFormat.getExtension().equals(extension)) { files.add(candidate); } } else { diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java index a69059fc8..0763446af 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java @@ -26,9 +26,12 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import de.dennisguse.opentracks.content.data.Waypoint; import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter; import de.dennisguse.opentracks.util.FileUtils; @@ -43,6 +46,13 @@ public class KmzTrackImporter implements TrackImporter { private static final String TAG = KmzTrackImporter.class.getSimpleName(); + public static final List KMZ_IMAGES_EXT = new ArrayList<>(); + static { + KMZ_IMAGES_EXT.add("jpeg"); + KMZ_IMAGES_EXT.add("jpg"); + KMZ_IMAGES_EXT.add("png"); + } + private static final int BUFFER_SIZE = 4096; private final Context context; @@ -77,17 +87,19 @@ public class KmzTrackImporter implements TrackImporter { return -1L; } + deleteOrphanImages(context, trackId); + return trackId; } /** - * Copies KMZ images that are inside KmzTrackExporter.KMZ_IMAGES_DIR to OpenTracks external storage. + * Copies all images that are inside KMZ to OpenTracks external storage. * * @return false if there are errors or true otherwise. */ private boolean copyKmzImages() { try (InputStream inputStream = context.getContentResolver().openInputStream(uriKmzFile); - ZipInputStream zipInputStream = new ZipInputStream(inputStream)) { + ZipInputStream zipInputStream = new ZipInputStream(inputStream)) { ZipEntry zipEntry; while ((zipEntry = zipInputStream.getNextEntry()) != null) { @@ -97,9 +109,8 @@ public class KmzTrackImporter implements TrackImporter { } String fileName = zipEntry.getName(); - String prefix = KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar; - if (fileName.startsWith(prefix)) { - readImageFile(zipInputStream, fileName.substring(prefix.length())); + if (hasImageExtension(fileName)) { + readAndSaveImageFile(zipInputStream, importNameForFilename(fileName)); } zipInputStream.closeEntry(); @@ -112,6 +123,50 @@ public class KmzTrackImporter implements TrackImporter { } } + /** + * From path fileName generates an import unique name and returns it. + * The name generator is simple: change the path fileName with '-' instead of File.separatorChar. + * + * @param fileName the file name. + */ + public static String importNameForFilename(String fileName) { + // TODO this tricky code for maintain backward compatibility must be deleted some day. + /* + * In versions before v3.5.0 photo URL in KML files were wrong. + * For compatibility reasons it checks if fileName begins with "content://" or "file://". + * All fileName begins with "content:/" or "file://" are cooked. + * We cannot guess what's the folder name where images are so we use "images" that was the folder name expected in versions before v3.5.0. + */ + if (fileName.startsWith("content://") || fileName.startsWith("file://")) { + fileName = "images/" + fileName.substring(fileName.lastIndexOf(File.separatorChar) + 1); + } + + return fileName.replace(File.separatorChar, '-'); + } + + /** + * Returns true if fileName ends with some of the KMZ_IMAGES_EXT suffixes. + * Otherwise returns false. + */ + private boolean hasImageExtension(String fileName) { + if (fileName == null) { + return false; + } + + String fileExt = FileUtils.getExtension(fileName.toLowerCase()); + if (fileExt == null) { + return false; + } + + for (String ext : KMZ_IMAGES_EXT) { + if (fileExt.equals(ext)) { + return true; + } + } + + return false; + } + /** * Finds KmzTrackExporter.KMZ_KML_FILE file inside kmz file (inputStream) and it parses it. * @@ -148,6 +203,40 @@ public class KmzTrackImporter implements TrackImporter { } } + /** + * Deletes all images that remained in external storage that doesn't have waypoint (marker) associated. + * + * @param context the Context object. + * @param trackId the id of the Track. + */ + private void deleteOrphanImages(Context context, long trackId) { + if (trackId != 1L) { + // 1.- Gets all photo names in the waypoints of the track identified by id. + ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); + List waypoints = contentProviderUtils.getWaypoints(trackId); + List photosName = new ArrayList<>(); + for (Waypoint w : waypoints) { + if (w.hasPhoto()) { + String photoUrl = Uri.decode(w.getPhotoUrl()); + photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1)); + } + } + + // 2.- Deletes all orphan photos from external storage. + File dir = FileUtils.getPhotoDir(context, trackId); + if (dir.exists() && dir.isDirectory()) { + for (File file : dir.listFiles()) { + if (!photosName.contains(file.getName())) { + file.delete(); + } + } + if (dir.listFiles().length == 0) { + dir.delete(); + } + } + } + } + /** * Cleans up import. * @@ -201,12 +290,12 @@ public class KmzTrackImporter implements TrackImporter { } /** - * Reads an image file. + * Reads an image file (zipInputStream) and save it in a file called fileName inside photo folder. * * @param zipInputStream the zip input stream * @param fileName the file name */ - private void readImageFile(ZipInputStream zipInputStream, String fileName) throws IOException { + private void readAndSaveImageFile(ZipInputStream zipInputStream, String fileName) throws IOException { if (importTrackId == -1L || fileName.equals("")) { return; } diff --git a/src/main/java/de/dennisguse/opentracks/util/FileUtils.java b/src/main/java/de/dennisguse/opentracks/util/FileUtils.java index 9772bad29..c480a8979 100644 --- a/src/main/java/de/dennisguse/opentracks/util/FileUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/FileUtils.java @@ -95,11 +95,15 @@ public class FileUtils { /** * Gets the extension from a file name. - * Returns null if there is no extension. + * Returns null if there is no extension or fileName is null. * * @param fileName the file name */ public static String getExtension(String fileName) { + if (fileName == null) { + return null; + } + int index = fileName.lastIndexOf('.'); if (index == -1) { return null;