KMZ importing: look for images anywhere in KMZ file and only images referenced remain in external storage.

This commit is contained in:
Román
2020-04-21 11:03:02 +02:00
parent 54b9c4370b
commit 653eb8320a
4 changed files with 104 additions and 10 deletions
@@ -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;
@@ -99,7 +99,7 @@ class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
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 {
@@ -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<String> 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<Waypoint> waypoints = contentProviderUtils.getWaypoints(trackId);
List<String> 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;
}
@@ -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;