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.

14 thoughts on “Android Notification, Uploading Progressbar”

  1. Hi,

    Thanks for your code. I’m using the same code as NotificationProgressTest t display a notification progressBar, but on my phone after a few notification, the phone simply reboots… It usualy happened between 20 and 25% of the task.
    Everything is just fine on the android Emulator, so I’m not having any clue on how to solve this problem…
    Any suggestion ?

    1. Hi,

      I’ve not heard of any problems like this.

      Have you tried attaching adb (adb -d logcat) to your phone via a USB cable and streaming the logs up to the point it reboots? It might catch the the cause of the problem.

  2. The problem is too frequent updates of the notification. You’re flooding the NotificationService, and the device reboots.

    1. @anonymous

      That shouldn’t be the case as the notification is only called if the % value actually changes (see the onChange method). But if it is then the test in the onChange method can be updated to throttle the rate of updates.

  3. actually I am trying to upload an image file and a user id to keep track that who uploaded that image file for that i need to send a USERID as well

  4. @hardllb just change the superclass of countingInputStream to multipart stream entity and this will allow to add extra fields like userid or username who uploaded the Media file.

  5. if it is ok with you I want to discuss some more issues like write now above written code will upload the media file by uploading each byte separately this seems a bit issue because it takes to long to upload a single file.

  6. @Gagandeep as mentioned in one of the other comments, the performance hit is because it’s updating the scrollbar every byte, even if the % has not changed.

    It should be a trivial change to make it only call listener onChange() when the % has changed which should be a lot quicker.

    1. Madan, The code looks like a generic OutputStream, so you should be able to write any amount of data to server via this channel.

Leave a Reply

Your email address will not be published. Required fields are marked *

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