aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBananeweizen <bananeweizen@gmx.de>2014-05-17 13:04:29 +0200
committerBananeweizen <bananeweizen@gmx.de>2014-05-17 13:04:29 +0200
commit02004207ed83c3996a5fdf7a765ba9f8264010d6 (patch)
treee86166aef759bafd9bfdea2c1b4b221145f08224
parent496878826d638367c129b02e66f992202e0d36c9 (diff)
downloadcgeo-02004207ed83c3996a5fdf7a765ba9f8264010d6.zip
cgeo-02004207ed83c3996a5fdf7a765ba9f8264010d6.tar.gz
cgeo-02004207ed83c3996a5fdf7a765ba9f8264010d6.tar.bz2
fix #3823: detect import file type by content
-rw-r--r--main/src/cgeo/geocaching/files/FileType.java8
-rw-r--r--main/src/cgeo/geocaching/files/FileTypeDetector.java77
-rw-r--r--main/src/cgeo/geocaching/files/GPXImporter.java100
-rw-r--r--tests/src/cgeo/geocaching/files/FileTypeDetectorTest.java51
-rw-r--r--tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java7
5 files changed, 209 insertions, 34 deletions
diff --git a/main/src/cgeo/geocaching/files/FileType.java b/main/src/cgeo/geocaching/files/FileType.java
new file mode 100644
index 0000000..ef62351
--- /dev/null
+++ b/main/src/cgeo/geocaching/files/FileType.java
@@ -0,0 +1,8 @@
+package cgeo.geocaching.files;
+
+public enum FileType {
+ UNKNOWN,
+ LOC,
+ GPX,
+ ZIP
+}
diff --git a/main/src/cgeo/geocaching/files/FileTypeDetector.java b/main/src/cgeo/geocaching/files/FileTypeDetector.java
new file mode 100644
index 0000000..389b83a
--- /dev/null
+++ b/main/src/cgeo/geocaching/files/FileTypeDetector.java
@@ -0,0 +1,77 @@
+package cgeo.geocaching.files;
+
+import cgeo.geocaching.utils.Log;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class FileTypeDetector {
+
+ private final ContentResolver contentResolver;
+ private final Uri uri;
+
+ public FileTypeDetector(Uri uri, ContentResolver contentResolver) {
+ this.uri = uri;
+ this.contentResolver = contentResolver;
+ }
+
+ public @NonNull FileType getFileType() {
+ InputStream is = null;
+ BufferedReader reader = null;
+ FileType type = FileType.UNKNOWN;
+ try {
+ is = contentResolver.openInputStream(uri);
+ if (is == null) {
+ return FileType.UNKNOWN;
+ }
+ reader = new BufferedReader(new InputStreamReader(is));
+ type = detectHeader(reader);
+ reader.close();
+ } catch (FileNotFoundException e) {
+ Log.e("FileTypeDetector", e);
+ } catch (IOException e) {
+ Log.e("FileTypeDetector", e);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ IOUtils.closeQuietly(is);
+ }
+ return type;
+ }
+
+ private static FileType detectHeader(BufferedReader reader)
+ throws IOException {
+ String line = reader.readLine();
+ if (isZip(line)) {
+ return FileType.ZIP;
+ }
+ // scan at most 5 lines of a GPX file
+ for (int i = 0; i < 5; i++) {
+ line = StringUtils.trim(line);
+ if (StringUtils.contains(line, "<loc")) {
+ return FileType.LOC;
+ }
+ if (StringUtils.contains(line, "<gpx")) {
+ return FileType.GPX;
+ }
+ line = reader.readLine();
+ }
+ return FileType.UNKNOWN;
+ }
+
+ private static boolean isZip(String line) {
+ return StringUtils.length(line) >= 4
+ && StringUtils.startsWith(line, "PK") && line.charAt(2) == 3
+ && line.charAt(3) == 4;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/files/GPXImporter.java b/main/src/cgeo/geocaching/files/GPXImporter.java
index cd2f445..f87e6b9 100644
--- a/main/src/cgeo/geocaching/files/GPXImporter.java
+++ b/main/src/cgeo/geocaching/files/GPXImporter.java
@@ -14,6 +14,7 @@ import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.Log;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import android.app.Activity;
@@ -93,46 +94,77 @@ public class GPXImporter {
*
* @param uri
* URI of the file to import
- * @param knownMimeType
- * @param knownPathName
+ * @param mimeType
+ * @param pathName
*/
- public void importGPX(final Uri uri, final @Nullable String knownMimeType, final @Nullable String knownPathName) {
+ public void importGPX(final Uri uri, final @Nullable String mimeType, final @Nullable String pathName) {
final ContentResolver contentResolver = fromActivity.getContentResolver();
- String mimeType = knownMimeType;
- final String pathName = knownPathName != null ? knownPathName : uri.getPath();
-
- // if mimetype can't be determined (e.g. for emulators email app), derive it from uri file extension
- // contentResolver.getType(uri) doesn't help but throws exception for emulators email app
- // Permission Denial: reading com.android.email.provider.EmailProvider uri
- // Google search says: there is no solution for this problem
- // Gmail doesn't work at all, see #967
- if (mimeType == null) {
- if (StringUtils.endsWithIgnoreCase(pathName, GPX_FILE_EXTENSION) || StringUtils.endsWithIgnoreCase(pathName, LOC_FILE_EXTENSION)) {
- mimeType = "application/xml";
- } else {
- // if we can't determine a better type, default to zip import
- // emulator email sends e.g. content://com.android.email.attachmentprovider/1/1/RAW, mimetype=null
- mimeType = "application/zip";
- }
- }
Log.i("importGPX: " + uri + ", mimetype=" + mimeType);
- if (GPX_MIME_TYPES.contains(mimeType)) {
- if (StringUtils.endsWithIgnoreCase(pathName, LOC_FILE_EXTENSION)) {
- new ImportLocAttachmentThread(uri, contentResolver, listId, importStepHandler, progressHandler).start();
- } else {
- new ImportGpxAttachmentThread(uri, contentResolver, listId, importStepHandler, progressHandler).start();
- }
- } else if (ZIP_MIME_TYPES.contains(mimeType)) {
- new ImportGpxZipAttachmentThread(uri, contentResolver, listId, importStepHandler, progressHandler).start();
- } else {
- importFinished();
+ @NonNull
+ FileType fileType = new FileTypeDetector(uri, contentResolver)
+ .getFileType();
+
+ if (fileType == FileType.UNKNOWN) {
+ fileType = getFileTypeFromPathName(pathName);
+ }
+ if (fileType == FileType.UNKNOWN) {
+ fileType = getFileTypeFromMimeType(mimeType);
+ }
+
+ ImportThread importer = getImporterFromFileType(uri, contentResolver,
+ fileType);
+
+ if (importer != null) {
+ importer.start();
+ } else {
+ importFinished();
+ }
+ }
+
+ private static @NonNull FileType getFileTypeFromPathName(
+ final String pathName) {
+ if (StringUtils.endsWithIgnoreCase(pathName, GPX_FILE_EXTENSION)) {
+ return FileType.GPX;
}
- }
- /**
- * Import GPX provided via intent of activity that instantiated this GPXImporter.
- */
+ if (StringUtils.endsWithIgnoreCase(pathName, LOC_FILE_EXTENSION)) {
+ return FileType.LOC;
+ }
+ return FileType.UNKNOWN;
+ }
+
+ private static @NonNull FileType getFileTypeFromMimeType(
+ final String mimeType) {
+ if (GPX_MIME_TYPES.contains(mimeType)) {
+ return FileType.GPX;
+ } else if (ZIP_MIME_TYPES.contains(mimeType)) {
+ return FileType.ZIP;
+ }
+ return FileType.UNKNOWN;
+ }
+
+ private ImportThread getImporterFromFileType(Uri uri,
+ ContentResolver contentResolver, FileType fileType) {
+ switch (fileType) {
+ case ZIP:
+ return new ImportGpxZipAttachmentThread(uri, contentResolver,
+ listId, importStepHandler, progressHandler);
+ case GPX:
+ return new ImportGpxAttachmentThread(uri, contentResolver, listId,
+ importStepHandler, progressHandler);
+ case LOC:
+ return new ImportLocAttachmentThread(uri, contentResolver, listId,
+ importStepHandler, progressHandler);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Import GPX provided via intent of activity that instantiated this
+ * GPXImporter.
+ */
public void importGPX() {
final Intent intent = fromActivity.getIntent();
final Uri uri = intent.getData();
diff --git a/tests/src/cgeo/geocaching/files/FileTypeDetectorTest.java b/tests/src/cgeo/geocaching/files/FileTypeDetectorTest.java
new file mode 100644
index 0000000..ec7b3a8
--- /dev/null
+++ b/tests/src/cgeo/geocaching/files/FileTypeDetectorTest.java
@@ -0,0 +1,51 @@
+package cgeo.geocaching.files;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase;
+import cgeo.geocaching.test.R;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+
+public class FileTypeDetectorTest extends AbstractResourceInstrumentationTestCase {
+
+ private static class FileContentProvider extends ContentResolver {
+
+ public FileContentProvider() {
+ super(null);
+ }
+
+ }
+
+ public void testUnknown() throws Exception {
+ assertFileType(R.raw.gc2cjpf_html, FileType.UNKNOWN);
+ assertFileType(R.raw.map1, FileType.UNKNOWN);
+ }
+
+ public void testLoc() throws Exception {
+ assertFileType(R.raw.gc1bkp3_loc, FileType.LOC);
+ assertFileType(R.raw.oc5952_loc, FileType.LOC);
+ assertFileType(R.raw.waymarking_loc, FileType.LOC);
+ }
+
+ public void testGpx() throws Exception {
+ assertFileType(R.raw.gc1bkp3_gpx100, FileType.GPX);
+ assertFileType(R.raw.gc1bkp3_gpx101, FileType.GPX);
+ assertFileType(R.raw.oc5952_gpx, FileType.GPX);
+ assertFileType(R.raw.renamed_waypoints_wpts, FileType.GPX);
+ assertFileType(R.raw.waymarking_gpx, FileType.GPX);
+ }
+
+ public void testZip() throws Exception {
+ assertFileType(R.raw.pq_error, FileType.ZIP);
+ assertFileType(R.raw.pq7545915, FileType.ZIP);
+ }
+
+ private void assertFileType(final int resourceId, final @NonNull FileType fileType) {
+ final Uri resourceURI = getResourceURI(resourceId);
+ assertThat(new FileTypeDetector(resourceURI, new FileContentProvider()).getFileType()).isEqualTo(fileType);
+ }
+}
diff --git a/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java b/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java
index df8dc1f..cbad794 100644
--- a/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java
+++ b/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java
@@ -12,7 +12,9 @@ import cgeo.geocaching.files.GPX10Parser;
import cgeo.geocaching.files.ParserException;
import cgeo.geocaching.list.StoredList;
+import android.content.ContentResolver;
import android.content.res.Resources;
+import android.net.Uri;
import android.test.InstrumentationTestCase;
import java.io.File;
@@ -102,4 +104,9 @@ public abstract class AbstractResourceInstrumentationTestCase extends Instrument
instream.close();
}
}
+
+ protected Uri getResourceURI(int resId) {
+ Resources resources = getInstrumentation().getContext().getResources();
+ return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + resources.getResourcePackageName(resId) + '/' + resources.getResourceTypeName(resId) + '/' + resources.getResourceEntryName(resId));
+ }
}