My Tracks to dailymile GPX route exporter – part 3

So I’ve got the oAuth 2.0 all sorted it’s time to write the code to do the actual publish to dailymile.

First up I need a UI to capture all the other info about the workout other than the route. Not all of the options on the website are available via the API, but there are enough make a useful post.

I wanted to fill in as many of the fields as possible, so the route name comes from the GPX file name. When My Tracks creates the intent to share the GPX file it adds 2 extras which map to the equivalent of an email subject and email some body text to go with the file, but none of this contains data like the distance covered or the time. This information isn’t explicitly in the default GPX files either.

I could parse the GPX file and generate this info, but it’s going to be a heavy weight process and the data is something My Tracks should already have. I had a quick look at some of the other formats that My Tracks can export and found a the track description field in the KML version which holds the info I’m looking for.


<desc><![CDATA[Created by <a href='http://mytracks.appspot.com'>My Tracks</a< on Android.<p>Total Distance: 223.32 km (138.8 mi)<br>Total Time: 1:57<br>Moving Time: 0:20<br>Average Speed: 6866.26 km/h (4266.5 mi/h)<br> Average Moving Speed: 40197.14 km/h (24977.3 mi/h)<br> Max Speed: 0.00 km/h (0.0 mi/h)<br>Min Elevation: 0 m (0 ft)<br>Max Elevation: 0 m (0 ft)<br>Elevation Gain: 0 m (0 ft)<br>Max Grade: 0 %<br>Min Grade: 0 %<br>Recorded: Tue Apr 12 18:53:08 GMT+00:00 2011<br>Activity type: -<br><img border="0" src="http://chart.apis.google.com/chart?&chs=600x350&cht=lxy&chtt=Elevation&chxt=x,y&chxr=0,0,223,37|1,0.0,100.0,25&chco=009A00&chm=B,00AA00,0,0,0&chg=100000,25.0,1,0&chd=e:,"/>]]></desc>

There is a similar field in the GPX file that is currently empty. A quick look at the code showed that a 5 line patch should populate that field with the same data. I’ve sent the patch off to the My Tracks guys, but in the mean time if the field is empty the user will have to fill in the time and distance by hand.

With the UI sorted, the publishing is done in a service as it’s a multi-stage process including uploading the GPX file which could take a while over mobile networks. It’s basically 3 dailymile API calls

  1. Create a new route – POST https://api.dailymile.com/routes.json
  2. Upload GPS data to form the route – PUT https://api.dailymile.com/entries/id/track.json
  3. Create new workout entry – POST https://api.dailymile.com/entries.json

Interspersing these with Notification updates so the user can see how far through the process it is.

 

private void upload() {
HttpResponse resp;
final HttpClient httpClient = new DefaultHttpClient();</code>

String ns = Context.NOTIFICATION_SERVICE;
NotificationManager notificationManager = (NotificationManager) getSystemService(ns);

Notification notification = new Notification(R.drawable.icon, "Uploading Workout", System.currentTimeMillis());
Context context = getApplicationContext();

Intent notificationIntent = new Intent();
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(context, "Uploading Workout", "Creating new Route", contentIntent);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.flags |= Notification.FLAG_NO_CLEAR;
notificationManager.notify(1, notification);
Log.i("DailyMile",Constants.NEW_ROUTE_URL + authToken);
final HttpPost post = new HttpPost(Constants.NEW_ROUTE_URL + authToken);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("name", routeName));
nameValuePairs.add(new BasicNameValuePair("activity_type", routeType));
try {
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String routeID = "";
try {
resp = httpClient.execute(post);
if (resp.getStatusLine().getStatusCode() == 201) {
InputStream in = resp.getEntity().getContent();
StringBuffer buffer = new StringBuffer();
for (int i = in.read(); i != -1; i = in.read()) {
buffer.append((char)i);
}
JSONObject jsObject = new JSONObject(new JSONTokener(buffer.toString()));
routeID = jsObject.getString("id");
Log.i("DailyMile", routeID);
} else {
Log.i("DailyMile","" + resp.getStatusLine().getStatusCode());
notification.setLatestEventInfo(context, "Uploading Workout", "Failed to create a new route", contentIntent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1, notification);
//barf
return;
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
notification.setLatestEventInfo(context, "Uploading Workout", "Uploading Route", contentIntent);
notificationManager.notify(1, notification);
//{"id":622197,"activity_type":"Fitness","name":"test"}

String putURL = Constants.UPDATE_ROUTE_URL.replace("{id}", routeID) + authToken;
Log.i("DailyMile", putURL);
final HttpPut put = new HttpPut(putURL);
try {
ParcelFileDescriptor pfd = this.getContentResolver().openFileDescriptor(gpxURI, "r");
InputStream in = this.getContentResolver().openInputStream(gpxURI);
InputStreamEntity entity = new InputStreamEntity(in, pfd.getStatSize());
entity.setContentType("application/gpx+xml");
put.setEntity(entity);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

try {
resp = httpClient.execute(put);
if (resp.getStatusLine().getStatusCode() != 201) {
Log.i("DailyMile","" + resp.getStatusLine().getStatusCode());
notification.setLatestEventInfo(context, "Uploading Workout", "Failed to upload new route", contentIntent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1, notification);
//barf
return;
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

notification.setLatestEventInfo(context, "Uploading Workout", "Adding new workout", contentIntent);
notificationManager.notify(1, notification);

final HttpPost postWorkOut = new HttpPost(Constants.NEW_ENTRY_URL+authToken);
StringEntity entity = null;
JSONObject json = new JSONObject();
JSONObject workout = new JSONObject();
JSONObject distance = new JSONObject();
try {
json.put("message",workoutMessage);
workout.put("duration",workoutDuration);
workout.put("activity_type", routeType);
workout.put("felt", workoutRating.toLowerCase());
workout.put("route_id",Integer.parseInt(routeID));
distance.put("units", workoutDistanceUnits);
distance.put("value",workoutDistance);
workout.put("distance", distance);
json.put("workout", workout);
Log.i("DailyMile",json.toString());
entity = new StringEntity(json.toString());
entity.setContentType("application/json");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
postWorkOut.setEntity(entity);

try {
resp = httpClient.execute(postWorkOut);
if (resp.getStatusLine().getStatusCode() != 201) {
Log.i("DailyMile","" + resp.getStatusLine().getStatusCode());
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(context, "Uploading Workout", "Failed to upload new workout", contentIntent);
notificationManager.notify(1, notification);
//barf
return;
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

notification.setLatestEventInfo(context, "Uploading Workout", "All Done", contentIntent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1, notification);

}

 

So it’s all done now, just got to jump trough a few hopes to get it sign off to publish it in the Android Market and hopefully the My Tracks guys will include my patch in the next drop.

2 thoughts on “My Tracks to dailymile GPX route exporter – part 3”

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.