Gingerbread Architecture

Last years Gingerbread Christmas Tree was OK, but not up to the standard of the previous efforts. This led me to have a bit of a look round and I spotted this book – The Gingerbread Architect

I started out just getting my eye in with a little Gingerbread Cottage this weekend.

Gingerbread Cottage

I’ll stick an update online once this years proper edifice is complete.

Tracks2Miles 1.1.10

Just a quick update while I’m still working on a 2.x release*.

This release has 2 updates:

  1. A small update to the import code to make use of the patch I managed to get included in the latest (1.1.11) release of My Tracks
  2. A new setting in the preferences to block uploading GPX data with a workout imported from My Tracks. This was a feature requested to help people with privacy concerns

You can download Tracks2Miles here: https://market.android.com/details?id=uk.me.hardill.dailymile

* The plan is for 2.x to have a updated UI making use of fragments for tablet and ICS devices.

New My Tracks Patch

After my last My Tracks patch had to be pulled (to be honest it did break sharing routes by anything other than Tracks2Miles) a new (safe) patch was accepted a few days ago.

This patch passes just the My Tracks track id along with the URL for the exported file in the SHARE intent. The key for the extra reuses the same key that is used by the My Tracks library when sending Broadcasts on starting and stopping recording a new track. The track id can be recovered from the intent with the following code:

long track_id = 
    intent.getLongExtra(getString(R.string.track_id_broadcast_extra), -1);

Once you have the track id then looking up all the details of that track using the My Tracks Library project is trivial (Once you got the user to allow 3rd party apps access).

MyTracksProviderUtils utils = 
    new MyTracksProviderUtilsImpl(this.getContentResolver());
Track track = utils.getTrack(track_id);
TripStatistics stats = track.getStatistics();
double totalDistance = stats.getTotalDistance();
long movingTime = stats.getMovingTime();
String cat = track.getCategory();
String desc = track.getDescription();
long lastLocId = utils.getLastLocationId(track_id);
Location loc = utils.getLocation(lastLocId);

The latest version of Tracks2TitanXT has already been updated to use this method and Tracks2Miles will get an update for this as well as some better tablet support before the next version of My Tracks ships.

Tracks2Miles 1.1.9 release

So the My Tracks guys released version 1.1.10 last week. This normally would be a good thing as they have been adding lots of nice new features and generally improving things.

Unfortunately in this release they also included a feature that stops 3rd party apps using their database without the user having to go into the their settings and allowing it.

I don’t agree with what the change is looking to achieve, My Tracks already has a set of Permissions that need to be requested by an application before it can access the data. If the user does not want to grant access then they should not install the 3rd party apps. I know nobody really reads closely the list of Permissions apps ask for at install time, but this is a problem for the Market application to solve by better high lighting the Permissions being asked for and explaining the consequences of accepting them.

On top of that there is no easy way to check if the “Allow access” option is set before you try and access the data. I will be raising a issue about this shortly as well as submitting another change to mean that I don’t have to try and guess the name of the track being exported.

It also would have been nice to get more than a couple of hours notice that the this new “feature” was about to ship. While I know it has been discussed on the site, I have a not been following the updates recently because the day job has been particularly busy. It would have at least been courteous to send the email that notified the owners of those with apps about the change a little sooner. So we had time to make the required changes rather than just get a whole slew of bug reports.

Anyway that’s the bitching out of the way. There is now a new version of Tracks2Miles that will show a dialog box if it can not access the data and return you to My Tracks to enable the access to the data.

You can grad the latest version from the Market here

Zephyr HxM BT

A new toy arrived in the post this morning. Eric Morse from TitanXT very kindly arranged this for me as a thank you for building the Tracks2TitanXT Android application.

image

I’ve been after one of these for a little while after getting a look at one when a couple of colleges bought them to help with their training.

As well as heart rate information the unit contains an accelerometer which is used to generate cadence information, this means that it can be used to approximate distances where there is no GPS reception. The data to is passed to a mobile phone via Bluetooth and there are a number of applications that understand the data including My Tacks. There is also an opensource project called zephyropen on goolge code that has documented the protocol and created a desktop app to display the data.

Zephyr also do a smarter version of the HxM called the BioHarness, this version gathers skin temperature and respiration rates as well as all the data from the HxM.

My Tracks only collects data from the sensor while it’s recording a route and it’s not easy to see the data once the workout has finished so I’m tempted to write a little app to grab the data at any time and possible a tool to view the data stored in the My Tracks database. I’ll also look at adding average heart rate info to the workout summary Tracks2Miles can upload to dailymile.

image

The soft belt and pickups is a lot comfier than some of the previous HRMs I’ve used before.

image

image

UPDATE:
From the logs it appears people are interested in where to buy these in the UK. Mine came from these guys, http://www.heartratemonitor-app.com/zephyr_hxm_bluetooth_heartrate_belt.html

Android Notification, Uploading Progressbar

While working on Tracks2Miles and Tracks2TitanXT I have been looking for a better way to show the status of the GPS track upload. In Tracks2Miles I have an entry in the Notification Area that changes as it moves through the steps of creating the route, uploading the track and finally adding a workout that uses the route. The first and the last steps are pretty quick HTTP post actions which return almost instantly, the uploading of the GPS track can take a long time for a big file on a slow network link and there was no way to know how much of the file had been uploaded. For Tracks2TitanXT I’m just using a AsyncTask for now which shows a dialog with a spinning progress bar.

I decided I wanted to add a progress bar to notification entry so went looking to see if anybody had outlined how to do it already. While I found this on United Coders which explained how to put a progress bar into the notification it did not show how to get the current bytes transferred data from the HTTPClient code included in Android.

I started to have a bit of a look at the HTTPClient API to see how I could get some status information out. A basic file upload looks a bit like this

final HttpResponse resp;
final HttpClient httpClient = new DefaultHttpClient();
final HttpPost post = new HttpPost(UPLOAD_URL);
ParcelFileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
InputStream in = context.getContentResolver().openInputStream(uri);
InputStreamEntity entity = new InputStreamEntity(in, fileDescriptor.getStatSize());
post.setEntity(entity);
resp = httpClient.execute(post);
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
...
}

Looking at the InputStreamEntity documentation showed a method called writeTo(OutputStream outputStream). Overriding this method should allow the wrapping of the OutputStream to count the amount of data written to it.

package uk.me.hardill.upload;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.http.entity.InputStreamEntity;

public class CountingInputStreamEntity extends InputStreamEntity {
	
	private UploadListener listener;
	private long length;

	public CountingInputStreamEntity(InputStream instream, long length) {
		super(instream, length);
		this.length = length;
	}
	
	public void setUploadListener(UploadListener listener) {
		this.listener = listener;
	}
	
	@Override
	public void writeTo(OutputStream outstream) throws IOException {
		super.writeTo(new CountingOutputStream(outstream));
	}
	
	class CountingOutputStream extends OutputStream {
		private long counter = 0l;
		private OutputStream outputStream;
		
		public CountingOutputStream(OutputStream outputStream) {
			this.outputStream = outputStream;
		}

		@Override
		public void write(int oneByte) throws IOException {
			this.outputStream.write(oneByte);
			counter++;
			if (listener != null) {
				int percent = (int) ((counter * 100)/ length);
				listener.onChange(percent);
			}
		}		
	}
	
	public interface UploadListener {
		public void onChange(int percent);
	}

}

Using a Listener model allows an external class to be notified when the % uploaded changes. With a minor update to the original code we get this

final HttpResponse resp;
final HttpClient httpClient = new DefaultHttpClient();
final HttpPost post = new HttpPost(UPLOAD_URL);
ParcelFileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
InputStream in = context.getContentResolver().openInputStream(uri);
CountingInputStreamEntity entity = new CountingInputStreamEntity(in, fileDescriptor.getStatSize());
entity.setListener(this);
post.setEntity(entity);
resp = httpClient.execute(post);
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
...
}

where the containing class has a onChange(int percent) method like this

@Override
public void onChange(int percent) {
	if(percent > lastPercent) {
		notification.contentView.setProgressBar(R.id.progressBar1, 100, percent, false);
		notificationManager.notify(1, notification);
		lastPercent = percent;
	}
}

Using a background thread started by a service to control the upload we end up with something the looks a but like this. A full example project is available here NotificationProgressTest.

Updated versions of both Tracks2Miles and Tracks2TitanXT should be available at the weekend with this new status notifications.

Tracks2TitanXT

About a month ago I was approached by Erik Morse from TitanXT to ask if I would be prepared to do something similar to Tracks2Miles for his site. At the time he did not have an API available, I said if he could run some thing up I would be prepared to have a look. Last week he sent me the details so I spent some of the long weekend running up a first pass of an app.

So Tracks2Miles now has a sister app supporting the TitanXT site called Tracks2TitanXT.

It works in much the same way as Tracks2Miles in that it hooks into My Tracks through the “Share with Friends…” option, except as well as supporting GPX files TitanXT supports the uploading of TCX files which also contain heart rate information if you are using one of the Bluetooth heart rate monitors that My Tracks supports.

The first pass is in the Android Market, it needs a few more bits of error handling and some cosmetic changes but I thought I’d let those that want to have play.

https://market.android.com/details?id=uk.me.hardill.titanxt

Tracks2Miles Facebook logon

Ever since Tracks2Miles shipped users have been having problems authorising the app if they choose to log in with their Facebook credentials.

The problem is that once the user had logged into Facebook it would redirect to a blank white page.

Last night I had another look at what was causing it.

To start with I added a Log statement to the shouldOverrideUrlLoading() method to print out the URL that was being loaded. This implied that the last URL loaded was from Facebook:

http://www.facebook.com/connect/uiserver.php?method=permissions.request&app_id=xxxxxxxxxx&display=touch&redirect_uri=https%3A%2F%2Fapi.dailymile.com%2Flogin%2Ffacebook%2Fcallback&state=next%3D%252Foauth%252Fauthorize%253Fresponse_type%253Dtoken%2526client_id%253Dxxxxxxxxxxxxxxxxxx%2526redirect_uri%253Ddm%253A%252F%252Flocalhost%252Ffoo&type=web_server&fbconnect=1&perms=email%2Cpublish_stream%2Coffline_access&from_login=1&refsrc=http%3A%2F%2Fm.facebook.com%2Fsetup_machine_mobile.php&refid=0&m_sess=MUm3C-eKFY7K8h&_rdr

I poked around at this for a while, but didn’t get very far, until Chris Banes pointed me at the source for the facebook-android-sdk, specifically the FbDialog.

When I added some trace to the onPageStarted() method I made some progress, the output from this implied that Facebook page had actually loaded successfully and redirected back to dailymile.

https://www.dailymile.com/login/facebook/callback?code=HphJjEM9nKsSez0ZW28W-FcvN80xR049KtQtk7CrykM.eyJpdiI6ImxGNVEyQ0FuSlF1X0xKdWJoU1MtZncifQ.yaVPEGNBOBPMQtZjrE431mw6a4-6yXUrvz0XrCd2ub44KKArQXpfiydYvhocYZuV0FW9mIO_3KLjBEO1XejL-EoplURcmbpoA3ueXjQJmvsu9Gf0Vaj7CLOdLOOpFw-d&state=next%3D%252Foauth%252Fauthorize%253Fresponse_type%253Dtoken%2526client_id%253Dxxxxxxxxxxxxxxxxxxxxxxxx%2526redirect_uri%253Ddm%253A%252F%252Flocalhost%252Ffoo

I had tried loading this new URL with Firefox on the desktop and immediately got a certificate mismatch error. The URL was hosted on www.dailymile.com but the cert was for api.dailymile.com

I was a little surprised that default WebView just swallowed this with no error messages, but a bit of googling and found the following blog post about how to work round the certificate miss match and even how to make it work with Android builds before 2.2

I also pointed the problem out to Ben from dailymile and he has fixed the cert mismatch with in a couple of hours.

There will be an updated version of Tracks2Miles in the next few days with these changes (just to be safe) and a couple of other updates around distance units.