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.