/*------------------------------------------------------------------------------ ** Ident: Sogeti Smart Mobile Solutions ** Author: rene ** Copyright: (c) Apr 24, 2011 Sogeti Nederland B.V. All Rights Reserved. **------------------------------------------------------------------------------ ** Sogeti Nederland B.V. | No part of this file may be reproduced ** Distributed Software Engineering | or transmitted in any form or by any ** Lange Dreef 17 | means, electronic or mechanical, for the ** 4131 NJ Vianen | purpose, without the express written ** The Netherlands | permission of the copyright holder. *------------------------------------------------------------------------------ * * This file is part of OpenGPSTracker. * * OpenGPSTracker is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenGPSTracker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenGPSTracker. If not, see <http://www.gnu.org/licenses/>. * */ package nl.sogeti.android.gpstracker.adapter.tasks; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.util.Log; import nl.sogeti.android.gpstracker.R; import nl.sogeti.android.gpstracker.actions.tasks.GpxCreator; import nl.sogeti.android.gpstracker.actions.tasks.XmlCreator; import nl.sogeti.android.gpstracker.actions.utils.ProgressListener; import nl.sogeti.android.gpstracker.adapter.BreadcrumbsAdapter; import nl.sogeti.android.gpstracker.adapter.BreadcrumbsTracks; import nl.sogeti.android.gpstracker.db.GPStracking.MetaData; import oauth.signpost.OAuthConsumer; import oauth.signpost.exception.OAuthCommunicationException; import oauth.signpost.exception.OAuthExpectationFailedException; import oauth.signpost.exception.OAuthMessageSignerException; import org.apache.ogt.http.Header; import org.apache.ogt.http.HttpEntity; import org.apache.ogt.http.HttpResponse; import org.apache.ogt.http.client.HttpClient; import org.apache.ogt.http.client.methods.HttpPost; import org.apache.ogt.http.entity.mime.HttpMultipartMode; import org.apache.ogt.http.entity.mime.MultipartEntity; import org.apache.ogt.http.entity.mime.content.FileBody; import org.apache.ogt.http.entity.mime.content.StringBody; import org.apache.ogt.http.util.EntityUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * An asynchronous task that communicates with Twitter to retrieve a request * token. (OAuthGetRequestToken) After receiving the request token from Twitter, * pop a browser to the user to authorize the Request Token. * (OAuthAuthorizeToken) */ public class UploadBreadcrumbsTrackTask extends GpxCreator { final String TAG = "OGT.UploadBreadcrumbsTrackTask"; private BreadcrumbsAdapter mAdapter; private OAuthConsumer mConsumer; private HttpClient mHttpClient; private String mActivityId; private String mBundleId; private String mDescription; private String mIsPublic; private String mBundleName; private String mBundleDescription; private boolean mIsBundleCreated; private List<File> mPhotoUploadQueue; /** * Constructor: create a new UploadBreadcrumbsTrackTask. * * @param context * @param adapter * @param listener * @param httpclient * @param consumer * @param trackUri * @param name */ public UploadBreadcrumbsTrackTask(Context context, BreadcrumbsAdapter adapter, ProgressListener listener, HttpClient httpclient, OAuthConsumer consumer, Uri trackUri, String name) { super(context, trackUri, name, true, listener); mAdapter = adapter; mHttpClient = httpclient; mConsumer = consumer; mPhotoUploadQueue = new LinkedList<File>(); } /** * Retrieve the OAuth Request Token and present a browser to the user to * authorize the token. */ @Override protected Uri doInBackground(Void... params) { // Leave room in the progressbar for uploading determineProgressGoal(); mXmlCreatorProgressAdmin.setUpload(true); // Build GPX file Uri gpxFile = exportGpx(); if (isCancelled()) { String text = mContext.getString(R.string.ticker_failed) + " \"http://api.gobreadcrumbs.com/v1/tracks\" " + mContext.getString(R.string.error_buildxml); handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), new IOException("Fail to execute request due to canceling"), text); } // Collect GPX Import option params mActivityId = null; mBundleId = null; mDescription = null; mIsPublic = null; Uri metadataUri = Uri.withAppendedPath(mTrackUri, "metadata"); Cursor cursor = null; try { cursor = mContext.getContentResolver().query(metadataUri, new String[] { MetaData.KEY, MetaData.VALUE }, null, null, null); if (cursor.moveToFirst()) { do { String key = cursor.getString(0); if (BreadcrumbsTracks.ACTIVITY_ID.equals(key)) { mActivityId = cursor.getString(1); } else if (BreadcrumbsTracks.BUNDLE_ID.equals(key)) { mBundleId = cursor.getString(1); } else if (BreadcrumbsTracks.DESCRIPTION.equals(key)) { mDescription = cursor.getString(1); } else if (BreadcrumbsTracks.ISPUBLIC.equals(key)) { mIsPublic = cursor.getString(1); } } while (cursor.moveToNext()); } } finally { if (cursor != null) { cursor.close(); } } if ("-1".equals(mActivityId)) { String text = "Unable to upload without a activity id stored in meta-data table"; IllegalStateException e = new IllegalStateException(text); handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), e, text); } int statusCode = 0; String responseText = null; Uri trackUri = null; HttpEntity responseEntity = null; try { if ("-1".equals(mBundleId)) { mBundleDescription = "";//mContext.getString(R.string.breadcrumbs_bundledescription); mBundleName = mContext.getString(R.string.app_name); mBundleId = createOpenGpsTrackerBundle(); } String gpxString = XmlCreator.convertStreamToString(mContext.getContentResolver().openInputStream(gpxFile)); HttpPost method = new HttpPost("http://api.gobreadcrumbs.com:80/v1/tracks"); if (isCancelled()) { throw new IOException("Fail to execute request due to canceling"); } // Build the multipart body with the upload data MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); entity.addPart("import_type", new StringBody("GPX")); //entity.addPart("gpx", new FileBody(gpxFile)); entity.addPart("gpx", new StringBody(gpxString)); entity.addPart("bundle_id", new StringBody(mBundleId)); entity.addPart("description", new StringBody(mDescription)); // entity.addPart("difficulty", new StringBody("3")); // entity.addPart("rating", new StringBody("4")); entity.addPart("public", new StringBody(mIsPublic)); method.setEntity(entity); // Execute the POST to OpenStreetMap mConsumer.sign(method); if( BreadcrumbsAdapter.DEBUG ) { Log.d( TAG, "HTTP Method "+method.getMethod() ); Log.d( TAG, "URI scheme "+method.getURI().getScheme() ); Log.d( TAG, "Host name "+method.getURI().getHost() ); Log.d( TAG, "Port "+method.getURI().getPort() ); Log.d( TAG, "Request path "+method.getURI().getPath()); Log.d( TAG, "Consumer Key: "+mConsumer.getConsumerKey()); Log.d( TAG, "Consumer Secret: "+mConsumer.getConsumerSecret()); Log.d( TAG, "Token: "+mConsumer.getToken()); Log.d( TAG, "Token Secret: "+mConsumer.getTokenSecret()); Log.d( TAG, "Execute request: "+method.getURI() ); for( Header header : method.getAllHeaders() ) { Log.d( TAG, " with header: "+header.toString()); } } HttpResponse response = mHttpClient.execute(method); mXmlCreatorProgressAdmin.addUploadProgress(); statusCode = response.getStatusLine().getStatusCode(); responseEntity = response.getEntity(); InputStream stream = responseEntity.getContent(); responseText = XmlCreator.convertStreamToString(stream); if( BreadcrumbsAdapter.DEBUG ) { Log.d( TAG, "Upload Response: "+responseText); } Pattern p = Pattern.compile(">([0-9]+)</id>"); Matcher m = p.matcher(responseText); if (m.find()) { Integer trackId = new Integer(m.group(1)); trackUri = Uri.parse("http://api.gobreadcrumbs.com/v1/tracks/" + trackId + "/placemarks.gpx"); for( File photo :mPhotoUploadQueue ) { uploadPhoto(photo, trackId); } } } catch (OAuthMessageSignerException e) { mAdapter.removeAuthentication(); handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), e, "Failed to sign the request with authentication signature"); } catch (OAuthExpectationFailedException e) { mAdapter.removeAuthentication(); handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), e, "The request did not authenticate"); } catch (OAuthCommunicationException e) { mAdapter.removeAuthentication(); handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), e, "The authentication communication failed"); } catch (IOException e) { handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), e, "A problem during communication"); } finally { if (responseEntity != null) { try { EntityUtils.consume(responseEntity); } catch (IOException e) { Log.e(TAG, "Failed to close the content stream", e); } } } if (statusCode == 200 || statusCode == 201) { if (trackUri == null) { handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), new IOException("Unable to retrieve URI from response"), responseText); } } else { //mAdapter.removeAuthentication(); handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), new IOException("Status code: " + statusCode), responseText); } return trackUri; } private String createOpenGpsTrackerBundle() throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException, IOException { HttpPost method = new HttpPost("http://api.gobreadcrumbs.com/v1/bundles.xml"); if (isCancelled()) { throw new IOException("Fail to execute request due to canceling"); } MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); entity.addPart("name", new StringBody(mBundleName)); entity.addPart("activity_id", new StringBody(mActivityId)); entity.addPart("description", new StringBody(mBundleDescription)); method.setEntity(entity); mConsumer.sign(method); HttpResponse response = mHttpClient.execute(method); HttpEntity responseEntity = response.getEntity(); InputStream stream = responseEntity.getContent(); String responseText = XmlCreator.convertStreamToString(stream); Pattern p = Pattern.compile(">([0-9]+)</id>"); Matcher m = p.matcher(responseText); String bundleId = null; if (m.find()) { bundleId = m.group(1); ContentValues values = new ContentValues(); values.put(MetaData.KEY, BreadcrumbsTracks.BUNDLE_ID); values.put(MetaData.VALUE, bundleId); Uri metadataUri = Uri.withAppendedPath(mTrackUri, "metadata"); mContext.getContentResolver().insert(metadataUri, values); mIsBundleCreated = true; } else { String text = "Unable to upload (yet) without a bunld id stored in meta-data table"; IllegalStateException e = new IllegalStateException(text); handleError(mContext.getString(R.string.taskerror_breadcrumbs_upload), e, text); } return bundleId; } /** * Queue's media * * @param inputFilePath * @return file path relative to the export dir * @throws IOException */ @Override protected String includeMediaFile(String inputFilePath) throws IOException { File source = new File(inputFilePath); if (source.exists()) { mXmlCreatorProgressAdmin.setPhotoUpload(source.length()); mPhotoUploadQueue.add(source); } return source.getName(); } private void uploadPhoto(File photo, Integer trackId) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException { HttpPost request = new HttpPost("http://api.gobreadcrumbs.com/v1/photos.xml"); if (isCancelled()) { throw new IOException("Fail to execute request due to canceling"); } MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); entity.addPart("name", new StringBody(photo.getName())); entity.addPart("track_id", new StringBody(Integer.toString(trackId))); //entity.addPart("description", new StringBody("")); entity.addPart("file", new FileBody(photo)); request.setEntity(entity); mConsumer.sign(request); if( BreadcrumbsAdapter.DEBUG ) { Log.d( TAG, "Execute request: "+request.getURI() ); for( Header header : request.getAllHeaders() ) { Log.d( TAG, " with header: "+header.toString()); } } HttpResponse response = mHttpClient.execute(request); HttpEntity responseEntity = response.getEntity(); InputStream stream = responseEntity.getContent(); String responseText = XmlCreator.convertStreamToString(stream); mXmlCreatorProgressAdmin.addPhotoUploadProgress(photo.length()); Log.i( TAG, "Uploaded photo "+responseText); } @Override protected void onPostExecute(Uri result) { BreadcrumbsTracks tracks = mAdapter.getBreadcrumbsTracks(); Uri metadataUri = Uri.withAppendedPath(mTrackUri, "metadata"); List<String> segments = result.getPathSegments(); Integer bcTrackId = new Integer(segments.get(segments.size() - 2)); ArrayList<ContentValues> metaValues = new ArrayList<ContentValues>(); metaValues.add(buildContentValues(BreadcrumbsTracks.TRACK_ID, Long.toString(bcTrackId))); if (mDescription != null) { metaValues.add(buildContentValues(BreadcrumbsTracks.DESCRIPTION, mDescription)); } if (mIsPublic != null) { metaValues.add(buildContentValues(BreadcrumbsTracks.ISPUBLIC, mIsPublic)); } metaValues.add(buildContentValues(BreadcrumbsTracks.BUNDLE_ID, mBundleId)); metaValues.add(buildContentValues(BreadcrumbsTracks.ACTIVITY_ID, mActivityId)); // Store in OGT provider ContentResolver resolver = mContext.getContentResolver(); resolver.bulkInsert(metadataUri, metaValues.toArray(new ContentValues[1])); // Store in Breadcrumbs adapter tracks.addSyncedTrack(new Long(mTrackUri.getLastPathSegment()), bcTrackId); if( mIsBundleCreated ) { mAdapter.getBreadcrumbsTracks().addBundle(Integer.parseInt(mBundleId), mBundleName, mBundleDescription); } //"http://api.gobreadcrumbs.com/v1/tracks/" + trackId + "/placemarks.gpx" mAdapter.getBreadcrumbsTracks().addTrack(bcTrackId, mName, new Integer(mBundleId), mDescription, null, null, null, mIsPublic, null, null, null, null, null); mAdapter.finishedTask(); super.onPostExecute(result); } private ContentValues buildContentValues(String key, String value) { ContentValues contentValues = new ContentValues(); contentValues.put(MetaData.KEY, key); contentValues.put(MetaData.VALUE, value); return contentValues; } }