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.