package me.guillaumin.android.osmtracker.osm;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import me.guillaumin.android.osmtracker.OSMTracker;
import me.guillaumin.android.osmtracker.R;
import me.guillaumin.android.osmtracker.db.DataHelper;
import me.guillaumin.android.osmtracker.db.model.Track.OSMVisibility;
import me.guillaumin.android.osmtracker.osm.ProgressMultipartEntity.ProgressListener;
import me.guillaumin.android.osmtracker.util.DialogUtils;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
/**
* Uploads a GPX file to OpenStreetMap
*
* @author Nicolas Guillaumin
*/
public class UploadToOpenStreetMapTask extends AsyncTask<Void, Void, Void> {
private static final String TAG = UploadToOpenStreetMapTask.class.getSimpleName();
private static final String GPX_MIMETYPE = "application/gpx+xml";
/** Upload progress dialog */
private ProgressDialog dialog;
private final Activity activity;
private final long trackId;
/** OAuth consumer to sign the post request */
private final CommonsHttpOAuthConsumer oAuthConsumer;
/** File to export */
private final File gpxFile;
/** Filename to use when uploading */
private final String filename;
/** Track description */
private final String description;
/** Track tags */
private final String tags;
/** Track visibility */
private final OSMVisibility visibility;
/**
* Error message, or text of the response returned by OSM
* if the request completed
*/
private String errorMsg;
/**
* Either the HTTP result code, or -1 for an internal error
*/
private int resultCode = -1;
private HttpPost request;
private HttpResponse response;
public UploadToOpenStreetMapTask(Activity activity,
long trackId, CommonsHttpOAuthConsumer oAuthConsumer,
File gpxFile, String filename,
String description, String tags, OSMVisibility visibility) {
this.activity = activity;
this.trackId = trackId;
this.filename = filename;
this.oAuthConsumer = oAuthConsumer;
this.gpxFile = gpxFile;
this.description = (description == null) ? "test" : description;
this.tags = (tags == null) ? "test" : tags;
this.visibility = (visibility == null) ? OSMVisibility.Private : visibility;
}
@Override
protected void onPreExecute() {
try {
// Prepare and OAuth-sign the request request
request = new HttpPost(OpenStreetMapConstants.Api.Gpx.CREATE);
oAuthConsumer.sign(request);
final long totalSize = gpxFile.length();
// Custom entity to display a progress bar while uploading
MultipartEntity entity = new ProgressMultipartEntity(
HttpMultipartMode.BROWSER_COMPATIBLE,
Charset.defaultCharset(),
new ProgressListener() {
@Override
public void transferred(long num) {
dialog.incrementProgressBy((int) num);
if (num >= totalSize) {
// Finish sending. Switch to an indeterminate progress
// dialog while the OSM server processes the request
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.setTitle(activity.getResources().getString(R.string.osm_upload_waiting_response));
}
});
}
}
});
// API parameters
entity.addPart(OpenStreetMapConstants.Api.Gpx.Parameters.FILE, new FileBody(gpxFile, filename, GPX_MIMETYPE, Charset.defaultCharset().name()));
entity.addPart(OpenStreetMapConstants.Api.Gpx.Parameters.DESCRIPTION, new StringBody(description, Charset.defaultCharset()));
entity.addPart(OpenStreetMapConstants.Api.Gpx.Parameters.TAGS, new StringBody(tags, Charset.defaultCharset()));
entity.addPart(OpenStreetMapConstants.Api.Gpx.Parameters.VISIBILITY, new StringBody(visibility.toString().toLowerCase(),Charset.defaultCharset()));
request.setEntity(entity);
// Display progress dialog
dialog = new ProgressDialog(activity);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setIndeterminate(false);
dialog.setMax((int) totalSize);
dialog.setTitle(
activity.getResources().getString(R.string.osm_upload_sending)
.replace("{0}", Long.toString(trackId)));
dialog.setCancelable(false);
dialog.show();
} catch (Exception e) {
Log.e(TAG, "onPreExecute() failed", e);
errorMsg = e.getLocalizedMessage();
cancel(true);
}
}
@Override
protected void onPostExecute(Void result) {
switch (resultCode) {
case -1:
dialog.dismiss();
// Internal error, the request didn't start at all
DialogUtils.showErrorDialog(activity,
activity.getResources().getString(R.string.osm_upload_error)
+ ": " + errorMsg);
break;
case HttpStatus.SC_OK:
dialog.dismiss();
// Success ! Update database and close activity
DataHelper.setTrackUploadDate(trackId, System.currentTimeMillis(), activity.getContentResolver());
new AlertDialog.Builder(activity)
.setTitle(android.R.string.dialog_alert_title)
.setIcon(android.R.drawable.ic_dialog_info)
.setMessage(R.string.osm_upload_sucess)
.setCancelable(true)
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
activity.finish();
}
}).create().show();
break;
case HttpStatus.SC_UNAUTHORIZED:
dialog.dismiss();
// Authorization issue. Provide a way to clear credentials
new AlertDialog.Builder(activity)
.setTitle(android.R.string.dialog_alert_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.osm_upload_unauthorized)
.setCancelable(true)
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Editor editor = PreferenceManager.getDefaultSharedPreferences(activity).edit();
editor.remove(OSMTracker.Preferences.KEY_OSM_OAUTH_TOKEN);
editor.remove(OSMTracker.Preferences.KEY_OSM_OAUTH_SECRET);
editor.commit();
dialog.dismiss();
}
}).create().show();
default:
// Another error. Display OSM response
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(response.getEntity().getContent())));
StringBuilder sb = new StringBuilder();
String line = null;
while ( (line = reader.readLine()) != null) {
sb.append(line).append(System.getProperty("line.separator"));
}
dialog.dismiss();
DialogUtils.showErrorDialog(activity,
activity.getResources().getString(R.string.osm_upload_bad_response)
.replace("{0}", Integer.toString(resultCode))
.replace("{1}", sb.toString()));
} catch (IOException ioe) {
DialogUtils.showErrorDialog(activity, activity.getResources().getString(R.string.osm_upload_error) + ": " + ioe);
} finally {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (reader != null) {
try { reader.close(); }
catch (IOException ioe) { }
}
}
}
}
@Override
protected Void doInBackground(Void... params) {
try {
// Post request and get response code
DefaultHttpClient httpClient = new DefaultHttpClient();
response = httpClient.execute(request);
resultCode = response.getStatusLine().getStatusCode();
} catch (Exception e) {
Log.e(TAG, "doInBackground failed", e);
errorMsg = e.getLocalizedMessage();
}
return null;
}
}