package cgeo.geocaching.files;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.List;
import cgeo.geocaching.R;
import cgeo.geocaching.activity.ActivityMixin;
import cgeo.geocaching.activity.Progress;
import cgeo.geocaching.ui.dialog.Dialogs;
import cgeo.geocaching.utils.DisposableHandler;
import cgeo.geocaching.utils.Log;
public class GPXImporter {
static final int IMPORT_STEP_START = 0;
static final int IMPORT_STEP_READ_FILE = 1;
static final int IMPORT_STEP_READ_WPT_FILE = 2;
static final int IMPORT_STEP_STORE_STATIC_MAPS = 4;
static final int IMPORT_STEP_FINISHED = 5;
static final int IMPORT_STEP_FINISHED_WITH_ERROR = 6;
static final int IMPORT_STEP_CANCEL = 7;
static final int IMPORT_STEP_CANCELED = 8;
static final int IMPORT_STEP_STATIC_MAPS_SKIPPED = 9;
public static final String GPX_FILE_EXTENSION = ".gpx";
public static final String LOC_FILE_EXTENSION = ".loc";
public static final String ZIP_FILE_EXTENSION = ".zip";
public static final String COMPRESSED_GPX_FILE_EXTENSION = ".ggz";
public static final String WAYPOINTS_FILE_SUFFIX = "-wpts";
public static final String WAYPOINTS_FILE_SUFFIX_AND_EXTENSION = WAYPOINTS_FILE_SUFFIX + GPX_FILE_EXTENSION;
private static final List<String> GPX_MIME_TYPES = Arrays.asList("text/xml", "application/xml");
private static final List<String> ZIP_MIME_TYPES = Arrays.asList("application/zip", "application/x-compressed", "application/x-zip-compressed", "application/x-zip", "application/octet-stream");
private final Progress progress = new Progress(true);
private final int listId;
private final Activity fromActivity;
private final Handler importFinishedHandler;
private final DisposableHandler progressHandler = new ProgressHandler(progress);
private final Handler importStepHandler;
public GPXImporter(final Activity fromActivity, final int listId, final Handler importFinishedHandler) {
this.listId = listId;
this.fromActivity = fromActivity;
this.importFinishedHandler = importFinishedHandler;
importStepHandler = new ImportStepHandler(this, fromActivity);
}
/**
* Import GPX file. Currently supports *.gpx, *.zip (containing gpx files, e.g. PQ queries), *.ggz or *.loc files.
*
* @param file
* the file to import
*/
public void importGPX(final File file) {
if (StringUtils.endsWithIgnoreCase(file.getName(), GPX_FILE_EXTENSION)) {
new ImportGpxFileThread(file, listId, importStepHandler, progressHandler).start();
} else if (StringUtils.endsWithIgnoreCase(file.getName(), ZIP_FILE_EXTENSION) || StringUtils.endsWithIgnoreCase(file.getName(), COMPRESSED_GPX_FILE_EXTENSION)) {
new ImportGpxZipFileThread(file, listId, importStepHandler, progressHandler).start();
} else {
new ImportLocFileThread(file, listId, importStepHandler, progressHandler).start();
}
}
/**
* Import GPX file from URI.
*
* @param uri
* URI of the file to import
*/
public void importGPX(final Uri uri, @Nullable final String mimeType, @Nullable final String pathName) {
final ContentResolver contentResolver = fromActivity.getContentResolver();
Log.i("importGPX: " + uri + ", mimetype=" + mimeType);
FileType fileType = new FileTypeDetector(uri, contentResolver)
.getFileType();
if (fileType == FileType.UNKNOWN) {
fileType = getFileTypeFromPathName(pathName);
}
if (fileType == FileType.UNKNOWN) {
fileType = getFileTypeFromMimeType(mimeType);
}
if (fileType == FileType.UNKNOWN && uri != null) {
fileType = getFileTypeFromPathName(uri.toString());
}
final AbstractImportThread importer = getImporterFromFileType(uri, contentResolver,
fileType);
if (importer != null) {
importer.start();
} else {
importFinished();
}
}
@NonNull
private static FileType getFileTypeFromPathName(
final String pathName) {
if (StringUtils.endsWithIgnoreCase(pathName, GPX_FILE_EXTENSION)) {
return FileType.GPX;
}
if (StringUtils.endsWithIgnoreCase(pathName, LOC_FILE_EXTENSION)) {
return FileType.LOC;
}
return FileType.UNKNOWN;
}
@NonNull
private static FileType getFileTypeFromMimeType(
final String mimeType) {
if (GPX_MIME_TYPES.contains(mimeType)) {
return FileType.GPX;
}
if (ZIP_MIME_TYPES.contains(mimeType)) {
return FileType.ZIP;
}
return FileType.UNKNOWN;
}
private AbstractImportThread getImporterFromFileType(final Uri uri,
final ContentResolver contentResolver, final 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();
final String mimeType = intent.getType();
importGPX(uri, mimeType, null);
}
private static final class ProgressHandler extends DisposableHandler {
private final WeakReference<Progress> progressRef;
ProgressHandler(final Progress progress) {
progressRef = new WeakReference<>(progress);
}
@Override
public void handleRegularMessage(final Message msg) {
final Progress progress = progressRef.get();
if (progress != null) {
progress.setProgress(msg.arg1);
}
}
}
private static final class ImportStepHandler extends Handler {
private final Activity fromActivity;
private final Handler importFinishedHandler;
private final Progress progress;
private final WeakReference<DisposableHandler> progressHandlerRef;
private final Resources res;
ImportStepHandler(final GPXImporter importer, final Activity fromActivity) {
this.fromActivity = importer.fromActivity;
this.importFinishedHandler = importer.importFinishedHandler;
this.progress = importer.progress;
progressHandlerRef = new WeakReference<>(importer.progressHandler);
res = fromActivity.getResources();
}
private void disposeProgressHandler() {
final DisposableHandler progressHandler = progressHandlerRef.get();
if (progressHandler != null) {
progressHandler.dispose();
}
}
private void importFinished() {
if (importFinishedHandler != null) {
importFinishedHandler.sendEmptyMessage(0);
}
}
@Override
public void handleMessage(final Message msg) {
switch (msg.what) {
case IMPORT_STEP_START:
final Message cancelMessage = obtainMessage(IMPORT_STEP_CANCEL);
progress.show(fromActivity, res.getString(R.string.gpx_import_title_reading_file), res.getString(R.string.gpx_import_loading_caches_with_filename, msg.obj), ProgressDialog.STYLE_HORIZONTAL, cancelMessage);
break;
case IMPORT_STEP_READ_FILE:
case IMPORT_STEP_READ_WPT_FILE:
progress.setMessage(res.getString(msg.arg1, msg.obj));
progress.setMaxProgressAndReset(msg.arg2);
break;
case IMPORT_STEP_STORE_STATIC_MAPS:
progress.dismiss();
final Message skipMessage = obtainMessage(IMPORT_STEP_STATIC_MAPS_SKIPPED, msg.arg2, 0, msg.obj);
progress.show(fromActivity, res.getString(R.string.gpx_import_title_static_maps), res.getString(R.string.gpx_import_store_static_maps), ProgressDialog.STYLE_HORIZONTAL, skipMessage);
progress.setMaxProgressAndReset(msg.arg2);
break;
case IMPORT_STEP_STATIC_MAPS_SKIPPED:
progress.dismiss();
disposeProgressHandler();
Dialogs.message(fromActivity, R.string.gpx_import_title_caches_imported, res.getString(R.string.gpx_import_static_maps_skipped) + ", " + res.getString(R.string.gpx_import_caches_imported_with_filename, msg.arg1, msg.obj));
importFinished();
break;
case IMPORT_STEP_FINISHED:
progress.dismiss();
Dialogs.message(fromActivity, R.string.gpx_import_title_caches_imported, res.getString(R.string.gpx_import_caches_imported_with_filename, msg.arg1, msg.obj));
importFinished();
break;
case IMPORT_STEP_FINISHED_WITH_ERROR:
progress.dismiss();
Dialogs.message(fromActivity, R.string.gpx_import_title_caches_import_failed, res.getString(msg.arg1) + "\n\n" + msg.obj);
importFinished();
break;
case IMPORT_STEP_CANCEL:
progress.dismiss();
disposeProgressHandler();
break;
case IMPORT_STEP_CANCELED:
ActivityMixin.showShortToast(fromActivity, res.getString(R.string.gpx_import_canceled_with_filename, msg.obj));
importFinished();
break;
default:
break;
}
}
}
/**
* @param gpxfile
* the gpx file
* @return the expected file name of the waypoints file
*/
static String getWaypointsFileNameForGpxFile(final File gpxfile) {
if (gpxfile == null || !gpxfile.canRead()) {
return null;
}
final File dir = gpxfile.getParentFile();
final String[] filenameList = dir.list();
if (filenameList == null) {
return null;
}
final String gpxFileName = gpxfile.getName();
for (final String filename : filenameList) {
if (!StringUtils.containsIgnoreCase(filename, WAYPOINTS_FILE_SUFFIX)) {
continue;
}
final String expectedGpxFileName = StringUtils.substringBeforeLast(filename, WAYPOINTS_FILE_SUFFIX)
+ StringUtils.substringAfterLast(filename, WAYPOINTS_FILE_SUFFIX);
if (gpxFileName.equals(expectedGpxFileName)) {
return filename;
}
}
return null;
}
protected void importFinished() {
if (importFinishedHandler != null) {
importFinishedHandler.sendEmptyMessage(0);
}
}
}